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  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir"
4724     };
4725
4726     if(config && config.data){
4727         this.inlineData = config.data;
4728         delete config.data;
4729     }
4730
4731     Roo.apply(this, config);
4732     
4733     if(this.reader){ // reader passed
4734         this.reader = Roo.factory(this.reader, Roo.data);
4735         this.reader.xmodule = this.xmodule || false;
4736         if(!this.recordType){
4737             this.recordType = this.reader.recordType;
4738         }
4739         if(this.reader.onMetaChange){
4740             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4741         }
4742     }
4743
4744     if(this.recordType){
4745         this.fields = this.recordType.prototype.fields;
4746     }
4747     this.modified = [];
4748
4749     this.addEvents({
4750         /**
4751          * @event datachanged
4752          * Fires when the data cache has changed, and a widget which is using this Store
4753          * as a Record cache should refresh its view.
4754          * @param {Store} this
4755          */
4756         datachanged : true,
4757         /**
4758          * @event metachange
4759          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4760          * @param {Store} this
4761          * @param {Object} meta The JSON metadata
4762          */
4763         metachange : true,
4764         /**
4765          * @event add
4766          * Fires when Records have been added to the Store
4767          * @param {Store} this
4768          * @param {Roo.data.Record[]} records The array of Records added
4769          * @param {Number} index The index at which the record(s) were added
4770          */
4771         add : true,
4772         /**
4773          * @event remove
4774          * Fires when a Record has been removed from the Store
4775          * @param {Store} this
4776          * @param {Roo.data.Record} record The Record that was removed
4777          * @param {Number} index The index at which the record was removed
4778          */
4779         remove : true,
4780         /**
4781          * @event update
4782          * Fires when a Record has been updated
4783          * @param {Store} this
4784          * @param {Roo.data.Record} record The Record that was updated
4785          * @param {String} operation The update operation being performed.  Value may be one of:
4786          * <pre><code>
4787  Roo.data.Record.EDIT
4788  Roo.data.Record.REJECT
4789  Roo.data.Record.COMMIT
4790          * </code></pre>
4791          */
4792         update : true,
4793         /**
4794          * @event clear
4795          * Fires when the data cache has been cleared.
4796          * @param {Store} this
4797          */
4798         clear : true,
4799         /**
4800          * @event beforeload
4801          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4802          * the load action will be canceled.
4803          * @param {Store} this
4804          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4805          */
4806         beforeload : true,
4807         /**
4808          * @event load
4809          * Fires after a new set of Records has been loaded.
4810          * @param {Store} this
4811          * @param {Roo.data.Record[]} records The Records that were loaded
4812          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4813          */
4814         load : true,
4815         /**
4816          * @event loadexception
4817          * Fires if an exception occurs in the Proxy during loading.
4818          * Called with the signature of the Proxy's "loadexception" event.
4819          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4820          * 
4821          * @param {Proxy} 
4822          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4823          * @param {Object} load options 
4824          * @param {Object} jsonData from your request (normally this contains the Exception)
4825          */
4826         loadexception : true
4827     });
4828     
4829     if(this.proxy){
4830         this.proxy = Roo.factory(this.proxy, Roo.data);
4831         this.proxy.xmodule = this.xmodule || false;
4832         this.relayEvents(this.proxy,  ["loadexception"]);
4833     }
4834     this.sortToggle = {};
4835
4836     Roo.data.Store.superclass.constructor.call(this);
4837
4838     if(this.inlineData){
4839         this.loadData(this.inlineData);
4840         delete this.inlineData;
4841     }
4842 };
4843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4844      /**
4845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4846     * without a remote query - used by combo/forms at present.
4847     */
4848     
4849     /**
4850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4851     */
4852     /**
4853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4854     */
4855     /**
4856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4858     */
4859     /**
4860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4861     * on any HTTP request
4862     */
4863     /**
4864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4865     */
4866     /**
4867     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4868     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4869     */
4870     remoteSort : false,
4871
4872     /**
4873     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4874      * loaded or when a record is removed. (defaults to false).
4875     */
4876     pruneModifiedRecords : false,
4877
4878     // private
4879     lastOptions : null,
4880
4881     /**
4882      * Add Records to the Store and fires the add event.
4883      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4884      */
4885     add : function(records){
4886         records = [].concat(records);
4887         for(var i = 0, len = records.length; i < len; i++){
4888             records[i].join(this);
4889         }
4890         var index = this.data.length;
4891         this.data.addAll(records);
4892         this.fireEvent("add", this, records, index);
4893     },
4894
4895     /**
4896      * Remove a Record from the Store and fires the remove event.
4897      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4898      */
4899     remove : function(record){
4900         var index = this.data.indexOf(record);
4901         this.data.removeAt(index);
4902         if(this.pruneModifiedRecords){
4903             this.modified.remove(record);
4904         }
4905         this.fireEvent("remove", this, record, index);
4906     },
4907
4908     /**
4909      * Remove all Records from the Store and fires the clear event.
4910      */
4911     removeAll : function(){
4912         this.data.clear();
4913         if(this.pruneModifiedRecords){
4914             this.modified = [];
4915         }
4916         this.fireEvent("clear", this);
4917     },
4918
4919     /**
4920      * Inserts Records to the Store at the given index and fires the add event.
4921      * @param {Number} index The start index at which to insert the passed Records.
4922      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4923      */
4924     insert : function(index, records){
4925         records = [].concat(records);
4926         for(var i = 0, len = records.length; i < len; i++){
4927             this.data.insert(index, records[i]);
4928             records[i].join(this);
4929         }
4930         this.fireEvent("add", this, records, index);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the passed Record.
4935      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4936      * @return {Number} The index of the passed Record. Returns -1 if not found.
4937      */
4938     indexOf : function(record){
4939         return this.data.indexOf(record);
4940     },
4941
4942     /**
4943      * Get the index within the cache of the Record with the passed id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Number} The index of the Record. Returns -1 if not found.
4946      */
4947     indexOfId : function(id){
4948         return this.data.indexOfKey(id);
4949     },
4950
4951     /**
4952      * Get the Record with the specified id.
4953      * @param {String} id The id of the Record to find.
4954      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4955      */
4956     getById : function(id){
4957         return this.data.key(id);
4958     },
4959
4960     /**
4961      * Get the Record at the specified index.
4962      * @param {Number} index The index of the Record to find.
4963      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4964      */
4965     getAt : function(index){
4966         return this.data.itemAt(index);
4967     },
4968
4969     /**
4970      * Returns a range of Records between specified indices.
4971      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4972      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4973      * @return {Roo.data.Record[]} An array of Records
4974      */
4975     getRange : function(start, end){
4976         return this.data.getRange(start, end);
4977     },
4978
4979     // private
4980     storeOptions : function(o){
4981         o = Roo.apply({}, o);
4982         delete o.callback;
4983         delete o.scope;
4984         this.lastOptions = o;
4985     },
4986
4987     /**
4988      * Loads the Record cache from the configured Proxy using the configured Reader.
4989      * <p>
4990      * If using remote paging, then the first load call must specify the <em>start</em>
4991      * and <em>limit</em> properties in the options.params property to establish the initial
4992      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4993      * <p>
4994      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4995      * and this call will return before the new data has been loaded. Perform any post-processing
4996      * in a callback function, or in a "load" event handler.</strong>
4997      * <p>
4998      * @param {Object} options An object containing properties which control loading options:<ul>
4999      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5000      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5001      * passed the following arguments:<ul>
5002      * <li>r : Roo.data.Record[]</li>
5003      * <li>options: Options object from the load call</li>
5004      * <li>success: Boolean success indicator</li></ul></li>
5005      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5006      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5007      * </ul>
5008      */
5009     load : function(options){
5010         options = options || {};
5011         if(this.fireEvent("beforeload", this, options) !== false){
5012             this.storeOptions(options);
5013             var p = Roo.apply(options.params || {}, this.baseParams);
5014             // if meta was not loaded from remote source.. try requesting it.
5015             if (!this.reader.metaFromRemote) {
5016                 p._requestMeta = 1;
5017             }
5018             if(this.sortInfo && this.remoteSort){
5019                 var pn = this.paramNames;
5020                 p[pn["sort"]] = this.sortInfo.field;
5021                 p[pn["dir"]] = this.sortInfo.direction;
5022             }
5023             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5024         }
5025     },
5026
5027     /**
5028      * Reloads the Record cache from the configured Proxy using the configured Reader and
5029      * the options from the last load operation performed.
5030      * @param {Object} options (optional) An object containing properties which may override the options
5031      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5032      * the most recently used options are reused).
5033      */
5034     reload : function(options){
5035         this.load(Roo.applyIf(options||{}, this.lastOptions));
5036     },
5037
5038     // private
5039     // Called as a callback by the Reader during a load operation.
5040     loadRecords : function(o, options, success){
5041         if(!o || success === false){
5042             if(success !== false){
5043                 this.fireEvent("load", this, [], options);
5044             }
5045             if(options.callback){
5046                 options.callback.call(options.scope || this, [], options, false);
5047             }
5048             return;
5049         }
5050         // if data returned failure - throw an exception.
5051         if (o.success === false) {
5052             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5053             return;
5054         }
5055         var r = o.records, t = o.totalRecords || r.length;
5056         if(!options || options.add !== true){
5057             if(this.pruneModifiedRecords){
5058                 this.modified = [];
5059             }
5060             for(var i = 0, len = r.length; i < len; i++){
5061                 r[i].join(this);
5062             }
5063             if(this.snapshot){
5064                 this.data = this.snapshot;
5065                 delete this.snapshot;
5066             }
5067             this.data.clear();
5068             this.data.addAll(r);
5069             this.totalLength = t;
5070             this.applySort();
5071             this.fireEvent("datachanged", this);
5072         }else{
5073             this.totalLength = Math.max(t, this.data.length+r.length);
5074             this.add(r);
5075         }
5076         this.fireEvent("load", this, r, options);
5077         if(options.callback){
5078             options.callback.call(options.scope || this, r, options, true);
5079         }
5080     },
5081
5082     /**
5083      * Loads data from a passed data block. A Reader which understands the format of the data
5084      * must have been configured in the constructor.
5085      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5086      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5087      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5088      */
5089     loadData : function(o, append){
5090         var r = this.reader.readRecords(o);
5091         this.loadRecords(r, {add: append}, true);
5092     },
5093
5094     /**
5095      * Gets the number of cached records.
5096      * <p>
5097      * <em>If using paging, this may not be the total size of the dataset. If the data object
5098      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5099      * the data set size</em>
5100      */
5101     getCount : function(){
5102         return this.data.length || 0;
5103     },
5104
5105     /**
5106      * Gets the total number of records in the dataset as returned by the server.
5107      * <p>
5108      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5109      * the dataset size</em>
5110      */
5111     getTotalCount : function(){
5112         return this.totalLength || 0;
5113     },
5114
5115     /**
5116      * Returns the sort state of the Store as an object with two properties:
5117      * <pre><code>
5118  field {String} The name of the field by which the Records are sorted
5119  direction {String} The sort order, "ASC" or "DESC"
5120      * </code></pre>
5121      */
5122     getSortState : function(){
5123         return this.sortInfo;
5124     },
5125
5126     // private
5127     applySort : function(){
5128         if(this.sortInfo && !this.remoteSort){
5129             var s = this.sortInfo, f = s.field;
5130             var st = this.fields.get(f).sortType;
5131             var fn = function(r1, r2){
5132                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5133                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5134             };
5135             this.data.sort(s.direction, fn);
5136             if(this.snapshot && this.snapshot != this.data){
5137                 this.snapshot.sort(s.direction, fn);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * Sets the default sort column and order to be used by the next load operation.
5144      * @param {String} fieldName The name of the field to sort by.
5145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5146      */
5147     setDefaultSort : function(field, dir){
5148         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5149     },
5150
5151     /**
5152      * Sort the Records.
5153      * If remote sorting is used, the sort is performed on the server, and the cache is
5154      * reloaded. If local sorting is used, the cache is sorted internally.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     sort : function(fieldName, dir){
5159         var f = this.fields.get(fieldName);
5160         if(!dir){
5161             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5162                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5163             }else{
5164                 dir = f.sortDir;
5165             }
5166         }
5167         this.sortToggle[f.name] = dir;
5168         this.sortInfo = {field: f.name, direction: dir};
5169         if(!this.remoteSort){
5170             this.applySort();
5171             this.fireEvent("datachanged", this);
5172         }else{
5173             this.load(this.lastOptions);
5174         }
5175     },
5176
5177     /**
5178      * Calls the specified function for each of the Records in the cache.
5179      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5180      * Returning <em>false</em> aborts and exits the iteration.
5181      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5182      */
5183     each : function(fn, scope){
5184         this.data.each(fn, scope);
5185     },
5186
5187     /**
5188      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5189      * (e.g., during paging).
5190      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5191      */
5192     getModifiedRecords : function(){
5193         return this.modified;
5194     },
5195
5196     // private
5197     createFilterFn : function(property, value, anyMatch){
5198         if(!value.exec){ // not a regex
5199             value = String(value);
5200             if(value.length == 0){
5201                 return false;
5202             }
5203             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5204         }
5205         return function(r){
5206             return value.test(r.data[property]);
5207         };
5208     },
5209
5210     /**
5211      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5212      * @param {String} property A field on your records
5213      * @param {Number} start The record index to start at (defaults to 0)
5214      * @param {Number} end The last record index to include (defaults to length - 1)
5215      * @return {Number} The sum
5216      */
5217     sum : function(property, start, end){
5218         var rs = this.data.items, v = 0;
5219         start = start || 0;
5220         end = (end || end === 0) ? end : rs.length-1;
5221
5222         for(var i = start; i <= end; i++){
5223             v += (rs[i].data[property] || 0);
5224         }
5225         return v;
5226     },
5227
5228     /**
5229      * Filter the records by a specified property.
5230      * @param {String} field A field on your records
5231      * @param {String/RegExp} value Either a string that the field
5232      * should start with or a RegExp to test against the field
5233      * @param {Boolean} anyMatch True to match any part not just the beginning
5234      */
5235     filter : function(property, value, anyMatch){
5236         var fn = this.createFilterFn(property, value, anyMatch);
5237         return fn ? this.filterBy(fn) : this.clearFilter();
5238     },
5239
5240     /**
5241      * Filter by a function. The specified function will be called with each
5242      * record in this data source. If the function returns true the record is included,
5243      * otherwise it is filtered.
5244      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5245      * @param {Object} scope (optional) The scope of the function (defaults to this)
5246      */
5247     filterBy : function(fn, scope){
5248         this.snapshot = this.snapshot || this.data;
5249         this.data = this.queryBy(fn, scope||this);
5250         this.fireEvent("datachanged", this);
5251     },
5252
5253     /**
5254      * Query the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5260      */
5261     query : function(property, value, anyMatch){
5262         var fn = this.createFilterFn(property, value, anyMatch);
5263         return fn ? this.queryBy(fn) : this.data.clone();
5264     },
5265
5266     /**
5267      * Query by a function. The specified function will be called with each
5268      * record in this data source. If the function returns true the record is included
5269      * in the results.
5270      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5271      * @param {Object} scope (optional) The scope of the function (defaults to this)
5272       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      **/
5274     queryBy : function(fn, scope){
5275         var data = this.snapshot || this.data;
5276         return data.filterBy(fn, scope||this);
5277     },
5278
5279     /**
5280      * Collects unique values for a particular dataIndex from this store.
5281      * @param {String} dataIndex The property to collect
5282      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5283      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5284      * @return {Array} An array of the unique values
5285      **/
5286     collect : function(dataIndex, allowNull, bypassFilter){
5287         var d = (bypassFilter === true && this.snapshot) ?
5288                 this.snapshot.items : this.data.items;
5289         var v, sv, r = [], l = {};
5290         for(var i = 0, len = d.length; i < len; i++){
5291             v = d[i].data[dataIndex];
5292             sv = String(v);
5293             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5294                 l[sv] = true;
5295                 r[r.length] = v;
5296             }
5297         }
5298         return r;
5299     },
5300
5301     /**
5302      * Revert to a view of the Record cache with no filtering applied.
5303      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5304      */
5305     clearFilter : function(suppressEvent){
5306         if(this.snapshot && this.snapshot != this.data){
5307             this.data = this.snapshot;
5308             delete this.snapshot;
5309             if(suppressEvent !== true){
5310                 this.fireEvent("datachanged", this);
5311             }
5312         }
5313     },
5314
5315     // private
5316     afterEdit : function(record){
5317         if(this.modified.indexOf(record) == -1){
5318             this.modified.push(record);
5319         }
5320         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5321     },
5322
5323     // private
5324     afterReject : function(record){
5325         this.modified.remove(record);
5326         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5327     },
5328
5329     // private
5330     afterCommit : function(record){
5331         this.modified.remove(record);
5332         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5333     },
5334
5335     /**
5336      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5337      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5338      */
5339     commitChanges : function(){
5340         var m = this.modified.slice(0);
5341         this.modified = [];
5342         for(var i = 0, len = m.length; i < len; i++){
5343             m[i].commit();
5344         }
5345     },
5346
5347     /**
5348      * Cancel outstanding changes on all changed records.
5349      */
5350     rejectChanges : function(){
5351         var m = this.modified.slice(0);
5352         this.modified = [];
5353         for(var i = 0, len = m.length; i < len; i++){
5354             m[i].reject();
5355         }
5356     },
5357
5358     onMetaChange : function(meta, rtype, o){
5359         this.recordType = rtype;
5360         this.fields = rtype.prototype.fields;
5361         delete this.snapshot;
5362         this.sortInfo = meta.sortInfo || this.sortInfo;
5363         this.modified = [];
5364         this.fireEvent('metachange', this, this.reader.meta);
5365     }
5366 });/*
5367  * Based on:
5368  * Ext JS Library 1.1.1
5369  * Copyright(c) 2006-2007, Ext JS, LLC.
5370  *
5371  * Originally Released Under LGPL - original licence link has changed is not relivant.
5372  *
5373  * Fork - LGPL
5374  * <script type="text/javascript">
5375  */
5376
5377 /**
5378  * @class Roo.data.SimpleStore
5379  * @extends Roo.data.Store
5380  * Small helper class to make creating Stores from Array data easier.
5381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5382  * @cfg {Array} fields An array of field definition objects, or field name strings.
5383  * @cfg {Array} data The multi-dimensional array of data
5384  * @constructor
5385  * @param {Object} config
5386  */
5387 Roo.data.SimpleStore = function(config){
5388     Roo.data.SimpleStore.superclass.constructor.call(this, {
5389         isLocal : true,
5390         reader: new Roo.data.ArrayReader({
5391                 id: config.id
5392             },
5393             Roo.data.Record.create(config.fields)
5394         ),
5395         proxy : new Roo.data.MemoryProxy(config.data)
5396     });
5397     this.load();
5398 };
5399 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409
5410 /**
5411 /**
5412  * @extends Roo.data.Store
5413  * @class Roo.data.JsonStore
5414  * Small helper class to make creating Stores for JSON data easier. <br/>
5415 <pre><code>
5416 var store = new Roo.data.JsonStore({
5417     url: 'get-images.php',
5418     root: 'images',
5419     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5420 });
5421 </code></pre>
5422  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5423  * JsonReader and HttpProxy (unless inline data is provided).</b>
5424  * @cfg {Array} fields An array of field definition objects, or field name strings.
5425  * @constructor
5426  * @param {Object} config
5427  */
5428 Roo.data.JsonStore = function(c){
5429     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5430         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5431         reader: new Roo.data.JsonReader(c, c.fields)
5432     }));
5433 };
5434 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5435  * Based on:
5436  * Ext JS Library 1.1.1
5437  * Copyright(c) 2006-2007, Ext JS, LLC.
5438  *
5439  * Originally Released Under LGPL - original licence link has changed is not relivant.
5440  *
5441  * Fork - LGPL
5442  * <script type="text/javascript">
5443  */
5444
5445  
5446 Roo.data.Field = function(config){
5447     if(typeof config == "string"){
5448         config = {name: config};
5449     }
5450     Roo.apply(this, config);
5451     
5452     if(!this.type){
5453         this.type = "auto";
5454     }
5455     
5456     var st = Roo.data.SortTypes;
5457     // named sortTypes are supported, here we look them up
5458     if(typeof this.sortType == "string"){
5459         this.sortType = st[this.sortType];
5460     }
5461     
5462     // set default sortType for strings and dates
5463     if(!this.sortType){
5464         switch(this.type){
5465             case "string":
5466                 this.sortType = st.asUCString;
5467                 break;
5468             case "date":
5469                 this.sortType = st.asDate;
5470                 break;
5471             default:
5472                 this.sortType = st.none;
5473         }
5474     }
5475
5476     // define once
5477     var stripRe = /[\$,%]/g;
5478
5479     // prebuilt conversion function for this field, instead of
5480     // switching every time we're reading a value
5481     if(!this.convert){
5482         var cv, dateFormat = this.dateFormat;
5483         switch(this.type){
5484             case "":
5485             case "auto":
5486             case undefined:
5487                 cv = function(v){ return v; };
5488                 break;
5489             case "string":
5490                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5491                 break;
5492             case "int":
5493                 cv = function(v){
5494                     return v !== undefined && v !== null && v !== '' ?
5495                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5496                     };
5497                 break;
5498             case "float":
5499                 cv = function(v){
5500                     return v !== undefined && v !== null && v !== '' ?
5501                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5502                     };
5503                 break;
5504             case "bool":
5505             case "boolean":
5506                 cv = function(v){ return v === true || v === "true" || v == 1; };
5507                 break;
5508             case "date":
5509                 cv = function(v){
5510                     if(!v){
5511                         return '';
5512                     }
5513                     if(v instanceof Date){
5514                         return v;
5515                     }
5516                     if(dateFormat){
5517                         if(dateFormat == "timestamp"){
5518                             return new Date(v*1000);
5519                         }
5520                         return Date.parseDate(v, dateFormat);
5521                     }
5522                     var parsed = Date.parse(v);
5523                     return parsed ? new Date(parsed) : null;
5524                 };
5525              break;
5526             
5527         }
5528         this.convert = cv;
5529     }
5530 };
5531
5532 Roo.data.Field.prototype = {
5533     dateFormat: null,
5534     defaultValue: "",
5535     mapping: null,
5536     sortType : null,
5537     sortDir : "ASC"
5538 };/*
5539  * Based on:
5540  * Ext JS Library 1.1.1
5541  * Copyright(c) 2006-2007, Ext JS, LLC.
5542  *
5543  * Originally Released Under LGPL - original licence link has changed is not relivant.
5544  *
5545  * Fork - LGPL
5546  * <script type="text/javascript">
5547  */
5548  
5549 // Base class for reading structured data from a data source.  This class is intended to be
5550 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5551
5552 /**
5553  * @class Roo.data.DataReader
5554  * Base class for reading structured data from a data source.  This class is intended to be
5555  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5556  */
5557
5558 Roo.data.DataReader = function(meta, recordType){
5559     
5560     this.meta = meta;
5561     
5562     this.recordType = recordType instanceof Array ? 
5563         Roo.data.Record.create(recordType) : recordType;
5564 };
5565
5566 Roo.data.DataReader.prototype = {
5567      /**
5568      * Create an empty record
5569      * @param {Object} data (optional) - overlay some values
5570      * @return {Roo.data.Record} record created.
5571      */
5572     newRow :  function(d) {
5573         var da =  {};
5574         this.recordType.prototype.fields.each(function(c) {
5575             switch( c.type) {
5576                 case 'int' : da[c.name] = 0; break;
5577                 case 'date' : da[c.name] = new Date(); break;
5578                 case 'float' : da[c.name] = 0.0; break;
5579                 case 'boolean' : da[c.name] = false; break;
5580                 default : da[c.name] = ""; break;
5581             }
5582             
5583         });
5584         return new this.recordType(Roo.apply(da, d));
5585     }
5586     
5587 };/*
5588  * Based on:
5589  * Ext JS Library 1.1.1
5590  * Copyright(c) 2006-2007, Ext JS, LLC.
5591  *
5592  * Originally Released Under LGPL - original licence link has changed is not relivant.
5593  *
5594  * Fork - LGPL
5595  * <script type="text/javascript">
5596  */
5597
5598 /**
5599  * @class Roo.data.DataProxy
5600  * @extends Roo.data.Observable
5601  * This class is an abstract base class for implementations which provide retrieval of
5602  * unformatted data objects.<br>
5603  * <p>
5604  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5605  * (of the appropriate type which knows how to parse the data object) to provide a block of
5606  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5607  * <p>
5608  * Custom implementations must implement the load method as described in
5609  * {@link Roo.data.HttpProxy#load}.
5610  */
5611 Roo.data.DataProxy = function(){
5612     this.addEvents({
5613         /**
5614          * @event beforeload
5615          * Fires before a network request is made to retrieve a data object.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} params The params parameter to the load function.
5618          */
5619         beforeload : true,
5620         /**
5621          * @event load
5622          * Fires before the load method's callback is called.
5623          * @param {Object} This DataProxy object.
5624          * @param {Object} o The data object.
5625          * @param {Object} arg The callback argument object passed to the load function.
5626          */
5627         load : true,
5628         /**
5629          * @event loadexception
5630          * Fires if an Exception occurs during data retrieval.
5631          * @param {Object} This DataProxy object.
5632          * @param {Object} o The data object.
5633          * @param {Object} arg The callback argument object passed to the load function.
5634          * @param {Object} e The Exception.
5635          */
5636         loadexception : true
5637     });
5638     Roo.data.DataProxy.superclass.constructor.call(this);
5639 };
5640
5641 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5642
5643     /**
5644      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5645      */
5646 /*
5647  * Based on:
5648  * Ext JS Library 1.1.1
5649  * Copyright(c) 2006-2007, Ext JS, LLC.
5650  *
5651  * Originally Released Under LGPL - original licence link has changed is not relivant.
5652  *
5653  * Fork - LGPL
5654  * <script type="text/javascript">
5655  */
5656 /**
5657  * @class Roo.data.MemoryProxy
5658  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5659  * to the Reader when its load method is called.
5660  * @constructor
5661  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5662  */
5663 Roo.data.MemoryProxy = function(data){
5664     if (data.data) {
5665         data = data.data;
5666     }
5667     Roo.data.MemoryProxy.superclass.constructor.call(this);
5668     this.data = data;
5669 };
5670
5671 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5672     /**
5673      * Load data from the requested source (in this case an in-memory
5674      * data object passed to the constructor), read the data object into
5675      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5676      * process that block using the passed callback.
5677      * @param {Object} params This parameter is not used by the MemoryProxy class.
5678      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5679      * object into a block of Roo.data.Records.
5680      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5681      * The function must be passed <ul>
5682      * <li>The Record block object</li>
5683      * <li>The "arg" argument from the load function</li>
5684      * <li>A boolean success indicator</li>
5685      * </ul>
5686      * @param {Object} scope The scope in which to call the callback
5687      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5688      */
5689     load : function(params, reader, callback, scope, arg){
5690         params = params || {};
5691         var result;
5692         try {
5693             result = reader.readRecords(this.data);
5694         }catch(e){
5695             this.fireEvent("loadexception", this, arg, null, e);
5696             callback.call(scope, null, arg, false);
5697             return;
5698         }
5699         callback.call(scope, result, arg, true);
5700     },
5701     
5702     // private
5703     update : function(params, records){
5704         
5705     }
5706 });/*
5707  * Based on:
5708  * Ext JS Library 1.1.1
5709  * Copyright(c) 2006-2007, Ext JS, LLC.
5710  *
5711  * Originally Released Under LGPL - original licence link has changed is not relivant.
5712  *
5713  * Fork - LGPL
5714  * <script type="text/javascript">
5715  */
5716 /**
5717  * @class Roo.data.HttpProxy
5718  * @extends Roo.data.DataProxy
5719  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5720  * configured to reference a certain URL.<br><br>
5721  * <p>
5722  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5723  * from which the running page was served.<br><br>
5724  * <p>
5725  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5726  * <p>
5727  * Be aware that to enable the browser to parse an XML document, the server must set
5728  * the Content-Type header in the HTTP response to "text/xml".
5729  * @constructor
5730  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5731  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5732  * will be used to make the request.
5733  */
5734 Roo.data.HttpProxy = function(conn){
5735     Roo.data.HttpProxy.superclass.constructor.call(this);
5736     // is conn a conn config or a real conn?
5737     this.conn = conn;
5738     this.useAjax = !conn || !conn.events;
5739   
5740 };
5741
5742 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5743     // thse are take from connection...
5744     
5745     /**
5746      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5750      * extra parameters to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5754      *  to each request made by this object. (defaults to undefined)
5755      */
5756     /**
5757      * @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)
5758      */
5759     /**
5760      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5761      */
5762      /**
5763      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5764      * @type Boolean
5765      */
5766   
5767
5768     /**
5769      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5770      * @type Boolean
5771      */
5772     /**
5773      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5774      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5775      * a finer-grained basis than the DataProxy events.
5776      */
5777     getConnection : function(){
5778         return this.useAjax ? Roo.Ajax : this.conn;
5779     },
5780
5781     /**
5782      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5783      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5784      * process that block using the passed callback.
5785      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5786      * for the request to the remote server.
5787      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5788      * object into a block of Roo.data.Records.
5789      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5790      * The function must be passed <ul>
5791      * <li>The Record block object</li>
5792      * <li>The "arg" argument from the load function</li>
5793      * <li>A boolean success indicator</li>
5794      * </ul>
5795      * @param {Object} scope The scope in which to call the callback
5796      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5797      */
5798     load : function(params, reader, callback, scope, arg){
5799         if(this.fireEvent("beforeload", this, params) !== false){
5800             var  o = {
5801                 params : params || {},
5802                 request: {
5803                     callback : callback,
5804                     scope : scope,
5805                     arg : arg
5806                 },
5807                 reader: reader,
5808                 callback : this.loadResponse,
5809                 scope: this
5810             };
5811             if(this.useAjax){
5812                 Roo.applyIf(o, this.conn);
5813                 if(this.activeRequest){
5814                     Roo.Ajax.abort(this.activeRequest);
5815                 }
5816                 this.activeRequest = Roo.Ajax.request(o);
5817             }else{
5818                 this.conn.request(o);
5819             }
5820         }else{
5821             callback.call(scope||this, null, arg, false);
5822         }
5823     },
5824
5825     // private
5826     loadResponse : function(o, success, response){
5827         delete this.activeRequest;
5828         if(!success){
5829             this.fireEvent("loadexception", this, o, response);
5830             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5831             return;
5832         }
5833         var result;
5834         try {
5835             result = o.reader.read(response);
5836         }catch(e){
5837             this.fireEvent("loadexception", this, o, response, e);
5838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5839             return;
5840         }
5841         
5842         this.fireEvent("load", this, o, o.request.arg);
5843         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5844     },
5845
5846     // private
5847     update : function(dataSet){
5848
5849     },
5850
5851     // private
5852     updateResponse : function(dataSet){
5853
5854     }
5855 });/*
5856  * Based on:
5857  * Ext JS Library 1.1.1
5858  * Copyright(c) 2006-2007, Ext JS, LLC.
5859  *
5860  * Originally Released Under LGPL - original licence link has changed is not relivant.
5861  *
5862  * Fork - LGPL
5863  * <script type="text/javascript">
5864  */
5865
5866 /**
5867  * @class Roo.data.ScriptTagProxy
5868  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5869  * other than the originating domain of the running page.<br><br>
5870  * <p>
5871  * <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
5872  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5873  * <p>
5874  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5875  * source code that is used as the source inside a &lt;script> tag.<br><br>
5876  * <p>
5877  * In order for the browser to process the returned data, the server must wrap the data object
5878  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5879  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5880  * depending on whether the callback name was passed:
5881  * <p>
5882  * <pre><code>
5883 boolean scriptTag = false;
5884 String cb = request.getParameter("callback");
5885 if (cb != null) {
5886     scriptTag = true;
5887     response.setContentType("text/javascript");
5888 } else {
5889     response.setContentType("application/x-json");
5890 }
5891 Writer out = response.getWriter();
5892 if (scriptTag) {
5893     out.write(cb + "(");
5894 }
5895 out.print(dataBlock.toJsonString());
5896 if (scriptTag) {
5897     out.write(");");
5898 }
5899 </pre></code>
5900  *
5901  * @constructor
5902  * @param {Object} config A configuration object.
5903  */
5904 Roo.data.ScriptTagProxy = function(config){
5905     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5906     Roo.apply(this, config);
5907     this.head = document.getElementsByTagName("head")[0];
5908 };
5909
5910 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5911
5912 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5913     /**
5914      * @cfg {String} url The URL from which to request the data object.
5915      */
5916     /**
5917      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5918      */
5919     timeout : 30000,
5920     /**
5921      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5922      * the server the name of the callback function set up by the load call to process the returned data object.
5923      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5924      * javascript output which calls this named function passing the data object as its only parameter.
5925      */
5926     callbackParam : "callback",
5927     /**
5928      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5929      * name to the request.
5930      */
5931     nocache : true,
5932
5933     /**
5934      * Load data from the configured URL, read the data object into
5935      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5936      * process that block using the passed callback.
5937      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5938      * for the request to the remote server.
5939      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5940      * object into a block of Roo.data.Records.
5941      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5942      * The function must be passed <ul>
5943      * <li>The Record block object</li>
5944      * <li>The "arg" argument from the load function</li>
5945      * <li>A boolean success indicator</li>
5946      * </ul>
5947      * @param {Object} scope The scope in which to call the callback
5948      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5949      */
5950     load : function(params, reader, callback, scope, arg){
5951         if(this.fireEvent("beforeload", this, params) !== false){
5952
5953             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5954
5955             var url = this.url;
5956             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5957             if(this.nocache){
5958                 url += "&_dc=" + (new Date().getTime());
5959             }
5960             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5961             var trans = {
5962                 id : transId,
5963                 cb : "stcCallback"+transId,
5964                 scriptId : "stcScript"+transId,
5965                 params : params,
5966                 arg : arg,
5967                 url : url,
5968                 callback : callback,
5969                 scope : scope,
5970                 reader : reader
5971             };
5972             var conn = this;
5973
5974             window[trans.cb] = function(o){
5975                 conn.handleResponse(o, trans);
5976             };
5977
5978             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5979
5980             if(this.autoAbort !== false){
5981                 this.abort();
5982             }
5983
5984             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5985
5986             var script = document.createElement("script");
5987             script.setAttribute("src", url);
5988             script.setAttribute("type", "text/javascript");
5989             script.setAttribute("id", trans.scriptId);
5990             this.head.appendChild(script);
5991
5992             this.trans = trans;
5993         }else{
5994             callback.call(scope||this, null, arg, false);
5995         }
5996     },
5997
5998     // private
5999     isLoading : function(){
6000         return this.trans ? true : false;
6001     },
6002
6003     /**
6004      * Abort the current server request.
6005      */
6006     abort : function(){
6007         if(this.isLoading()){
6008             this.destroyTrans(this.trans);
6009         }
6010     },
6011
6012     // private
6013     destroyTrans : function(trans, isLoaded){
6014         this.head.removeChild(document.getElementById(trans.scriptId));
6015         clearTimeout(trans.timeoutId);
6016         if(isLoaded){
6017             window[trans.cb] = undefined;
6018             try{
6019                 delete window[trans.cb];
6020             }catch(e){}
6021         }else{
6022             // if hasn't been loaded, wait for load to remove it to prevent script error
6023             window[trans.cb] = function(){
6024                 window[trans.cb] = undefined;
6025                 try{
6026                     delete window[trans.cb];
6027                 }catch(e){}
6028             };
6029         }
6030     },
6031
6032     // private
6033     handleResponse : function(o, trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, true);
6036         var result;
6037         try {
6038             result = trans.reader.readRecords(o);
6039         }catch(e){
6040             this.fireEvent("loadexception", this, o, trans.arg, e);
6041             trans.callback.call(trans.scope||window, null, trans.arg, false);
6042             return;
6043         }
6044         this.fireEvent("load", this, o, trans.arg);
6045         trans.callback.call(trans.scope||window, result, trans.arg, true);
6046     },
6047
6048     // private
6049     handleFailure : function(trans){
6050         this.trans = false;
6051         this.destroyTrans(trans, false);
6052         this.fireEvent("loadexception", this, null, trans.arg);
6053         trans.callback.call(trans.scope||window, null, trans.arg, false);
6054     }
6055 });/*
6056  * Based on:
6057  * Ext JS Library 1.1.1
6058  * Copyright(c) 2006-2007, Ext JS, LLC.
6059  *
6060  * Originally Released Under LGPL - original licence link has changed is not relivant.
6061  *
6062  * Fork - LGPL
6063  * <script type="text/javascript">
6064  */
6065
6066 /**
6067  * @class Roo.data.JsonReader
6068  * @extends Roo.data.DataReader
6069  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6070  * based on mappings in a provided Roo.data.Record constructor.
6071  * 
6072  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6073  * in the reply previously. 
6074  * 
6075  * <p>
6076  * Example code:
6077  * <pre><code>
6078 var RecordDef = Roo.data.Record.create([
6079     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6080     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6081 ]);
6082 var myReader = new Roo.data.JsonReader({
6083     totalProperty: "results",    // The property which contains the total dataset size (optional)
6084     root: "rows",                // The property which contains an Array of row objects
6085     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6086 }, RecordDef);
6087 </code></pre>
6088  * <p>
6089  * This would consume a JSON file like this:
6090  * <pre><code>
6091 { 'results': 2, 'rows': [
6092     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6093     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6094 }
6095 </code></pre>
6096  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6097  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6098  * paged from the remote server.
6099  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6100  * @cfg {String} root name of the property which contains the Array of row objects.
6101  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6102  * @constructor
6103  * Create a new JsonReader
6104  * @param {Object} meta Metadata configuration options
6105  * @param {Object} recordType Either an Array of field definition objects,
6106  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6107  */
6108 Roo.data.JsonReader = function(meta, recordType){
6109     
6110     meta = meta || {};
6111     // set some defaults:
6112     Roo.applyIf(meta, {
6113         totalProperty: 'total',
6114         successProperty : 'success',
6115         root : 'data',
6116         id : 'id'
6117     });
6118     
6119     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6120 };
6121 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6122     
6123     /**
6124      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6125      * Used by Store query builder to append _requestMeta to params.
6126      * 
6127      */
6128     metaFromRemote : false,
6129     /**
6130      * This method is only used by a DataProxy which has retrieved data from a remote server.
6131      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6132      * @return {Object} data A data block which is used by an Roo.data.Store object as
6133      * a cache of Roo.data.Records.
6134      */
6135     read : function(response){
6136         var json = response.responseText;
6137        
6138         var o = /* eval:var:o */ eval("("+json+")");
6139         if(!o) {
6140             throw {message: "JsonReader.read: Json object not found"};
6141         }
6142         
6143         if(o.metaData){
6144             
6145             delete this.ef;
6146             this.metaFromRemote = true;
6147             this.meta = o.metaData;
6148             this.recordType = Roo.data.Record.create(o.metaData.fields);
6149             this.onMetaChange(this.meta, this.recordType, o);
6150         }
6151         return this.readRecords(o);
6152     },
6153
6154     // private function a store will implement
6155     onMetaChange : function(meta, recordType, o){
6156
6157     },
6158
6159     /**
6160          * @ignore
6161          */
6162     simpleAccess: function(obj, subsc) {
6163         return obj[subsc];
6164     },
6165
6166         /**
6167          * @ignore
6168          */
6169     getJsonAccessor: function(){
6170         var re = /[\[\.]/;
6171         return function(expr) {
6172             try {
6173                 return(re.test(expr))
6174                     ? new Function("obj", "return obj." + expr)
6175                     : function(obj){
6176                         return obj[expr];
6177                     };
6178             } catch(e){}
6179             return Roo.emptyFn;
6180         };
6181     }(),
6182
6183     /**
6184      * Create a data block containing Roo.data.Records from an XML document.
6185      * @param {Object} o An object which contains an Array of row objects in the property specified
6186      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6187      * which contains the total size of the dataset.
6188      * @return {Object} data A data block which is used by an Roo.data.Store object as
6189      * a cache of Roo.data.Records.
6190      */
6191     readRecords : function(o){
6192         /**
6193          * After any data loads, the raw JSON data is available for further custom processing.
6194          * @type Object
6195          */
6196         this.jsonData = o;
6197         var s = this.meta, Record = this.recordType,
6198             f = Record.prototype.fields, fi = f.items, fl = f.length;
6199
6200 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6201         if (!this.ef) {
6202             if(s.totalProperty) {
6203                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6204                 }
6205                 if(s.successProperty) {
6206                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6207                 }
6208                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6209                 if (s.id) {
6210                         var g = this.getJsonAccessor(s.id);
6211                         this.getId = function(rec) {
6212                                 var r = g(rec);
6213                                 return (r === undefined || r === "") ? null : r;
6214                         };
6215                 } else {
6216                         this.getId = function(){return null;};
6217                 }
6218             this.ef = [];
6219             for(var jj = 0; jj < fl; jj++){
6220                 f = fi[jj];
6221                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6222                 this.ef[jj] = this.getJsonAccessor(map);
6223             }
6224         }
6225
6226         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6227         if(s.totalProperty){
6228             var vt = parseInt(this.getTotal(o), 10);
6229             if(!isNaN(vt)){
6230                 totalRecords = vt;
6231             }
6232         }
6233         if(s.successProperty){
6234             var vs = this.getSuccess(o);
6235             if(vs === false || vs === 'false'){
6236                 success = false;
6237             }
6238         }
6239         var records = [];
6240             for(var i = 0; i < c; i++){
6241                     var n = root[i];
6242                 var values = {};
6243                 var id = this.getId(n);
6244                 for(var j = 0; j < fl; j++){
6245                     f = fi[j];
6246                 var v = this.ef[j](n);
6247                 if (!f.convert) {
6248                     Roo.log('missing convert for ' + f.name);
6249                     Roo.log(f);
6250                     continue;
6251                 }
6252                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6253                 }
6254                 var record = new Record(values, id);
6255                 record.json = n;
6256                 records[i] = record;
6257             }
6258             return {
6259                 success : success,
6260                 records : records,
6261                 totalRecords : totalRecords
6262             };
6263     }
6264 });/*
6265  * Based on:
6266  * Ext JS Library 1.1.1
6267  * Copyright(c) 2006-2007, Ext JS, LLC.
6268  *
6269  * Originally Released Under LGPL - original licence link has changed is not relivant.
6270  *
6271  * Fork - LGPL
6272  * <script type="text/javascript">
6273  */
6274
6275 /**
6276  * @class Roo.data.XmlReader
6277  * @extends Roo.data.DataReader
6278  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6279  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6280  * <p>
6281  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6282  * header in the HTTP response must be set to "text/xml".</em>
6283  * <p>
6284  * Example code:
6285  * <pre><code>
6286 var RecordDef = Roo.data.Record.create([
6287    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6288    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6289 ]);
6290 var myReader = new Roo.data.XmlReader({
6291    totalRecords: "results", // The element which contains the total dataset size (optional)
6292    record: "row",           // The repeated element which contains row information
6293    id: "id"                 // The element within the row that provides an ID for the record (optional)
6294 }, RecordDef);
6295 </code></pre>
6296  * <p>
6297  * This would consume an XML file like this:
6298  * <pre><code>
6299 &lt;?xml?>
6300 &lt;dataset>
6301  &lt;results>2&lt;/results>
6302  &lt;row>
6303    &lt;id>1&lt;/id>
6304    &lt;name>Bill&lt;/name>
6305    &lt;occupation>Gardener&lt;/occupation>
6306  &lt;/row>
6307  &lt;row>
6308    &lt;id>2&lt;/id>
6309    &lt;name>Ben&lt;/name>
6310    &lt;occupation>Horticulturalist&lt;/occupation>
6311  &lt;/row>
6312 &lt;/dataset>
6313 </code></pre>
6314  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6315  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6316  * paged from the remote server.
6317  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6318  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6319  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6320  * a record identifier value.
6321  * @constructor
6322  * Create a new XmlReader
6323  * @param {Object} meta Metadata configuration options
6324  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6325  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6326  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6327  */
6328 Roo.data.XmlReader = function(meta, recordType){
6329     meta = meta || {};
6330     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6331 };
6332 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6333     /**
6334      * This method is only used by a DataProxy which has retrieved data from a remote server.
6335          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6336          * to contain a method called 'responseXML' that returns an XML document object.
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     read : function(response){
6341         var doc = response.responseXML;
6342         if(!doc) {
6343             throw {message: "XmlReader.read: XML Document not available"};
6344         }
6345         return this.readRecords(doc);
6346     },
6347
6348     /**
6349      * Create a data block containing Roo.data.Records from an XML document.
6350          * @param {Object} doc A parsed XML document.
6351      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6352      * a cache of Roo.data.Records.
6353      */
6354     readRecords : function(doc){
6355         /**
6356          * After any data loads/reads, the raw XML Document is available for further custom processing.
6357          * @type XMLDocument
6358          */
6359         this.xmlData = doc;
6360         var root = doc.documentElement || doc;
6361         var q = Roo.DomQuery;
6362         var recordType = this.recordType, fields = recordType.prototype.fields;
6363         var sid = this.meta.id;
6364         var totalRecords = 0, success = true;
6365         if(this.meta.totalRecords){
6366             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6367         }
6368         
6369         if(this.meta.success){
6370             var sv = q.selectValue(this.meta.success, root, true);
6371             success = sv !== false && sv !== 'false';
6372         }
6373         var records = [];
6374         var ns = q.select(this.meta.record, root);
6375         for(var i = 0, len = ns.length; i < len; i++) {
6376                 var n = ns[i];
6377                 var values = {};
6378                 var id = sid ? q.selectValue(sid, n) : undefined;
6379                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6380                     var f = fields.items[j];
6381                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6382                     v = f.convert(v);
6383                     values[f.name] = v;
6384                 }
6385                 var record = new recordType(values, id);
6386                 record.node = n;
6387                 records[records.length] = record;
6388             }
6389
6390             return {
6391                 success : success,
6392                 records : records,
6393                 totalRecords : totalRecords || records.length
6394             };
6395     }
6396 });/*
6397  * Based on:
6398  * Ext JS Library 1.1.1
6399  * Copyright(c) 2006-2007, Ext JS, LLC.
6400  *
6401  * Originally Released Under LGPL - original licence link has changed is not relivant.
6402  *
6403  * Fork - LGPL
6404  * <script type="text/javascript">
6405  */
6406
6407 /**
6408  * @class Roo.data.ArrayReader
6409  * @extends Roo.data.DataReader
6410  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6411  * Each element of that Array represents a row of data fields. The
6412  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6413  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6414  * <p>
6415  * Example code:.
6416  * <pre><code>
6417 var RecordDef = Roo.data.Record.create([
6418     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6419     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6420 ]);
6421 var myReader = new Roo.data.ArrayReader({
6422     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6423 }, RecordDef);
6424 </code></pre>
6425  * <p>
6426  * This would consume an Array like this:
6427  * <pre><code>
6428 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6429   </code></pre>
6430  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6431  * @constructor
6432  * Create a new JsonReader
6433  * @param {Object} meta Metadata configuration options.
6434  * @param {Object} recordType Either an Array of field definition objects
6435  * as specified to {@link Roo.data.Record#create},
6436  * or an {@link Roo.data.Record} object
6437  * created using {@link Roo.data.Record#create}.
6438  */
6439 Roo.data.ArrayReader = function(meta, recordType){
6440     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6441 };
6442
6443 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6444     /**
6445      * Create a data block containing Roo.data.Records from an XML document.
6446      * @param {Object} o An Array of row objects which represents the dataset.
6447      * @return {Object} data A data block which is used by an Roo.data.Store object as
6448      * a cache of Roo.data.Records.
6449      */
6450     readRecords : function(o){
6451         var sid = this.meta ? this.meta.id : null;
6452         var recordType = this.recordType, fields = recordType.prototype.fields;
6453         var records = [];
6454         var root = o;
6455             for(var i = 0; i < root.length; i++){
6456                     var n = root[i];
6457                 var values = {};
6458                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6459                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6460                 var f = fields.items[j];
6461                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6462                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6463                 v = f.convert(v);
6464                 values[f.name] = v;
6465             }
6466                 var record = new recordType(values, id);
6467                 record.json = n;
6468                 records[records.length] = record;
6469             }
6470             return {
6471                 records : records,
6472                 totalRecords : records.length
6473             };
6474     }
6475 });/*
6476  * Based on:
6477  * Ext JS Library 1.1.1
6478  * Copyright(c) 2006-2007, Ext JS, LLC.
6479  *
6480  * Originally Released Under LGPL - original licence link has changed is not relivant.
6481  *
6482  * Fork - LGPL
6483  * <script type="text/javascript">
6484  */
6485
6486
6487 /**
6488  * @class Roo.data.Tree
6489  * @extends Roo.util.Observable
6490  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6491  * in the tree have most standard DOM functionality.
6492  * @constructor
6493  * @param {Node} root (optional) The root node
6494  */
6495 Roo.data.Tree = function(root){
6496    this.nodeHash = {};
6497    /**
6498     * The root node for this tree
6499     * @type Node
6500     */
6501    this.root = null;
6502    if(root){
6503        this.setRootNode(root);
6504    }
6505    this.addEvents({
6506        /**
6507         * @event append
6508         * Fires when a new child node is appended to a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The newly appended node
6512         * @param {Number} index The index of the newly appended node
6513         */
6514        "append" : true,
6515        /**
6516         * @event remove
6517         * Fires when a child node is removed from a node in this tree.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node removed
6521         */
6522        "remove" : true,
6523        /**
6524         * @event move
6525         * Fires when a node is moved to a new location in the tree
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} node The node moved
6528         * @param {Node} oldParent The old parent of this node
6529         * @param {Node} newParent The new parent of this node
6530         * @param {Number} index The index it was moved to
6531         */
6532        "move" : true,
6533        /**
6534         * @event insert
6535         * Fires when a new child node is inserted in a node in this tree.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node inserted
6539         * @param {Node} refNode The child node the node was inserted before
6540         */
6541        "insert" : true,
6542        /**
6543         * @event beforeappend
6544         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} parent The parent node
6547         * @param {Node} node The child node to be appended
6548         */
6549        "beforeappend" : true,
6550        /**
6551         * @event beforeremove
6552         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6553         * @param {Tree} tree The owner tree
6554         * @param {Node} parent The parent node
6555         * @param {Node} node The child node to be removed
6556         */
6557        "beforeremove" : true,
6558        /**
6559         * @event beforemove
6560         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} node The node being moved
6563         * @param {Node} oldParent The parent of the node
6564         * @param {Node} newParent The new parent the node is moving to
6565         * @param {Number} index The index it is being moved to
6566         */
6567        "beforemove" : true,
6568        /**
6569         * @event beforeinsert
6570         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6571         * @param {Tree} tree The owner tree
6572         * @param {Node} parent The parent node
6573         * @param {Node} node The child node to be inserted
6574         * @param {Node} refNode The child node the node is being inserted before
6575         */
6576        "beforeinsert" : true
6577    });
6578
6579     Roo.data.Tree.superclass.constructor.call(this);
6580 };
6581
6582 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6583     pathSeparator: "/",
6584
6585     proxyNodeEvent : function(){
6586         return this.fireEvent.apply(this, arguments);
6587     },
6588
6589     /**
6590      * Returns the root node for this tree.
6591      * @return {Node}
6592      */
6593     getRootNode : function(){
6594         return this.root;
6595     },
6596
6597     /**
6598      * Sets the root node for this tree.
6599      * @param {Node} node
6600      * @return {Node}
6601      */
6602     setRootNode : function(node){
6603         this.root = node;
6604         node.ownerTree = this;
6605         node.isRoot = true;
6606         this.registerNode(node);
6607         return node;
6608     },
6609
6610     /**
6611      * Gets a node in this tree by its id.
6612      * @param {String} id
6613      * @return {Node}
6614      */
6615     getNodeById : function(id){
6616         return this.nodeHash[id];
6617     },
6618
6619     registerNode : function(node){
6620         this.nodeHash[node.id] = node;
6621     },
6622
6623     unregisterNode : function(node){
6624         delete this.nodeHash[node.id];
6625     },
6626
6627     toString : function(){
6628         return "[Tree"+(this.id?" "+this.id:"")+"]";
6629     }
6630 });
6631
6632 /**
6633  * @class Roo.data.Node
6634  * @extends Roo.util.Observable
6635  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6636  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6637  * @constructor
6638  * @param {Object} attributes The attributes/config for the node
6639  */
6640 Roo.data.Node = function(attributes){
6641     /**
6642      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6643      * @type {Object}
6644      */
6645     this.attributes = attributes || {};
6646     this.leaf = this.attributes.leaf;
6647     /**
6648      * The node id. @type String
6649      */
6650     this.id = this.attributes.id;
6651     if(!this.id){
6652         this.id = Roo.id(null, "ynode-");
6653         this.attributes.id = this.id;
6654     }
6655     /**
6656      * All child nodes of this node. @type Array
6657      */
6658     this.childNodes = [];
6659     if(!this.childNodes.indexOf){ // indexOf is a must
6660         this.childNodes.indexOf = function(o){
6661             for(var i = 0, len = this.length; i < len; i++){
6662                 if(this[i] == o) {
6663                     return i;
6664                 }
6665             }
6666             return -1;
6667         };
6668     }
6669     /**
6670      * The parent node for this node. @type Node
6671      */
6672     this.parentNode = null;
6673     /**
6674      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.firstChild = null;
6677     /**
6678      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6679      */
6680     this.lastChild = null;
6681     /**
6682      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.previousSibling = null;
6685     /**
6686      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6687      */
6688     this.nextSibling = null;
6689
6690     this.addEvents({
6691        /**
6692         * @event append
6693         * Fires when a new child node is appended
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The newly appended node
6697         * @param {Number} index The index of the newly appended node
6698         */
6699        "append" : true,
6700        /**
6701         * @event remove
6702         * Fires when a child node is removed
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The removed node
6706         */
6707        "remove" : true,
6708        /**
6709         * @event move
6710         * Fires when this node is moved to a new location in the tree
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} oldParent The old parent of this node
6714         * @param {Node} newParent The new parent of this node
6715         * @param {Number} index The index it was moved to
6716         */
6717        "move" : true,
6718        /**
6719         * @event insert
6720         * Fires when a new child node is inserted.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node inserted
6724         * @param {Node} refNode The child node the node was inserted before
6725         */
6726        "insert" : true,
6727        /**
6728         * @event beforeappend
6729         * Fires before a new child is appended, return false to cancel the append.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The child node to be appended
6733         */
6734        "beforeappend" : true,
6735        /**
6736         * @event beforeremove
6737         * Fires before a child is removed, return false to cancel the remove.
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} node The child node to be removed
6741         */
6742        "beforeremove" : true,
6743        /**
6744         * @event beforemove
6745         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} oldParent The parent of this node
6749         * @param {Node} newParent The new parent this node is moving to
6750         * @param {Number} index The index it is being moved to
6751         */
6752        "beforemove" : true,
6753        /**
6754         * @event beforeinsert
6755         * Fires before a new child is inserted, return false to cancel the insert.
6756         * @param {Tree} tree The owner tree
6757         * @param {Node} this This node
6758         * @param {Node} node The child node to be inserted
6759         * @param {Node} refNode The child node the node is being inserted before
6760         */
6761        "beforeinsert" : true
6762    });
6763     this.listeners = this.attributes.listeners;
6764     Roo.data.Node.superclass.constructor.call(this);
6765 };
6766
6767 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6768     fireEvent : function(evtName){
6769         // first do standard event for this node
6770         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6771             return false;
6772         }
6773         // then bubble it up to the tree if the event wasn't cancelled
6774         var ot = this.getOwnerTree();
6775         if(ot){
6776             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6777                 return false;
6778             }
6779         }
6780         return true;
6781     },
6782
6783     /**
6784      * Returns true if this node is a leaf
6785      * @return {Boolean}
6786      */
6787     isLeaf : function(){
6788         return this.leaf === true;
6789     },
6790
6791     // private
6792     setFirstChild : function(node){
6793         this.firstChild = node;
6794     },
6795
6796     //private
6797     setLastChild : function(node){
6798         this.lastChild = node;
6799     },
6800
6801
6802     /**
6803      * Returns true if this node is the last child of its parent
6804      * @return {Boolean}
6805      */
6806     isLast : function(){
6807        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6808     },
6809
6810     /**
6811      * Returns true if this node is the first child of its parent
6812      * @return {Boolean}
6813      */
6814     isFirst : function(){
6815        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6816     },
6817
6818     hasChildNodes : function(){
6819         return !this.isLeaf() && this.childNodes.length > 0;
6820     },
6821
6822     /**
6823      * Insert node(s) as the last child node of this node.
6824      * @param {Node/Array} node The node or Array of nodes to append
6825      * @return {Node} The appended node if single append, or null if an array was passed
6826      */
6827     appendChild : function(node){
6828         var multi = false;
6829         if(node instanceof Array){
6830             multi = node;
6831         }else if(arguments.length > 1){
6832             multi = arguments;
6833         }
6834         // if passed an array or multiple args do them one by one
6835         if(multi){
6836             for(var i = 0, len = multi.length; i < len; i++) {
6837                 this.appendChild(multi[i]);
6838             }
6839         }else{
6840             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6841                 return false;
6842             }
6843             var index = this.childNodes.length;
6844             var oldParent = node.parentNode;
6845             // it's a move, make sure we move it cleanly
6846             if(oldParent){
6847                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6848                     return false;
6849                 }
6850                 oldParent.removeChild(node);
6851             }
6852             index = this.childNodes.length;
6853             if(index == 0){
6854                 this.setFirstChild(node);
6855             }
6856             this.childNodes.push(node);
6857             node.parentNode = this;
6858             var ps = this.childNodes[index-1];
6859             if(ps){
6860                 node.previousSibling = ps;
6861                 ps.nextSibling = node;
6862             }else{
6863                 node.previousSibling = null;
6864             }
6865             node.nextSibling = null;
6866             this.setLastChild(node);
6867             node.setOwnerTree(this.getOwnerTree());
6868             this.fireEvent("append", this.ownerTree, this, node, index);
6869             if(oldParent){
6870                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6871             }
6872             return node;
6873         }
6874     },
6875
6876     /**
6877      * Removes a child node from this node.
6878      * @param {Node} node The node to remove
6879      * @return {Node} The removed node
6880      */
6881     removeChild : function(node){
6882         var index = this.childNodes.indexOf(node);
6883         if(index == -1){
6884             return false;
6885         }
6886         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6887             return false;
6888         }
6889
6890         // remove it from childNodes collection
6891         this.childNodes.splice(index, 1);
6892
6893         // update siblings
6894         if(node.previousSibling){
6895             node.previousSibling.nextSibling = node.nextSibling;
6896         }
6897         if(node.nextSibling){
6898             node.nextSibling.previousSibling = node.previousSibling;
6899         }
6900
6901         // update child refs
6902         if(this.firstChild == node){
6903             this.setFirstChild(node.nextSibling);
6904         }
6905         if(this.lastChild == node){
6906             this.setLastChild(node.previousSibling);
6907         }
6908
6909         node.setOwnerTree(null);
6910         // clear any references from the node
6911         node.parentNode = null;
6912         node.previousSibling = null;
6913         node.nextSibling = null;
6914         this.fireEvent("remove", this.ownerTree, this, node);
6915         return node;
6916     },
6917
6918     /**
6919      * Inserts the first node before the second node in this nodes childNodes collection.
6920      * @param {Node} node The node to insert
6921      * @param {Node} refNode The node to insert before (if null the node is appended)
6922      * @return {Node} The inserted node
6923      */
6924     insertBefore : function(node, refNode){
6925         if(!refNode){ // like standard Dom, refNode can be null for append
6926             return this.appendChild(node);
6927         }
6928         // nothing to do
6929         if(node == refNode){
6930             return false;
6931         }
6932
6933         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6934             return false;
6935         }
6936         var index = this.childNodes.indexOf(refNode);
6937         var oldParent = node.parentNode;
6938         var refIndex = index;
6939
6940         // when moving internally, indexes will change after remove
6941         if(oldParent == this && this.childNodes.indexOf(node) < index){
6942             refIndex--;
6943         }
6944
6945         // it's a move, make sure we move it cleanly
6946         if(oldParent){
6947             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6948                 return false;
6949             }
6950             oldParent.removeChild(node);
6951         }
6952         if(refIndex == 0){
6953             this.setFirstChild(node);
6954         }
6955         this.childNodes.splice(refIndex, 0, node);
6956         node.parentNode = this;
6957         var ps = this.childNodes[refIndex-1];
6958         if(ps){
6959             node.previousSibling = ps;
6960             ps.nextSibling = node;
6961         }else{
6962             node.previousSibling = null;
6963         }
6964         node.nextSibling = refNode;
6965         refNode.previousSibling = node;
6966         node.setOwnerTree(this.getOwnerTree());
6967         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6968         if(oldParent){
6969             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6970         }
6971         return node;
6972     },
6973
6974     /**
6975      * Returns the child node at the specified index.
6976      * @param {Number} index
6977      * @return {Node}
6978      */
6979     item : function(index){
6980         return this.childNodes[index];
6981     },
6982
6983     /**
6984      * Replaces one child node in this node with another.
6985      * @param {Node} newChild The replacement node
6986      * @param {Node} oldChild The node to replace
6987      * @return {Node} The replaced node
6988      */
6989     replaceChild : function(newChild, oldChild){
6990         this.insertBefore(newChild, oldChild);
6991         this.removeChild(oldChild);
6992         return oldChild;
6993     },
6994
6995     /**
6996      * Returns the index of a child node
6997      * @param {Node} node
6998      * @return {Number} The index of the node or -1 if it was not found
6999      */
7000     indexOf : function(child){
7001         return this.childNodes.indexOf(child);
7002     },
7003
7004     /**
7005      * Returns the tree this node is in.
7006      * @return {Tree}
7007      */
7008     getOwnerTree : function(){
7009         // if it doesn't have one, look for one
7010         if(!this.ownerTree){
7011             var p = this;
7012             while(p){
7013                 if(p.ownerTree){
7014                     this.ownerTree = p.ownerTree;
7015                     break;
7016                 }
7017                 p = p.parentNode;
7018             }
7019         }
7020         return this.ownerTree;
7021     },
7022
7023     /**
7024      * Returns depth of this node (the root node has a depth of 0)
7025      * @return {Number}
7026      */
7027     getDepth : function(){
7028         var depth = 0;
7029         var p = this;
7030         while(p.parentNode){
7031             ++depth;
7032             p = p.parentNode;
7033         }
7034         return depth;
7035     },
7036
7037     // private
7038     setOwnerTree : function(tree){
7039         // if it's move, we need to update everyone
7040         if(tree != this.ownerTree){
7041             if(this.ownerTree){
7042                 this.ownerTree.unregisterNode(this);
7043             }
7044             this.ownerTree = tree;
7045             var cs = this.childNodes;
7046             for(var i = 0, len = cs.length; i < len; i++) {
7047                 cs[i].setOwnerTree(tree);
7048             }
7049             if(tree){
7050                 tree.registerNode(this);
7051             }
7052         }
7053     },
7054
7055     /**
7056      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7057      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7058      * @return {String} The path
7059      */
7060     getPath : function(attr){
7061         attr = attr || "id";
7062         var p = this.parentNode;
7063         var b = [this.attributes[attr]];
7064         while(p){
7065             b.unshift(p.attributes[attr]);
7066             p = p.parentNode;
7067         }
7068         var sep = this.getOwnerTree().pathSeparator;
7069         return sep + b.join(sep);
7070     },
7071
7072     /**
7073      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7074      * function call will be the scope provided or the current node. The arguments to the function
7075      * will be the args provided or the current node. If the function returns false at any point,
7076      * the bubble is stopped.
7077      * @param {Function} fn The function to call
7078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7080      */
7081     bubble : function(fn, scope, args){
7082         var p = this;
7083         while(p){
7084             if(fn.call(scope || p, args || p) === false){
7085                 break;
7086             }
7087             p = p.parentNode;
7088         }
7089     },
7090
7091     /**
7092      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7093      * function call will be the scope provided or the current node. The arguments to the function
7094      * will be the args provided or the current node. If the function returns false at any point,
7095      * the cascade is stopped on that branch.
7096      * @param {Function} fn The function to call
7097      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7098      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7099      */
7100     cascade : function(fn, scope, args){
7101         if(fn.call(scope || this, args || this) !== false){
7102             var cs = this.childNodes;
7103             for(var i = 0, len = cs.length; i < len; i++) {
7104                 cs[i].cascade(fn, scope, args);
7105             }
7106         }
7107     },
7108
7109     /**
7110      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7111      * function call will be the scope provided or the current node. The arguments to the function
7112      * will be the args provided or the current node. If the function returns false at any point,
7113      * the iteration stops.
7114      * @param {Function} fn The function to call
7115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7117      */
7118     eachChild : function(fn, scope, args){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(fn.call(scope || this, args || cs[i]) === false){
7122                     break;
7123                 }
7124         }
7125     },
7126
7127     /**
7128      * Finds the first child that has the attribute with the specified value.
7129      * @param {String} attribute The attribute name
7130      * @param {Mixed} value The value to search for
7131      * @return {Node} The found child or null if none was found
7132      */
7133     findChild : function(attribute, value){
7134         var cs = this.childNodes;
7135         for(var i = 0, len = cs.length; i < len; i++) {
7136                 if(cs[i].attributes[attribute] == value){
7137                     return cs[i];
7138                 }
7139         }
7140         return null;
7141     },
7142
7143     /**
7144      * Finds the first child by a custom function. The child matches if the function passed
7145      * returns true.
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      * @return {Node} The found child or null if none was found
7149      */
7150     findChildBy : function(fn, scope){
7151         var cs = this.childNodes;
7152         for(var i = 0, len = cs.length; i < len; i++) {
7153                 if(fn.call(scope||cs[i], cs[i]) === true){
7154                     return cs[i];
7155                 }
7156         }
7157         return null;
7158     },
7159
7160     /**
7161      * Sorts this nodes children using the supplied sort function
7162      * @param {Function} fn
7163      * @param {Object} scope (optional)
7164      */
7165     sort : function(fn, scope){
7166         var cs = this.childNodes;
7167         var len = cs.length;
7168         if(len > 0){
7169             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7170             cs.sort(sortFn);
7171             for(var i = 0; i < len; i++){
7172                 var n = cs[i];
7173                 n.previousSibling = cs[i-1];
7174                 n.nextSibling = cs[i+1];
7175                 if(i == 0){
7176                     this.setFirstChild(n);
7177                 }
7178                 if(i == len-1){
7179                     this.setLastChild(n);
7180                 }
7181             }
7182         }
7183     },
7184
7185     /**
7186      * Returns true if this node is an ancestor (at any point) of the passed node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     contains : function(node){
7191         return node.isAncestor(this);
7192     },
7193
7194     /**
7195      * Returns true if the passed node is an ancestor (at any point) of this node.
7196      * @param {Node} node
7197      * @return {Boolean}
7198      */
7199     isAncestor : function(node){
7200         var p = this.parentNode;
7201         while(p){
7202             if(p == node){
7203                 return true;
7204             }
7205             p = p.parentNode;
7206         }
7207         return false;
7208     },
7209
7210     toString : function(){
7211         return "[Node"+(this.id?" "+this.id:"")+"]";
7212     }
7213 });/*
7214  * Based on:
7215  * Ext JS Library 1.1.1
7216  * Copyright(c) 2006-2007, Ext JS, LLC.
7217  *
7218  * Originally Released Under LGPL - original licence link has changed is not relivant.
7219  *
7220  * Fork - LGPL
7221  * <script type="text/javascript">
7222  */
7223  
7224
7225 /**
7226  * @class Roo.ComponentMgr
7227  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7228  * @singleton
7229  */
7230 Roo.ComponentMgr = function(){
7231     var all = new Roo.util.MixedCollection();
7232
7233     return {
7234         /**
7235          * Registers a component.
7236          * @param {Roo.Component} c The component
7237          */
7238         register : function(c){
7239             all.add(c);
7240         },
7241
7242         /**
7243          * Unregisters a component.
7244          * @param {Roo.Component} c The component
7245          */
7246         unregister : function(c){
7247             all.remove(c);
7248         },
7249
7250         /**
7251          * Returns a component by id
7252          * @param {String} id The component id
7253          */
7254         get : function(id){
7255             return all.get(id);
7256         },
7257
7258         /**
7259          * Registers a function that will be called when a specified component is added to ComponentMgr
7260          * @param {String} id The component id
7261          * @param {Funtction} fn The callback function
7262          * @param {Object} scope The scope of the callback
7263          */
7264         onAvailable : function(id, fn, scope){
7265             all.on("add", function(index, o){
7266                 if(o.id == id){
7267                     fn.call(scope || o, o);
7268                     all.un("add", fn, scope);
7269                 }
7270             });
7271         }
7272     };
7273 }();/*
7274  * Based on:
7275  * Ext JS Library 1.1.1
7276  * Copyright(c) 2006-2007, Ext JS, LLC.
7277  *
7278  * Originally Released Under LGPL - original licence link has changed is not relivant.
7279  *
7280  * Fork - LGPL
7281  * <script type="text/javascript">
7282  */
7283  
7284 /**
7285  * @class Roo.Component
7286  * @extends Roo.util.Observable
7287  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7288  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7289  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7290  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7291  * All visual components (widgets) that require rendering into a layout should subclass Component.
7292  * @constructor
7293  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7294  * 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
7295  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7296  */
7297 Roo.Component = function(config){
7298     config = config || {};
7299     if(config.tagName || config.dom || typeof config == "string"){ // element object
7300         config = {el: config, id: config.id || config};
7301     }
7302     this.initialConfig = config;
7303
7304     Roo.apply(this, config);
7305     this.addEvents({
7306         /**
7307          * @event disable
7308          * Fires after the component is disabled.
7309              * @param {Roo.Component} this
7310              */
7311         disable : true,
7312         /**
7313          * @event enable
7314          * Fires after the component is enabled.
7315              * @param {Roo.Component} this
7316              */
7317         enable : true,
7318         /**
7319          * @event beforeshow
7320          * Fires before the component is shown.  Return false to stop the show.
7321              * @param {Roo.Component} this
7322              */
7323         beforeshow : true,
7324         /**
7325          * @event show
7326          * Fires after the component is shown.
7327              * @param {Roo.Component} this
7328              */
7329         show : true,
7330         /**
7331          * @event beforehide
7332          * Fires before the component is hidden. Return false to stop the hide.
7333              * @param {Roo.Component} this
7334              */
7335         beforehide : true,
7336         /**
7337          * @event hide
7338          * Fires after the component is hidden.
7339              * @param {Roo.Component} this
7340              */
7341         hide : true,
7342         /**
7343          * @event beforerender
7344          * Fires before the component is rendered. Return false to stop the render.
7345              * @param {Roo.Component} this
7346              */
7347         beforerender : true,
7348         /**
7349          * @event render
7350          * Fires after the component is rendered.
7351              * @param {Roo.Component} this
7352              */
7353         render : true,
7354         /**
7355          * @event beforedestroy
7356          * Fires before the component is destroyed. Return false to stop the destroy.
7357              * @param {Roo.Component} this
7358              */
7359         beforedestroy : true,
7360         /**
7361          * @event destroy
7362          * Fires after the component is destroyed.
7363              * @param {Roo.Component} this
7364              */
7365         destroy : true
7366     });
7367     if(!this.id){
7368         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7369     }
7370     Roo.ComponentMgr.register(this);
7371     Roo.Component.superclass.constructor.call(this);
7372     this.initComponent();
7373     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7374         this.render(this.renderTo);
7375         delete this.renderTo;
7376     }
7377 };
7378
7379 // private
7380 Roo.Component.AUTO_ID = 1000;
7381
7382 Roo.extend(Roo.Component, Roo.util.Observable, {
7383     /**
7384      * @property {Boolean} hidden
7385      * true if this component is hidden. Read-only.
7386      */
7387     hidden : false,
7388     /**
7389      * true if this component is disabled. Read-only.
7390      */
7391     disabled : false,
7392     /**
7393      * true if this component has been rendered. Read-only.
7394      */
7395     rendered : false,
7396     
7397     /** @cfg {String} disableClass
7398      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7399      */
7400     disabledClass : "x-item-disabled",
7401         /** @cfg {Boolean} allowDomMove
7402          * Whether the component can move the Dom node when rendering (defaults to true).
7403          */
7404     allowDomMove : true,
7405     /** @cfg {String} hideMode
7406      * How this component should hidden. Supported values are
7407      * "visibility" (css visibility), "offsets" (negative offset position) and
7408      * "display" (css display) - defaults to "display".
7409      */
7410     hideMode: 'display',
7411
7412     // private
7413     ctype : "Roo.Component",
7414
7415     /** @cfg {String} actionMode 
7416      * which property holds the element that used for  hide() / show() / disable() / enable()
7417      * default is 'el' 
7418      */
7419     actionMode : "el",
7420
7421     // private
7422     getActionEl : function(){
7423         return this[this.actionMode];
7424     },
7425
7426     initComponent : Roo.emptyFn,
7427     /**
7428      * If this is a lazy rendering component, render it to its container element.
7429      * @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.
7430      */
7431     render : function(container, position){
7432         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7433             if(!container && this.el){
7434                 this.el = Roo.get(this.el);
7435                 container = this.el.dom.parentNode;
7436                 this.allowDomMove = false;
7437             }
7438             this.container = Roo.get(container);
7439             this.rendered = true;
7440             if(position !== undefined){
7441                 if(typeof position == 'number'){
7442                     position = this.container.dom.childNodes[position];
7443                 }else{
7444                     position = Roo.getDom(position);
7445                 }
7446             }
7447             this.onRender(this.container, position || null);
7448             if(this.cls){
7449                 this.el.addClass(this.cls);
7450                 delete this.cls;
7451             }
7452             if(this.style){
7453                 this.el.applyStyles(this.style);
7454                 delete this.style;
7455             }
7456             this.fireEvent("render", this);
7457             this.afterRender(this.container);
7458             if(this.hidden){
7459                 this.hide();
7460             }
7461             if(this.disabled){
7462                 this.disable();
7463             }
7464         }
7465         return this;
7466     },
7467
7468     // private
7469     // default function is not really useful
7470     onRender : function(ct, position){
7471         if(this.el){
7472             this.el = Roo.get(this.el);
7473             if(this.allowDomMove !== false){
7474                 ct.dom.insertBefore(this.el.dom, position);
7475             }
7476         }
7477     },
7478
7479     // private
7480     getAutoCreate : function(){
7481         var cfg = typeof this.autoCreate == "object" ?
7482                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7483         if(this.id && !cfg.id){
7484             cfg.id = this.id;
7485         }
7486         return cfg;
7487     },
7488
7489     // private
7490     afterRender : Roo.emptyFn,
7491
7492     /**
7493      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7494      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7495      */
7496     destroy : function(){
7497         if(this.fireEvent("beforedestroy", this) !== false){
7498             this.purgeListeners();
7499             this.beforeDestroy();
7500             if(this.rendered){
7501                 this.el.removeAllListeners();
7502                 this.el.remove();
7503                 if(this.actionMode == "container"){
7504                     this.container.remove();
7505                 }
7506             }
7507             this.onDestroy();
7508             Roo.ComponentMgr.unregister(this);
7509             this.fireEvent("destroy", this);
7510         }
7511     },
7512
7513         // private
7514     beforeDestroy : function(){
7515
7516     },
7517
7518         // private
7519         onDestroy : function(){
7520
7521     },
7522
7523     /**
7524      * Returns the underlying {@link Roo.Element}.
7525      * @return {Roo.Element} The element
7526      */
7527     getEl : function(){
7528         return this.el;
7529     },
7530
7531     /**
7532      * Returns the id of this component.
7533      * @return {String}
7534      */
7535     getId : function(){
7536         return this.id;
7537     },
7538
7539     /**
7540      * Try to focus this component.
7541      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7542      * @return {Roo.Component} this
7543      */
7544     focus : function(selectText){
7545         if(this.rendered){
7546             this.el.focus();
7547             if(selectText === true){
7548                 this.el.dom.select();
7549             }
7550         }
7551         return this;
7552     },
7553
7554     // private
7555     blur : function(){
7556         if(this.rendered){
7557             this.el.blur();
7558         }
7559         return this;
7560     },
7561
7562     /**
7563      * Disable this component.
7564      * @return {Roo.Component} this
7565      */
7566     disable : function(){
7567         if(this.rendered){
7568             this.onDisable();
7569         }
7570         this.disabled = true;
7571         this.fireEvent("disable", this);
7572         return this;
7573     },
7574
7575         // private
7576     onDisable : function(){
7577         this.getActionEl().addClass(this.disabledClass);
7578         this.el.dom.disabled = true;
7579     },
7580
7581     /**
7582      * Enable this component.
7583      * @return {Roo.Component} this
7584      */
7585     enable : function(){
7586         if(this.rendered){
7587             this.onEnable();
7588         }
7589         this.disabled = false;
7590         this.fireEvent("enable", this);
7591         return this;
7592     },
7593
7594         // private
7595     onEnable : function(){
7596         this.getActionEl().removeClass(this.disabledClass);
7597         this.el.dom.disabled = false;
7598     },
7599
7600     /**
7601      * Convenience function for setting disabled/enabled by boolean.
7602      * @param {Boolean} disabled
7603      */
7604     setDisabled : function(disabled){
7605         this[disabled ? "disable" : "enable"]();
7606     },
7607
7608     /**
7609      * Show this component.
7610      * @return {Roo.Component} this
7611      */
7612     show: function(){
7613         if(this.fireEvent("beforeshow", this) !== false){
7614             this.hidden = false;
7615             if(this.rendered){
7616                 this.onShow();
7617             }
7618             this.fireEvent("show", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onShow : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "visible";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.removeClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "";
7632         }
7633     },
7634
7635     /**
7636      * Hide this component.
7637      * @return {Roo.Component} this
7638      */
7639     hide: function(){
7640         if(this.fireEvent("beforehide", this) !== false){
7641             this.hidden = true;
7642             if(this.rendered){
7643                 this.onHide();
7644             }
7645             this.fireEvent("hide", this);
7646         }
7647         return this;
7648     },
7649
7650     // private
7651     onHide : function(){
7652         var ae = this.getActionEl();
7653         if(this.hideMode == 'visibility'){
7654             ae.dom.style.visibility = "hidden";
7655         }else if(this.hideMode == 'offsets'){
7656             ae.addClass('x-hidden');
7657         }else{
7658             ae.dom.style.display = "none";
7659         }
7660     },
7661
7662     /**
7663      * Convenience function to hide or show this component by boolean.
7664      * @param {Boolean} visible True to show, false to hide
7665      * @return {Roo.Component} this
7666      */
7667     setVisible: function(visible){
7668         if(visible) {
7669             this.show();
7670         }else{
7671             this.hide();
7672         }
7673         return this;
7674     },
7675
7676     /**
7677      * Returns true if this component is visible.
7678      */
7679     isVisible : function(){
7680         return this.getActionEl().isVisible();
7681     },
7682
7683     cloneConfig : function(overrides){
7684         overrides = overrides || {};
7685         var id = overrides.id || Roo.id();
7686         var cfg = Roo.applyIf(overrides, this.initialConfig);
7687         cfg.id = id; // prevent dup id
7688         return new this.constructor(cfg);
7689     }
7690 });/*
7691  * Based on:
7692  * Ext JS Library 1.1.1
7693  * Copyright(c) 2006-2007, Ext JS, LLC.
7694  *
7695  * Originally Released Under LGPL - original licence link has changed is not relivant.
7696  *
7697  * Fork - LGPL
7698  * <script type="text/javascript">
7699  */
7700  (function(){ 
7701 /**
7702  * @class Roo.Layer
7703  * @extends Roo.Element
7704  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7705  * automatic maintaining of shadow/shim positions.
7706  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7707  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7708  * you can pass a string with a CSS class name. False turns off the shadow.
7709  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7710  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7711  * @cfg {String} cls CSS class to add to the element
7712  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7713  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7714  * @constructor
7715  * @param {Object} config An object with config options.
7716  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7717  */
7718
7719 Roo.Layer = function(config, existingEl){
7720     config = config || {};
7721     var dh = Roo.DomHelper;
7722     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7723     if(existingEl){
7724         this.dom = Roo.getDom(existingEl);
7725     }
7726     if(!this.dom){
7727         var o = config.dh || {tag: "div", cls: "x-layer"};
7728         this.dom = dh.append(pel, o);
7729     }
7730     if(config.cls){
7731         this.addClass(config.cls);
7732     }
7733     this.constrain = config.constrain !== false;
7734     this.visibilityMode = Roo.Element.VISIBILITY;
7735     if(config.id){
7736         this.id = this.dom.id = config.id;
7737     }else{
7738         this.id = Roo.id(this.dom);
7739     }
7740     this.zindex = config.zindex || this.getZIndex();
7741     this.position("absolute", this.zindex);
7742     if(config.shadow){
7743         this.shadowOffset = config.shadowOffset || 4;
7744         this.shadow = new Roo.Shadow({
7745             offset : this.shadowOffset,
7746             mode : config.shadow
7747         });
7748     }else{
7749         this.shadowOffset = 0;
7750     }
7751     this.useShim = config.shim !== false && Roo.useShims;
7752     this.useDisplay = config.useDisplay;
7753     this.hide();
7754 };
7755
7756 var supr = Roo.Element.prototype;
7757
7758 // shims are shared among layer to keep from having 100 iframes
7759 var shims = [];
7760
7761 Roo.extend(Roo.Layer, Roo.Element, {
7762
7763     getZIndex : function(){
7764         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7765     },
7766
7767     getShim : function(){
7768         if(!this.useShim){
7769             return null;
7770         }
7771         if(this.shim){
7772             return this.shim;
7773         }
7774         var shim = shims.shift();
7775         if(!shim){
7776             shim = this.createShim();
7777             shim.enableDisplayMode('block');
7778             shim.dom.style.display = 'none';
7779             shim.dom.style.visibility = 'visible';
7780         }
7781         var pn = this.dom.parentNode;
7782         if(shim.dom.parentNode != pn){
7783             pn.insertBefore(shim.dom, this.dom);
7784         }
7785         shim.setStyle('z-index', this.getZIndex()-2);
7786         this.shim = shim;
7787         return shim;
7788     },
7789
7790     hideShim : function(){
7791         if(this.shim){
7792             this.shim.setDisplayed(false);
7793             shims.push(this.shim);
7794             delete this.shim;
7795         }
7796     },
7797
7798     disableShadow : function(){
7799         if(this.shadow){
7800             this.shadowDisabled = true;
7801             this.shadow.hide();
7802             this.lastShadowOffset = this.shadowOffset;
7803             this.shadowOffset = 0;
7804         }
7805     },
7806
7807     enableShadow : function(show){
7808         if(this.shadow){
7809             this.shadowDisabled = false;
7810             this.shadowOffset = this.lastShadowOffset;
7811             delete this.lastShadowOffset;
7812             if(show){
7813                 this.sync(true);
7814             }
7815         }
7816     },
7817
7818     // private
7819     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7820     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7821     sync : function(doShow){
7822         var sw = this.shadow;
7823         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7824             var sh = this.getShim();
7825
7826             var w = this.getWidth(),
7827                 h = this.getHeight();
7828
7829             var l = this.getLeft(true),
7830                 t = this.getTop(true);
7831
7832             if(sw && !this.shadowDisabled){
7833                 if(doShow && !sw.isVisible()){
7834                     sw.show(this);
7835                 }else{
7836                     sw.realign(l, t, w, h);
7837                 }
7838                 if(sh){
7839                     if(doShow){
7840                        sh.show();
7841                     }
7842                     // fit the shim behind the shadow, so it is shimmed too
7843                     var a = sw.adjusts, s = sh.dom.style;
7844                     s.left = (Math.min(l, l+a.l))+"px";
7845                     s.top = (Math.min(t, t+a.t))+"px";
7846                     s.width = (w+a.w)+"px";
7847                     s.height = (h+a.h)+"px";
7848                 }
7849             }else if(sh){
7850                 if(doShow){
7851                    sh.show();
7852                 }
7853                 sh.setSize(w, h);
7854                 sh.setLeftTop(l, t);
7855             }
7856             
7857         }
7858     },
7859
7860     // private
7861     destroy : function(){
7862         this.hideShim();
7863         if(this.shadow){
7864             this.shadow.hide();
7865         }
7866         this.removeAllListeners();
7867         var pn = this.dom.parentNode;
7868         if(pn){
7869             pn.removeChild(this.dom);
7870         }
7871         Roo.Element.uncache(this.id);
7872     },
7873
7874     remove : function(){
7875         this.destroy();
7876     },
7877
7878     // private
7879     beginUpdate : function(){
7880         this.updating = true;
7881     },
7882
7883     // private
7884     endUpdate : function(){
7885         this.updating = false;
7886         this.sync(true);
7887     },
7888
7889     // private
7890     hideUnders : function(negOffset){
7891         if(this.shadow){
7892             this.shadow.hide();
7893         }
7894         this.hideShim();
7895     },
7896
7897     // private
7898     constrainXY : function(){
7899         if(this.constrain){
7900             var vw = Roo.lib.Dom.getViewWidth(),
7901                 vh = Roo.lib.Dom.getViewHeight();
7902             var s = Roo.get(document).getScroll();
7903
7904             var xy = this.getXY();
7905             var x = xy[0], y = xy[1];   
7906             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7907             // only move it if it needs it
7908             var moved = false;
7909             // first validate right/bottom
7910             if((x + w) > vw+s.left){
7911                 x = vw - w - this.shadowOffset;
7912                 moved = true;
7913             }
7914             if((y + h) > vh+s.top){
7915                 y = vh - h - this.shadowOffset;
7916                 moved = true;
7917             }
7918             // then make sure top/left isn't negative
7919             if(x < s.left){
7920                 x = s.left;
7921                 moved = true;
7922             }
7923             if(y < s.top){
7924                 y = s.top;
7925                 moved = true;
7926             }
7927             if(moved){
7928                 if(this.avoidY){
7929                     var ay = this.avoidY;
7930                     if(y <= ay && (y+h) >= ay){
7931                         y = ay-h-5;   
7932                     }
7933                 }
7934                 xy = [x, y];
7935                 this.storeXY(xy);
7936                 supr.setXY.call(this, xy);
7937                 this.sync();
7938             }
7939         }
7940     },
7941
7942     isVisible : function(){
7943         return this.visible;    
7944     },
7945
7946     // private
7947     showAction : function(){
7948         this.visible = true; // track visibility to prevent getStyle calls
7949         if(this.useDisplay === true){
7950             this.setDisplayed("");
7951         }else if(this.lastXY){
7952             supr.setXY.call(this, this.lastXY);
7953         }else if(this.lastLT){
7954             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7955         }
7956     },
7957
7958     // private
7959     hideAction : function(){
7960         this.visible = false;
7961         if(this.useDisplay === true){
7962             this.setDisplayed(false);
7963         }else{
7964             this.setLeftTop(-10000,-10000);
7965         }
7966     },
7967
7968     // overridden Element method
7969     setVisible : function(v, a, d, c, e){
7970         if(v){
7971             this.showAction();
7972         }
7973         if(a && v){
7974             var cb = function(){
7975                 this.sync(true);
7976                 if(c){
7977                     c();
7978                 }
7979             }.createDelegate(this);
7980             supr.setVisible.call(this, true, true, d, cb, e);
7981         }else{
7982             if(!v){
7983                 this.hideUnders(true);
7984             }
7985             var cb = c;
7986             if(a){
7987                 cb = function(){
7988                     this.hideAction();
7989                     if(c){
7990                         c();
7991                     }
7992                 }.createDelegate(this);
7993             }
7994             supr.setVisible.call(this, v, a, d, cb, e);
7995             if(v){
7996                 this.sync(true);
7997             }else if(!a){
7998                 this.hideAction();
7999             }
8000         }
8001     },
8002
8003     storeXY : function(xy){
8004         delete this.lastLT;
8005         this.lastXY = xy;
8006     },
8007
8008     storeLeftTop : function(left, top){
8009         delete this.lastXY;
8010         this.lastLT = [left, top];
8011     },
8012
8013     // private
8014     beforeFx : function(){
8015         this.beforeAction();
8016         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8017     },
8018
8019     // private
8020     afterFx : function(){
8021         Roo.Layer.superclass.afterFx.apply(this, arguments);
8022         this.sync(this.isVisible());
8023     },
8024
8025     // private
8026     beforeAction : function(){
8027         if(!this.updating && this.shadow){
8028             this.shadow.hide();
8029         }
8030     },
8031
8032     // overridden Element method
8033     setLeft : function(left){
8034         this.storeLeftTop(left, this.getTop(true));
8035         supr.setLeft.apply(this, arguments);
8036         this.sync();
8037     },
8038
8039     setTop : function(top){
8040         this.storeLeftTop(this.getLeft(true), top);
8041         supr.setTop.apply(this, arguments);
8042         this.sync();
8043     },
8044
8045     setLeftTop : function(left, top){
8046         this.storeLeftTop(left, top);
8047         supr.setLeftTop.apply(this, arguments);
8048         this.sync();
8049     },
8050
8051     setXY : function(xy, a, d, c, e){
8052         this.fixDisplay();
8053         this.beforeAction();
8054         this.storeXY(xy);
8055         var cb = this.createCB(c);
8056         supr.setXY.call(this, xy, a, d, cb, e);
8057         if(!a){
8058             cb();
8059         }
8060     },
8061
8062     // private
8063     createCB : function(c){
8064         var el = this;
8065         return function(){
8066             el.constrainXY();
8067             el.sync(true);
8068             if(c){
8069                 c();
8070             }
8071         };
8072     },
8073
8074     // overridden Element method
8075     setX : function(x, a, d, c, e){
8076         this.setXY([x, this.getY()], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setY : function(y, a, d, c, e){
8081         this.setXY([this.getX(), y], a, d, c, e);
8082     },
8083
8084     // overridden Element method
8085     setSize : function(w, h, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setSize.call(this, w, h, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setWidth : function(w, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setWidth.call(this, w, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setHeight : function(h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         supr.setHeight.call(this, h, a, d, cb, e);
8109         if(!a){
8110             cb();
8111         }
8112     },
8113
8114     // overridden Element method
8115     setBounds : function(x, y, w, h, a, d, c, e){
8116         this.beforeAction();
8117         var cb = this.createCB(c);
8118         if(!a){
8119             this.storeXY([x, y]);
8120             supr.setXY.call(this, [x, y]);
8121             supr.setSize.call(this, w, h, a, d, cb, e);
8122             cb();
8123         }else{
8124             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8125         }
8126         return this;
8127     },
8128     
8129     /**
8130      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8131      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8132      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8133      * @param {Number} zindex The new z-index to set
8134      * @return {this} The Layer
8135      */
8136     setZIndex : function(zindex){
8137         this.zindex = zindex;
8138         this.setStyle("z-index", zindex + 2);
8139         if(this.shadow){
8140             this.shadow.setZIndex(zindex + 1);
8141         }
8142         if(this.shim){
8143             this.shim.setStyle("z-index", zindex);
8144         }
8145     }
8146 });
8147 })();/*
8148  * Based on:
8149  * Ext JS Library 1.1.1
8150  * Copyright(c) 2006-2007, Ext JS, LLC.
8151  *
8152  * Originally Released Under LGPL - original licence link has changed is not relivant.
8153  *
8154  * Fork - LGPL
8155  * <script type="text/javascript">
8156  */
8157
8158
8159 /**
8160  * @class Roo.Shadow
8161  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8162  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8163  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8164  * @constructor
8165  * Create a new Shadow
8166  * @param {Object} config The config object
8167  */
8168 Roo.Shadow = function(config){
8169     Roo.apply(this, config);
8170     if(typeof this.mode != "string"){
8171         this.mode = this.defaultMode;
8172     }
8173     var o = this.offset, a = {h: 0};
8174     var rad = Math.floor(this.offset/2);
8175     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8176         case "drop":
8177             a.w = 0;
8178             a.l = a.t = o;
8179             a.t -= 1;
8180             if(Roo.isIE){
8181                 a.l -= this.offset + rad;
8182                 a.t -= this.offset + rad;
8183                 a.w -= rad;
8184                 a.h -= rad;
8185                 a.t += 1;
8186             }
8187         break;
8188         case "sides":
8189             a.w = (o*2);
8190             a.l = -o;
8191             a.t = o-1;
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)*2;
8197                 a.w -= rad + 1;
8198                 a.h -= 1;
8199             }
8200         break;
8201         case "frame":
8202             a.w = a.h = (o*2);
8203             a.l = a.t = -o;
8204             a.t += 1;
8205             a.h -= 2;
8206             if(Roo.isIE){
8207                 a.l -= (this.offset - rad);
8208                 a.t -= (this.offset - rad);
8209                 a.l += 1;
8210                 a.w -= (this.offset + rad + 1);
8211                 a.h -= (this.offset + rad);
8212                 a.h += 1;
8213             }
8214         break;
8215     };
8216
8217     this.adjusts = a;
8218 };
8219
8220 Roo.Shadow.prototype = {
8221     /**
8222      * @cfg {String} mode
8223      * The shadow display mode.  Supports the following options:<br />
8224      * sides: Shadow displays on both sides and bottom only<br />
8225      * frame: Shadow displays equally on all four sides<br />
8226      * drop: Traditional bottom-right drop shadow (default)
8227      */
8228     /**
8229      * @cfg {String} offset
8230      * The number of pixels to offset the shadow from the element (defaults to 4)
8231      */
8232     offset: 4,
8233
8234     // private
8235     defaultMode: "drop",
8236
8237     /**
8238      * Displays the shadow under the target element
8239      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8240      */
8241     show : function(target){
8242         target = Roo.get(target);
8243         if(!this.el){
8244             this.el = Roo.Shadow.Pool.pull();
8245             if(this.el.dom.nextSibling != target.dom){
8246                 this.el.insertBefore(target);
8247             }
8248         }
8249         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8250         if(Roo.isIE){
8251             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8252         }
8253         this.realign(
8254             target.getLeft(true),
8255             target.getTop(true),
8256             target.getWidth(),
8257             target.getHeight()
8258         );
8259         this.el.dom.style.display = "block";
8260     },
8261
8262     /**
8263      * Returns true if the shadow is visible, else false
8264      */
8265     isVisible : function(){
8266         return this.el ? true : false;  
8267     },
8268
8269     /**
8270      * Direct alignment when values are already available. Show must be called at least once before
8271      * calling this method to ensure it is initialized.
8272      * @param {Number} left The target element left position
8273      * @param {Number} top The target element top position
8274      * @param {Number} width The target element width
8275      * @param {Number} height The target element height
8276      */
8277     realign : function(l, t, w, h){
8278         if(!this.el){
8279             return;
8280         }
8281         var a = this.adjusts, d = this.el.dom, s = d.style;
8282         var iea = 0;
8283         s.left = (l+a.l)+"px";
8284         s.top = (t+a.t)+"px";
8285         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8286  
8287         if(s.width != sws || s.height != shs){
8288             s.width = sws;
8289             s.height = shs;
8290             if(!Roo.isIE){
8291                 var cn = d.childNodes;
8292                 var sww = Math.max(0, (sw-12))+"px";
8293                 cn[0].childNodes[1].style.width = sww;
8294                 cn[1].childNodes[1].style.width = sww;
8295                 cn[2].childNodes[1].style.width = sww;
8296                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8297             }
8298         }
8299     },
8300
8301     /**
8302      * Hides this shadow
8303      */
8304     hide : function(){
8305         if(this.el){
8306             this.el.dom.style.display = "none";
8307             Roo.Shadow.Pool.push(this.el);
8308             delete this.el;
8309         }
8310     },
8311
8312     /**
8313      * Adjust the z-index of this shadow
8314      * @param {Number} zindex The new z-index
8315      */
8316     setZIndex : function(z){
8317         this.zIndex = z;
8318         if(this.el){
8319             this.el.setStyle("z-index", z);
8320         }
8321     }
8322 };
8323
8324 // Private utility class that manages the internal Shadow cache
8325 Roo.Shadow.Pool = function(){
8326     var p = [];
8327     var markup = Roo.isIE ?
8328                  '<div class="x-ie-shadow"></div>' :
8329                  '<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>';
8330     return {
8331         pull : function(){
8332             var sh = p.shift();
8333             if(!sh){
8334                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8335                 sh.autoBoxAdjust = false;
8336             }
8337             return sh;
8338         },
8339
8340         push : function(sh){
8341             p.push(sh);
8342         }
8343     };
8344 }();/*
8345  * Based on:
8346  * Ext JS Library 1.1.1
8347  * Copyright(c) 2006-2007, Ext JS, LLC.
8348  *
8349  * Originally Released Under LGPL - original licence link has changed is not relivant.
8350  *
8351  * Fork - LGPL
8352  * <script type="text/javascript">
8353  */
8354
8355 /**
8356  * @class Roo.BoxComponent
8357  * @extends Roo.Component
8358  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8359  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8360  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8361  * layout containers.
8362  * @constructor
8363  * @param {Roo.Element/String/Object} config The configuration options.
8364  */
8365 Roo.BoxComponent = function(config){
8366     Roo.Component.call(this, config);
8367     this.addEvents({
8368         /**
8369          * @event resize
8370          * Fires after the component is resized.
8371              * @param {Roo.Component} this
8372              * @param {Number} adjWidth The box-adjusted width that was set
8373              * @param {Number} adjHeight The box-adjusted height that was set
8374              * @param {Number} rawWidth The width that was originally specified
8375              * @param {Number} rawHeight The height that was originally specified
8376              */
8377         resize : true,
8378         /**
8379          * @event move
8380          * Fires after the component is moved.
8381              * @param {Roo.Component} this
8382              * @param {Number} x The new x position
8383              * @param {Number} y The new y position
8384              */
8385         move : true
8386     });
8387 };
8388
8389 Roo.extend(Roo.BoxComponent, Roo.Component, {
8390     // private, set in afterRender to signify that the component has been rendered
8391     boxReady : false,
8392     // private, used to defer height settings to subclasses
8393     deferHeight: false,
8394     /** @cfg {Number} width
8395      * width (optional) size of component
8396      */
8397      /** @cfg {Number} height
8398      * height (optional) size of component
8399      */
8400      
8401     /**
8402      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8403      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8404      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8405      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8406      * @return {Roo.BoxComponent} this
8407      */
8408     setSize : function(w, h){
8409         // support for standard size objects
8410         if(typeof w == 'object'){
8411             h = w.height;
8412             w = w.width;
8413         }
8414         // not rendered
8415         if(!this.boxReady){
8416             this.width = w;
8417             this.height = h;
8418             return this;
8419         }
8420
8421         // prevent recalcs when not needed
8422         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8423             return this;
8424         }
8425         this.lastSize = {width: w, height: h};
8426
8427         var adj = this.adjustSize(w, h);
8428         var aw = adj.width, ah = adj.height;
8429         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8430             var rz = this.getResizeEl();
8431             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8432                 rz.setSize(aw, ah);
8433             }else if(!this.deferHeight && ah !== undefined){
8434                 rz.setHeight(ah);
8435             }else if(aw !== undefined){
8436                 rz.setWidth(aw);
8437             }
8438             this.onResize(aw, ah, w, h);
8439             this.fireEvent('resize', this, aw, ah, w, h);
8440         }
8441         return this;
8442     },
8443
8444     /**
8445      * Gets the current size of the component's underlying element.
8446      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8447      */
8448     getSize : function(){
8449         return this.el.getSize();
8450     },
8451
8452     /**
8453      * Gets the current XY position of the component's underlying element.
8454      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8455      * @return {Array} The XY position of the element (e.g., [100, 200])
8456      */
8457     getPosition : function(local){
8458         if(local === true){
8459             return [this.el.getLeft(true), this.el.getTop(true)];
8460         }
8461         return this.xy || this.el.getXY();
8462     },
8463
8464     /**
8465      * Gets the current box measurements of the component's underlying element.
8466      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8467      * @returns {Object} box An object in the format {x, y, width, height}
8468      */
8469     getBox : function(local){
8470         var s = this.el.getSize();
8471         if(local){
8472             s.x = this.el.getLeft(true);
8473             s.y = this.el.getTop(true);
8474         }else{
8475             var xy = this.xy || this.el.getXY();
8476             s.x = xy[0];
8477             s.y = xy[1];
8478         }
8479         return s;
8480     },
8481
8482     /**
8483      * Sets the current box measurements of the component's underlying element.
8484      * @param {Object} box An object in the format {x, y, width, height}
8485      * @returns {Roo.BoxComponent} this
8486      */
8487     updateBox : function(box){
8488         this.setSize(box.width, box.height);
8489         this.setPagePosition(box.x, box.y);
8490         return this;
8491     },
8492
8493     // protected
8494     getResizeEl : function(){
8495         return this.resizeEl || this.el;
8496     },
8497
8498     // protected
8499     getPositionEl : function(){
8500         return this.positionEl || this.el;
8501     },
8502
8503     /**
8504      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8505      * This method fires the move event.
8506      * @param {Number} left The new left
8507      * @param {Number} top The new top
8508      * @returns {Roo.BoxComponent} this
8509      */
8510     setPosition : function(x, y){
8511         this.x = x;
8512         this.y = y;
8513         if(!this.boxReady){
8514             return this;
8515         }
8516         var adj = this.adjustPosition(x, y);
8517         var ax = adj.x, ay = adj.y;
8518
8519         var el = this.getPositionEl();
8520         if(ax !== undefined || ay !== undefined){
8521             if(ax !== undefined && ay !== undefined){
8522                 el.setLeftTop(ax, ay);
8523             }else if(ax !== undefined){
8524                 el.setLeft(ax);
8525             }else if(ay !== undefined){
8526                 el.setTop(ay);
8527             }
8528             this.onPosition(ax, ay);
8529             this.fireEvent('move', this, ax, ay);
8530         }
8531         return this;
8532     },
8533
8534     /**
8535      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8536      * This method fires the move event.
8537      * @param {Number} x The new x position
8538      * @param {Number} y The new y position
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPagePosition : function(x, y){
8542         this.pageX = x;
8543         this.pageY = y;
8544         if(!this.boxReady){
8545             return;
8546         }
8547         if(x === undefined || y === undefined){ // cannot translate undefined points
8548             return;
8549         }
8550         var p = this.el.translatePoints(x, y);
8551         this.setPosition(p.left, p.top);
8552         return this;
8553     },
8554
8555     // private
8556     onRender : function(ct, position){
8557         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8558         if(this.resizeEl){
8559             this.resizeEl = Roo.get(this.resizeEl);
8560         }
8561         if(this.positionEl){
8562             this.positionEl = Roo.get(this.positionEl);
8563         }
8564     },
8565
8566     // private
8567     afterRender : function(){
8568         Roo.BoxComponent.superclass.afterRender.call(this);
8569         this.boxReady = true;
8570         this.setSize(this.width, this.height);
8571         if(this.x || this.y){
8572             this.setPosition(this.x, this.y);
8573         }
8574         if(this.pageX || this.pageY){
8575             this.setPagePosition(this.pageX, this.pageY);
8576         }
8577     },
8578
8579     /**
8580      * Force the component's size to recalculate based on the underlying element's current height and width.
8581      * @returns {Roo.BoxComponent} this
8582      */
8583     syncSize : function(){
8584         delete this.lastSize;
8585         this.setSize(this.el.getWidth(), this.el.getHeight());
8586         return this;
8587     },
8588
8589     /**
8590      * Called after the component is resized, this method is empty by default but can be implemented by any
8591      * subclass that needs to perform custom logic after a resize occurs.
8592      * @param {Number} adjWidth The box-adjusted width that was set
8593      * @param {Number} adjHeight The box-adjusted height that was set
8594      * @param {Number} rawWidth The width that was originally specified
8595      * @param {Number} rawHeight The height that was originally specified
8596      */
8597     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8598
8599     },
8600
8601     /**
8602      * Called after the component is moved, this method is empty by default but can be implemented by any
8603      * subclass that needs to perform custom logic after a move occurs.
8604      * @param {Number} x The new x position
8605      * @param {Number} y The new y position
8606      */
8607     onPosition : function(x, y){
8608
8609     },
8610
8611     // private
8612     adjustSize : function(w, h){
8613         if(this.autoWidth){
8614             w = 'auto';
8615         }
8616         if(this.autoHeight){
8617             h = 'auto';
8618         }
8619         return {width : w, height: h};
8620     },
8621
8622     // private
8623     adjustPosition : function(x, y){
8624         return {x : x, y: y};
8625     }
8626 });/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637
8638 /**
8639  * @class Roo.SplitBar
8640  * @extends Roo.util.Observable
8641  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8642  * <br><br>
8643  * Usage:
8644  * <pre><code>
8645 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8646                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8647 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8648 split.minSize = 100;
8649 split.maxSize = 600;
8650 split.animate = true;
8651 split.on('moved', splitterMoved);
8652 </code></pre>
8653  * @constructor
8654  * Create a new SplitBar
8655  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8656  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8657  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8659                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8660                         position of the SplitBar).
8661  */
8662 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8663     
8664     /** @private */
8665     this.el = Roo.get(dragElement, true);
8666     this.el.dom.unselectable = "on";
8667     /** @private */
8668     this.resizingEl = Roo.get(resizingElement, true);
8669
8670     /**
8671      * @private
8672      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8673      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8674      * @type Number
8675      */
8676     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8677     
8678     /**
8679      * The minimum size of the resizing element. (Defaults to 0)
8680      * @type Number
8681      */
8682     this.minSize = 0;
8683     
8684     /**
8685      * The maximum size of the resizing element. (Defaults to 2000)
8686      * @type Number
8687      */
8688     this.maxSize = 2000;
8689     
8690     /**
8691      * Whether to animate the transition to the new size
8692      * @type Boolean
8693      */
8694     this.animate = false;
8695     
8696     /**
8697      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8698      * @type Boolean
8699      */
8700     this.useShim = false;
8701     
8702     /** @private */
8703     this.shim = null;
8704     
8705     if(!existingProxy){
8706         /** @private */
8707         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8708     }else{
8709         this.proxy = Roo.get(existingProxy).dom;
8710     }
8711     /** @private */
8712     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8713     
8714     /** @private */
8715     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8716     
8717     /** @private */
8718     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8719     
8720     /** @private */
8721     this.dragSpecs = {};
8722     
8723     /**
8724      * @private The adapter to use to positon and resize elements
8725      */
8726     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8727     this.adapter.init(this);
8728     
8729     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8730         /** @private */
8731         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8732         this.el.addClass("x-splitbar-h");
8733     }else{
8734         /** @private */
8735         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8736         this.el.addClass("x-splitbar-v");
8737     }
8738     
8739     this.addEvents({
8740         /**
8741          * @event resize
8742          * Fires when the splitter is moved (alias for {@link #event-moved})
8743          * @param {Roo.SplitBar} this
8744          * @param {Number} newSize the new width or height
8745          */
8746         "resize" : true,
8747         /**
8748          * @event moved
8749          * Fires when the splitter is moved
8750          * @param {Roo.SplitBar} this
8751          * @param {Number} newSize the new width or height
8752          */
8753         "moved" : true,
8754         /**
8755          * @event beforeresize
8756          * Fires before the splitter is dragged
8757          * @param {Roo.SplitBar} this
8758          */
8759         "beforeresize" : true,
8760
8761         "beforeapply" : true
8762     });
8763
8764     Roo.util.Observable.call(this);
8765 };
8766
8767 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8768     onStartProxyDrag : function(x, y){
8769         this.fireEvent("beforeresize", this);
8770         if(!this.overlay){
8771             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8772             o.unselectable();
8773             o.enableDisplayMode("block");
8774             // all splitbars share the same overlay
8775             Roo.SplitBar.prototype.overlay = o;
8776         }
8777         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8778         this.overlay.show();
8779         Roo.get(this.proxy).setDisplayed("block");
8780         var size = this.adapter.getElementSize(this);
8781         this.activeMinSize = this.getMinimumSize();;
8782         this.activeMaxSize = this.getMaximumSize();;
8783         var c1 = size - this.activeMinSize;
8784         var c2 = Math.max(this.activeMaxSize - size, 0);
8785         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8786             this.dd.resetConstraints();
8787             this.dd.setXConstraint(
8788                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8789                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8790             );
8791             this.dd.setYConstraint(0, 0);
8792         }else{
8793             this.dd.resetConstraints();
8794             this.dd.setXConstraint(0, 0);
8795             this.dd.setYConstraint(
8796                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8797                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8798             );
8799          }
8800         this.dragSpecs.startSize = size;
8801         this.dragSpecs.startPoint = [x, y];
8802         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8803     },
8804     
8805     /** 
8806      * @private Called after the drag operation by the DDProxy
8807      */
8808     onEndProxyDrag : function(e){
8809         Roo.get(this.proxy).setDisplayed(false);
8810         var endPoint = Roo.lib.Event.getXY(e);
8811         if(this.overlay){
8812             this.overlay.hide();
8813         }
8814         var newSize;
8815         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8816             newSize = this.dragSpecs.startSize + 
8817                 (this.placement == Roo.SplitBar.LEFT ?
8818                     endPoint[0] - this.dragSpecs.startPoint[0] :
8819                     this.dragSpecs.startPoint[0] - endPoint[0]
8820                 );
8821         }else{
8822             newSize = this.dragSpecs.startSize + 
8823                 (this.placement == Roo.SplitBar.TOP ?
8824                     endPoint[1] - this.dragSpecs.startPoint[1] :
8825                     this.dragSpecs.startPoint[1] - endPoint[1]
8826                 );
8827         }
8828         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8829         if(newSize != this.dragSpecs.startSize){
8830             if(this.fireEvent('beforeapply', this, newSize) !== false){
8831                 this.adapter.setElementSize(this, newSize);
8832                 this.fireEvent("moved", this, newSize);
8833                 this.fireEvent("resize", this, newSize);
8834             }
8835         }
8836     },
8837     
8838     /**
8839      * Get the adapter this SplitBar uses
8840      * @return The adapter object
8841      */
8842     getAdapter : function(){
8843         return this.adapter;
8844     },
8845     
8846     /**
8847      * Set the adapter this SplitBar uses
8848      * @param {Object} adapter A SplitBar adapter object
8849      */
8850     setAdapter : function(adapter){
8851         this.adapter = adapter;
8852         this.adapter.init(this);
8853     },
8854     
8855     /**
8856      * Gets the minimum size for the resizing element
8857      * @return {Number} The minimum size
8858      */
8859     getMinimumSize : function(){
8860         return this.minSize;
8861     },
8862     
8863     /**
8864      * Sets the minimum size for the resizing element
8865      * @param {Number} minSize The minimum size
8866      */
8867     setMinimumSize : function(minSize){
8868         this.minSize = minSize;
8869     },
8870     
8871     /**
8872      * Gets the maximum size for the resizing element
8873      * @return {Number} The maximum size
8874      */
8875     getMaximumSize : function(){
8876         return this.maxSize;
8877     },
8878     
8879     /**
8880      * Sets the maximum size for the resizing element
8881      * @param {Number} maxSize The maximum size
8882      */
8883     setMaximumSize : function(maxSize){
8884         this.maxSize = maxSize;
8885     },
8886     
8887     /**
8888      * Sets the initialize size for the resizing element
8889      * @param {Number} size The initial size
8890      */
8891     setCurrentSize : function(size){
8892         var oldAnimate = this.animate;
8893         this.animate = false;
8894         this.adapter.setElementSize(this, size);
8895         this.animate = oldAnimate;
8896     },
8897     
8898     /**
8899      * Destroy this splitbar. 
8900      * @param {Boolean} removeEl True to remove the element
8901      */
8902     destroy : function(removeEl){
8903         if(this.shim){
8904             this.shim.remove();
8905         }
8906         this.dd.unreg();
8907         this.proxy.parentNode.removeChild(this.proxy);
8908         if(removeEl){
8909             this.el.remove();
8910         }
8911     }
8912 });
8913
8914 /**
8915  * @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.
8916  */
8917 Roo.SplitBar.createProxy = function(dir){
8918     var proxy = new Roo.Element(document.createElement("div"));
8919     proxy.unselectable();
8920     var cls = 'x-splitbar-proxy';
8921     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8922     document.body.appendChild(proxy.dom);
8923     return proxy.dom;
8924 };
8925
8926 /** 
8927  * @class Roo.SplitBar.BasicLayoutAdapter
8928  * Default Adapter. It assumes the splitter and resizing element are not positioned
8929  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8930  */
8931 Roo.SplitBar.BasicLayoutAdapter = function(){
8932 };
8933
8934 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8935     // do nothing for now
8936     init : function(s){
8937     
8938     },
8939     /**
8940      * Called before drag operations to get the current size of the resizing element. 
8941      * @param {Roo.SplitBar} s The SplitBar using this adapter
8942      */
8943      getElementSize : function(s){
8944         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8945             return s.resizingEl.getWidth();
8946         }else{
8947             return s.resizingEl.getHeight();
8948         }
8949     },
8950     
8951     /**
8952      * Called after drag operations to set the size of the resizing element.
8953      * @param {Roo.SplitBar} s The SplitBar using this adapter
8954      * @param {Number} newSize The new size to set
8955      * @param {Function} onComplete A function to be invoked when resizing is complete
8956      */
8957     setElementSize : function(s, newSize, onComplete){
8958         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8959             if(!s.animate){
8960                 s.resizingEl.setWidth(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }else{
8968             
8969             if(!s.animate){
8970                 s.resizingEl.setHeight(newSize);
8971                 if(onComplete){
8972                     onComplete(s, newSize);
8973                 }
8974             }else{
8975                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8976             }
8977         }
8978     }
8979 };
8980
8981 /** 
8982  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8983  * @extends Roo.SplitBar.BasicLayoutAdapter
8984  * Adapter that  moves the splitter element to align with the resized sizing element. 
8985  * Used with an absolute positioned SplitBar.
8986  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8987  * document.body, make sure you assign an id to the body element.
8988  */
8989 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8990     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8991     this.container = Roo.get(container);
8992 };
8993
8994 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8995     init : function(s){
8996         this.basic.init(s);
8997     },
8998     
8999     getElementSize : function(s){
9000         return this.basic.getElementSize(s);
9001     },
9002     
9003     setElementSize : function(s, newSize, onComplete){
9004         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9005     },
9006     
9007     moveSplitter : function(s){
9008         var yes = Roo.SplitBar;
9009         switch(s.placement){
9010             case yes.LEFT:
9011                 s.el.setX(s.resizingEl.getRight());
9012                 break;
9013             case yes.RIGHT:
9014                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9015                 break;
9016             case yes.TOP:
9017                 s.el.setY(s.resizingEl.getBottom());
9018                 break;
9019             case yes.BOTTOM:
9020                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9021                 break;
9022         }
9023     }
9024 };
9025
9026 /**
9027  * Orientation constant - Create a vertical SplitBar
9028  * @static
9029  * @type Number
9030  */
9031 Roo.SplitBar.VERTICAL = 1;
9032
9033 /**
9034  * Orientation constant - Create a horizontal SplitBar
9035  * @static
9036  * @type Number
9037  */
9038 Roo.SplitBar.HORIZONTAL = 2;
9039
9040 /**
9041  * Placement constant - The resizing element is to the left of the splitter element
9042  * @static
9043  * @type Number
9044  */
9045 Roo.SplitBar.LEFT = 1;
9046
9047 /**
9048  * Placement constant - The resizing element is to the right of the splitter element
9049  * @static
9050  * @type Number
9051  */
9052 Roo.SplitBar.RIGHT = 2;
9053
9054 /**
9055  * Placement constant - The resizing element is positioned above the splitter element
9056  * @static
9057  * @type Number
9058  */
9059 Roo.SplitBar.TOP = 3;
9060
9061 /**
9062  * Placement constant - The resizing element is positioned under splitter element
9063  * @static
9064  * @type Number
9065  */
9066 Roo.SplitBar.BOTTOM = 4;
9067 /*
9068  * Based on:
9069  * Ext JS Library 1.1.1
9070  * Copyright(c) 2006-2007, Ext JS, LLC.
9071  *
9072  * Originally Released Under LGPL - original licence link has changed is not relivant.
9073  *
9074  * Fork - LGPL
9075  * <script type="text/javascript">
9076  */
9077
9078 /**
9079  * @class Roo.View
9080  * @extends Roo.util.Observable
9081  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9082  * This class also supports single and multi selection modes. <br>
9083  * Create a data model bound view:
9084  <pre><code>
9085  var store = new Roo.data.Store(...);
9086
9087  var view = new Roo.View({
9088     el : "my-element",
9089     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9090  
9091     singleSelect: true,
9092     selectedClass: "ydataview-selected",
9093     store: store
9094  });
9095
9096  // listen for node click?
9097  view.on("click", function(vw, index, node, e){
9098  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9099  });
9100
9101  // load XML data
9102  dataModel.load("foobar.xml");
9103  </code></pre>
9104  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9105  * <br><br>
9106  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9107  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9108  * 
9109  * Note: old style constructor is still suported (container, template, config)
9110  * 
9111  * @constructor
9112  * Create a new View
9113  * @param {Object} config The config object
9114  * 
9115  */
9116 Roo.View = function(config, depreciated_tpl, depreciated_config){
9117     
9118     if (typeof(depreciated_tpl) == 'undefined') {
9119         // new way.. - universal constructor.
9120         Roo.apply(this, config);
9121         this.el  = Roo.get(this.el);
9122     } else {
9123         // old format..
9124         this.el  = Roo.get(config);
9125         this.tpl = depreciated_tpl;
9126         Roo.apply(this, depreciated_config);
9127     }
9128      
9129     
9130     if(typeof(this.tpl) == "string"){
9131         this.tpl = new Roo.Template(this.tpl);
9132     } else {
9133         // support xtype ctors..
9134         this.tpl = new Roo.factory(this.tpl, Roo);
9135     }
9136     
9137     
9138     this.tpl.compile();
9139    
9140
9141      
9142     /** @private */
9143     this.addEvents({
9144     /**
9145      * @event beforeclick
9146      * Fires before a click is processed. Returns false to cancel the default action.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "beforeclick" : true,
9153     /**
9154      * @event click
9155      * Fires when a template node is clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "click" : true,
9162     /**
9163      * @event dblclick
9164      * Fires when a template node is double clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "dblclick" : true,
9171     /**
9172      * @event contextmenu
9173      * Fires when a template node is right clicked.
9174      * @param {Roo.View} this
9175      * @param {Number} index The index of the target node
9176      * @param {HTMLElement} node The target node
9177      * @param {Roo.EventObject} e The raw event object
9178      */
9179         "contextmenu" : true,
9180     /**
9181      * @event selectionchange
9182      * Fires when the selected nodes change.
9183      * @param {Roo.View} this
9184      * @param {Array} selections Array of the selected nodes
9185      */
9186         "selectionchange" : true,
9187
9188     /**
9189      * @event beforeselect
9190      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9191      * @param {Roo.View} this
9192      * @param {HTMLElement} node The node to be selected
9193      * @param {Array} selections Array of currently selected nodes
9194      */
9195         "beforeselect" : true
9196     });
9197
9198     this.el.on({
9199         "click": this.onClick,
9200         "dblclick": this.onDblClick,
9201         "contextmenu": this.onContextMenu,
9202         scope:this
9203     });
9204
9205     this.selections = [];
9206     this.nodes = [];
9207     this.cmp = new Roo.CompositeElementLite([]);
9208     if(this.store){
9209         this.store = Roo.factory(this.store, Roo.data);
9210         this.setStore(this.store, true);
9211     }
9212     Roo.View.superclass.constructor.call(this);
9213 };
9214
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9216     
9217      /**
9218      * @cfg {Roo.data.Store} store Data store to load data from.
9219      */
9220     store : false,
9221     
9222     /**
9223      * @cfg {String|Roo.Element} el The container element.
9224      */
9225     el : '',
9226     
9227     /**
9228      * @cfg {String|Roo.Template} tpl The template used by this View 
9229      */
9230     tpl : false,
9231     
9232     /**
9233      * @cfg {String} selectedClass The css class to add to selected nodes
9234      */
9235     selectedClass : "x-view-selected",
9236      /**
9237      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9238      */
9239     emptyText : "",
9240     /**
9241      * @cfg {Boolean} multiSelect Allow multiple selection
9242      */
9243     
9244     multiSelect : false,
9245     /**
9246      * @cfg {Boolean} singleSelect Allow single selection
9247      */
9248     singleSelect:  false,
9249     
9250     /**
9251      * Returns the element this view is bound to.
9252      * @return {Roo.Element}
9253      */
9254     getEl : function(){
9255         return this.el;
9256     },
9257
9258     /**
9259      * Refreshes the view.
9260      */
9261     refresh : function(){
9262         var t = this.tpl;
9263         this.clearSelections();
9264         this.el.update("");
9265         var html = [];
9266         var records = this.store.getRange();
9267         if(records.length < 1){
9268             this.el.update(this.emptyText);
9269             return;
9270         }
9271         for(var i = 0, len = records.length; i < len; i++){
9272             var data = this.prepareData(records[i].data, i, records[i]);
9273             html[html.length] = t.apply(data);
9274         }
9275         this.el.update(html.join(""));
9276         this.nodes = this.el.dom.childNodes;
9277         this.updateIndexes(0);
9278     },
9279
9280     /**
9281      * Function to override to reformat the data that is sent to
9282      * the template for each node.
9283      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9284      * a JSON object for an UpdateManager bound view).
9285      */
9286     prepareData : function(data){
9287         return data;
9288     },
9289
9290     onUpdate : function(ds, record){
9291         this.clearSelections();
9292         var index = this.store.indexOf(record);
9293         var n = this.nodes[index];
9294         this.tpl.insertBefore(n, this.prepareData(record.data));
9295         n.parentNode.removeChild(n);
9296         this.updateIndexes(index, index);
9297     },
9298
9299     onAdd : function(ds, records, index){
9300         this.clearSelections();
9301         if(this.nodes.length == 0){
9302             this.refresh();
9303             return;
9304         }
9305         var n = this.nodes[index];
9306         for(var i = 0, len = records.length; i < len; i++){
9307             var d = this.prepareData(records[i].data);
9308             if(n){
9309                 this.tpl.insertBefore(n, d);
9310             }else{
9311                 this.tpl.append(this.el, d);
9312             }
9313         }
9314         this.updateIndexes(index);
9315     },
9316
9317     onRemove : function(ds, record, index){
9318         this.clearSelections();
9319         this.el.dom.removeChild(this.nodes[index]);
9320         this.updateIndexes(index);
9321     },
9322
9323     /**
9324      * Refresh an individual node.
9325      * @param {Number} index
9326      */
9327     refreshNode : function(index){
9328         this.onUpdate(this.store, this.store.getAt(index));
9329     },
9330
9331     updateIndexes : function(startIndex, endIndex){
9332         var ns = this.nodes;
9333         startIndex = startIndex || 0;
9334         endIndex = endIndex || ns.length - 1;
9335         for(var i = startIndex; i <= endIndex; i++){
9336             ns[i].nodeIndex = i;
9337         }
9338     },
9339
9340     /**
9341      * Changes the data store this view uses and refresh the view.
9342      * @param {Store} store
9343      */
9344     setStore : function(store, initial){
9345         if(!initial && this.store){
9346             this.store.un("datachanged", this.refresh);
9347             this.store.un("add", this.onAdd);
9348             this.store.un("remove", this.onRemove);
9349             this.store.un("update", this.onUpdate);
9350             this.store.un("clear", this.refresh);
9351         }
9352         if(store){
9353           
9354             store.on("datachanged", this.refresh, this);
9355             store.on("add", this.onAdd, this);
9356             store.on("remove", this.onRemove, this);
9357             store.on("update", this.onUpdate, this);
9358             store.on("clear", this.refresh, this);
9359         }
9360         
9361         if(store){
9362             this.refresh();
9363         }
9364     },
9365
9366     /**
9367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9368      * @param {HTMLElement} node
9369      * @return {HTMLElement} The template node
9370      */
9371     findItemFromChild : function(node){
9372         var el = this.el.dom;
9373         if(!node || node.parentNode == el){
9374                     return node;
9375             }
9376             var p = node.parentNode;
9377             while(p && p != el){
9378             if(p.parentNode == el){
9379                 return p;
9380             }
9381             p = p.parentNode;
9382         }
9383             return null;
9384     },
9385
9386     /** @ignore */
9387     onClick : function(e){
9388         var item = this.findItemFromChild(e.getTarget());
9389         if(item){
9390             var index = this.indexOf(item);
9391             if(this.onItemClick(item, index, e) !== false){
9392                 this.fireEvent("click", this, index, item, e);
9393             }
9394         }else{
9395             this.clearSelections();
9396         }
9397     },
9398
9399     /** @ignore */
9400     onContextMenu : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9404         }
9405     },
9406
9407     /** @ignore */
9408     onDblClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9412         }
9413     },
9414
9415     onItemClick : function(item, index, e){
9416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9417             return false;
9418         }
9419         if(this.multiSelect || this.singleSelect){
9420             if(this.multiSelect && e.shiftKey && this.lastSelection){
9421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9422             }else{
9423                 this.select(item, this.multiSelect && e.ctrlKey);
9424                 this.lastSelection = item;
9425             }
9426             e.preventDefault();
9427         }
9428         return true;
9429     },
9430
9431     /**
9432      * Get the number of selected nodes.
9433      * @return {Number}
9434      */
9435     getSelectionCount : function(){
9436         return this.selections.length;
9437     },
9438
9439     /**
9440      * Get the currently selected nodes.
9441      * @return {Array} An array of HTMLElements
9442      */
9443     getSelectedNodes : function(){
9444         return this.selections;
9445     },
9446
9447     /**
9448      * Get the indexes of the selected nodes.
9449      * @return {Array}
9450      */
9451     getSelectedIndexes : function(){
9452         var indexes = [], s = this.selections;
9453         for(var i = 0, len = s.length; i < len; i++){
9454             indexes.push(s[i].nodeIndex);
9455         }
9456         return indexes;
9457     },
9458
9459     /**
9460      * Clear all selections
9461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9462      */
9463     clearSelections : function(suppressEvent){
9464         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9465             this.cmp.elements = this.selections;
9466             this.cmp.removeClass(this.selectedClass);
9467             this.selections = [];
9468             if(!suppressEvent){
9469                 this.fireEvent("selectionchange", this, this.selections);
9470             }
9471         }
9472     },
9473
9474     /**
9475      * Returns true if the passed node is selected
9476      * @param {HTMLElement/Number} node The node or node index
9477      * @return {Boolean}
9478      */
9479     isSelected : function(node){
9480         var s = this.selections;
9481         if(s.length < 1){
9482             return false;
9483         }
9484         node = this.getNode(node);
9485         return s.indexOf(node) !== -1;
9486     },
9487
9488     /**
9489      * Selects nodes.
9490      * @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
9491      * @param {Boolean} keepExisting (optional) true to keep existing selections
9492      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9493      */
9494     select : function(nodeInfo, keepExisting, suppressEvent){
9495         if(nodeInfo instanceof Array){
9496             if(!keepExisting){
9497                 this.clearSelections(true);
9498             }
9499             for(var i = 0, len = nodeInfo.length; i < len; i++){
9500                 this.select(nodeInfo[i], true, true);
9501             }
9502         } else{
9503             var node = this.getNode(nodeInfo);
9504             if(node && !this.isSelected(node)){
9505                 if(!keepExisting){
9506                     this.clearSelections(true);
9507                 }
9508                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9509                     Roo.fly(node).addClass(this.selectedClass);
9510                     this.selections.push(node);
9511                     if(!suppressEvent){
9512                         this.fireEvent("selectionchange", this, this.selections);
9513                     }
9514                 }
9515             }
9516         }
9517     },
9518
9519     /**
9520      * Gets a template node.
9521      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9522      * @return {HTMLElement} The node or null if it wasn't found
9523      */
9524     getNode : function(nodeInfo){
9525         if(typeof nodeInfo == "string"){
9526             return document.getElementById(nodeInfo);
9527         }else if(typeof nodeInfo == "number"){
9528             return this.nodes[nodeInfo];
9529         }
9530         return nodeInfo;
9531     },
9532
9533     /**
9534      * Gets a range template nodes.
9535      * @param {Number} startIndex
9536      * @param {Number} endIndex
9537      * @return {Array} An array of nodes
9538      */
9539     getNodes : function(start, end){
9540         var ns = this.nodes;
9541         start = start || 0;
9542         end = typeof end == "undefined" ? ns.length - 1 : end;
9543         var nodes = [];
9544         if(start <= end){
9545             for(var i = start; i <= end; i++){
9546                 nodes.push(ns[i]);
9547             }
9548         } else{
9549             for(var i = start; i >= end; i--){
9550                 nodes.push(ns[i]);
9551             }
9552         }
9553         return nodes;
9554     },
9555
9556     /**
9557      * Finds the index of the passed node
9558      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9559      * @return {Number} The index of the node or -1
9560      */
9561     indexOf : function(node){
9562         node = this.getNode(node);
9563         if(typeof node.nodeIndex == "number"){
9564             return node.nodeIndex;
9565         }
9566         var ns = this.nodes;
9567         for(var i = 0, len = ns.length; i < len; i++){
9568             if(ns[i] == node){
9569                 return i;
9570             }
9571         }
9572         return -1;
9573     }
9574 });
9575 /*
9576  * Based on:
9577  * Ext JS Library 1.1.1
9578  * Copyright(c) 2006-2007, Ext JS, LLC.
9579  *
9580  * Originally Released Under LGPL - original licence link has changed is not relivant.
9581  *
9582  * Fork - LGPL
9583  * <script type="text/javascript">
9584  */
9585
9586 /**
9587  * @class Roo.JsonView
9588  * @extends Roo.View
9589  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9590 <pre><code>
9591 var view = new Roo.JsonView({
9592     container: "my-element",
9593     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9594     multiSelect: true, 
9595     jsonRoot: "data" 
9596 });
9597
9598 // listen for node click?
9599 view.on("click", function(vw, index, node, e){
9600     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9601 });
9602
9603 // direct load of JSON data
9604 view.load("foobar.php");
9605
9606 // Example from my blog list
9607 var tpl = new Roo.Template(
9608     '&lt;div class="entry"&gt;' +
9609     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9610     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9611     "&lt;/div&gt;&lt;hr /&gt;"
9612 );
9613
9614 var moreView = new Roo.JsonView({
9615     container :  "entry-list", 
9616     template : tpl,
9617     jsonRoot: "posts"
9618 });
9619 moreView.on("beforerender", this.sortEntries, this);
9620 moreView.load({
9621     url: "/blog/get-posts.php",
9622     params: "allposts=true",
9623     text: "Loading Blog Entries..."
9624 });
9625 </code></pre>
9626
9627 * Note: old code is supported with arguments : (container, template, config)
9628
9629
9630  * @constructor
9631  * Create a new JsonView
9632  * 
9633  * @param {Object} config The config object
9634  * 
9635  */
9636 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9637     
9638     
9639     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9640
9641     var um = this.el.getUpdateManager();
9642     um.setRenderer(this);
9643     um.on("update", this.onLoad, this);
9644     um.on("failure", this.onLoadException, this);
9645
9646     /**
9647      * @event beforerender
9648      * Fires before rendering of the downloaded JSON data.
9649      * @param {Roo.JsonView} this
9650      * @param {Object} data The JSON data loaded
9651      */
9652     /**
9653      * @event load
9654      * Fires when data is loaded.
9655      * @param {Roo.JsonView} this
9656      * @param {Object} data The JSON data loaded
9657      * @param {Object} response The raw Connect response object
9658      */
9659     /**
9660      * @event loadexception
9661      * Fires when loading fails.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} response The raw Connect response object
9664      */
9665     this.addEvents({
9666         'beforerender' : true,
9667         'load' : true,
9668         'loadexception' : true
9669     });
9670 };
9671 Roo.extend(Roo.JsonView, Roo.View, {
9672     /**
9673      * @type {String} The root property in the loaded JSON object that contains the data
9674      */
9675     jsonRoot : "",
9676
9677     /**
9678      * Refreshes the view.
9679      */
9680     refresh : function(){
9681         this.clearSelections();
9682         this.el.update("");
9683         var html = [];
9684         var o = this.jsonData;
9685         if(o && o.length > 0){
9686             for(var i = 0, len = o.length; i < len; i++){
9687                 var data = this.prepareData(o[i], i, o);
9688                 html[html.length] = this.tpl.apply(data);
9689             }
9690         }else{
9691             html.push(this.emptyText);
9692         }
9693         this.el.update(html.join(""));
9694         this.nodes = this.el.dom.childNodes;
9695         this.updateIndexes(0);
9696     },
9697
9698     /**
9699      * 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.
9700      * @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:
9701      <pre><code>
9702      view.load({
9703          url: "your-url.php",
9704          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9705          callback: yourFunction,
9706          scope: yourObject, //(optional scope)
9707          discardUrl: false,
9708          nocache: false,
9709          text: "Loading...",
9710          timeout: 30,
9711          scripts: false
9712      });
9713      </code></pre>
9714      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9715      * 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.
9716      * @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}
9717      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9718      * @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.
9719      */
9720     load : function(){
9721         var um = this.el.getUpdateManager();
9722         um.update.apply(um, arguments);
9723     },
9724
9725     render : function(el, response){
9726         this.clearSelections();
9727         this.el.update("");
9728         var o;
9729         try{
9730             o = Roo.util.JSON.decode(response.responseText);
9731             if(this.jsonRoot){
9732                 
9733                 o = o[this.jsonRoot];
9734             }
9735         } catch(e){
9736         }
9737         /**
9738          * The current JSON data or null
9739          */
9740         this.jsonData = o;
9741         this.beforeRender();
9742         this.refresh();
9743     },
9744
9745 /**
9746  * Get the number of records in the current JSON dataset
9747  * @return {Number}
9748  */
9749     getCount : function(){
9750         return this.jsonData ? this.jsonData.length : 0;
9751     },
9752
9753 /**
9754  * Returns the JSON object for the specified node(s)
9755  * @param {HTMLElement/Array} node The node or an array of nodes
9756  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9757  * you get the JSON object for the node
9758  */
9759     getNodeData : function(node){
9760         if(node instanceof Array){
9761             var data = [];
9762             for(var i = 0, len = node.length; i < len; i++){
9763                 data.push(this.getNodeData(node[i]));
9764             }
9765             return data;
9766         }
9767         return this.jsonData[this.indexOf(node)] || null;
9768     },
9769
9770     beforeRender : function(){
9771         this.snapshot = this.jsonData;
9772         if(this.sortInfo){
9773             this.sort.apply(this, this.sortInfo);
9774         }
9775         this.fireEvent("beforerender", this, this.jsonData);
9776     },
9777
9778     onLoad : function(el, o){
9779         this.fireEvent("load", this, this.jsonData, o);
9780     },
9781
9782     onLoadException : function(el, o){
9783         this.fireEvent("loadexception", this, o);
9784     },
9785
9786 /**
9787  * Filter the data by a specific property.
9788  * @param {String} property A property on your JSON objects
9789  * @param {String/RegExp} value Either string that the property values
9790  * should start with, or a RegExp to test against the property
9791  */
9792     filter : function(property, value){
9793         if(this.jsonData){
9794             var data = [];
9795             var ss = this.snapshot;
9796             if(typeof value == "string"){
9797                 var vlen = value.length;
9798                 if(vlen == 0){
9799                     this.clearFilter();
9800                     return;
9801                 }
9802                 value = value.toLowerCase();
9803                 for(var i = 0, len = ss.length; i < len; i++){
9804                     var o = ss[i];
9805                     if(o[property].substr(0, vlen).toLowerCase() == value){
9806                         data.push(o);
9807                     }
9808                 }
9809             } else if(value.exec){ // regex?
9810                 for(var i = 0, len = ss.length; i < len; i++){
9811                     var o = ss[i];
9812                     if(value.test(o[property])){
9813                         data.push(o);
9814                     }
9815                 }
9816             } else{
9817                 return;
9818             }
9819             this.jsonData = data;
9820             this.refresh();
9821         }
9822     },
9823
9824 /**
9825  * Filter by a function. The passed function will be called with each
9826  * object in the current dataset. If the function returns true the value is kept,
9827  * otherwise it is filtered.
9828  * @param {Function} fn
9829  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9830  */
9831     filterBy : function(fn, scope){
9832         if(this.jsonData){
9833             var data = [];
9834             var ss = this.snapshot;
9835             for(var i = 0, len = ss.length; i < len; i++){
9836                 var o = ss[i];
9837                 if(fn.call(scope || this, o)){
9838                     data.push(o);
9839                 }
9840             }
9841             this.jsonData = data;
9842             this.refresh();
9843         }
9844     },
9845
9846 /**
9847  * Clears the current filter.
9848  */
9849     clearFilter : function(){
9850         if(this.snapshot && this.jsonData != this.snapshot){
9851             this.jsonData = this.snapshot;
9852             this.refresh();
9853         }
9854     },
9855
9856
9857 /**
9858  * Sorts the data for this view and refreshes it.
9859  * @param {String} property A property on your JSON objects to sort on
9860  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9861  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9862  */
9863     sort : function(property, dir, sortType){
9864         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9865         if(this.jsonData){
9866             var p = property;
9867             var dsc = dir && dir.toLowerCase() == "desc";
9868             var f = function(o1, o2){
9869                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9870                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9871                 ;
9872                 if(v1 < v2){
9873                     return dsc ? +1 : -1;
9874                 } else if(v1 > v2){
9875                     return dsc ? -1 : +1;
9876                 } else{
9877                     return 0;
9878                 }
9879             };
9880             this.jsonData.sort(f);
9881             this.refresh();
9882             if(this.jsonData != this.snapshot){
9883                 this.snapshot.sort(f);
9884             }
9885         }
9886     }
9887 });/*
9888  * Based on:
9889  * Ext JS Library 1.1.1
9890  * Copyright(c) 2006-2007, Ext JS, LLC.
9891  *
9892  * Originally Released Under LGPL - original licence link has changed is not relivant.
9893  *
9894  * Fork - LGPL
9895  * <script type="text/javascript">
9896  */
9897  
9898
9899 /**
9900  * @class Roo.ColorPalette
9901  * @extends Roo.Component
9902  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9903  * Here's an example of typical usage:
9904  * <pre><code>
9905 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9906 cp.render('my-div');
9907
9908 cp.on('select', function(palette, selColor){
9909     // do something with selColor
9910 });
9911 </code></pre>
9912  * @constructor
9913  * Create a new ColorPalette
9914  * @param {Object} config The config object
9915  */
9916 Roo.ColorPalette = function(config){
9917     Roo.ColorPalette.superclass.constructor.call(this, config);
9918     this.addEvents({
9919         /**
9920              * @event select
9921              * Fires when a color is selected
9922              * @param {ColorPalette} this
9923              * @param {String} color The 6-digit color hex code (without the # symbol)
9924              */
9925         select: true
9926     });
9927
9928     if(this.handler){
9929         this.on("select", this.handler, this.scope, true);
9930     }
9931 };
9932 Roo.extend(Roo.ColorPalette, Roo.Component, {
9933     /**
9934      * @cfg {String} itemCls
9935      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9936      */
9937     itemCls : "x-color-palette",
9938     /**
9939      * @cfg {String} value
9940      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9941      * the hex codes are case-sensitive.
9942      */
9943     value : null,
9944     clickEvent:'click',
9945     // private
9946     ctype: "Roo.ColorPalette",
9947
9948     /**
9949      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9950      */
9951     allowReselect : false,
9952
9953     /**
9954      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9955      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9956      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9957      * of colors with the width setting until the box is symmetrical.</p>
9958      * <p>You can override individual colors if needed:</p>
9959      * <pre><code>
9960 var cp = new Roo.ColorPalette();
9961 cp.colors[0] = "FF0000";  // change the first box to red
9962 </code></pre>
9963
9964 Or you can provide a custom array of your own for complete control:
9965 <pre><code>
9966 var cp = new Roo.ColorPalette();
9967 cp.colors = ["000000", "993300", "333300"];
9968 </code></pre>
9969      * @type Array
9970      */
9971     colors : [
9972         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9973         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9974         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9975         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9976         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9977     ],
9978
9979     // private
9980     onRender : function(container, position){
9981         var t = new Roo.MasterTemplate(
9982             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9983         );
9984         var c = this.colors;
9985         for(var i = 0, len = c.length; i < len; i++){
9986             t.add([c[i]]);
9987         }
9988         var el = document.createElement("div");
9989         el.className = this.itemCls;
9990         t.overwrite(el);
9991         container.dom.insertBefore(el, position);
9992         this.el = Roo.get(el);
9993         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9994         if(this.clickEvent != 'click'){
9995             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9996         }
9997     },
9998
9999     // private
10000     afterRender : function(){
10001         Roo.ColorPalette.superclass.afterRender.call(this);
10002         if(this.value){
10003             var s = this.value;
10004             this.value = null;
10005             this.select(s);
10006         }
10007     },
10008
10009     // private
10010     handleClick : function(e, t){
10011         e.preventDefault();
10012         if(!this.disabled){
10013             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10014             this.select(c.toUpperCase());
10015         }
10016     },
10017
10018     /**
10019      * Selects the specified color in the palette (fires the select event)
10020      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10021      */
10022     select : function(color){
10023         color = color.replace("#", "");
10024         if(color != this.value || this.allowReselect){
10025             var el = this.el;
10026             if(this.value){
10027                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10028             }
10029             el.child("a.color-"+color).addClass("x-color-palette-sel");
10030             this.value = color;
10031             this.fireEvent("select", this, color);
10032         }
10033     }
10034 });/*
10035  * Based on:
10036  * Ext JS Library 1.1.1
10037  * Copyright(c) 2006-2007, Ext JS, LLC.
10038  *
10039  * Originally Released Under LGPL - original licence link has changed is not relivant.
10040  *
10041  * Fork - LGPL
10042  * <script type="text/javascript">
10043  */
10044  
10045 /**
10046  * @class Roo.DatePicker
10047  * @extends Roo.Component
10048  * Simple date picker class.
10049  * @constructor
10050  * Create a new DatePicker
10051  * @param {Object} config The config object
10052  */
10053 Roo.DatePicker = function(config){
10054     Roo.DatePicker.superclass.constructor.call(this, config);
10055
10056     this.value = config && config.value ?
10057                  config.value.clearTime() : new Date().clearTime();
10058
10059     this.addEvents({
10060         /**
10061              * @event select
10062              * Fires when a date is selected
10063              * @param {DatePicker} this
10064              * @param {Date} date The selected date
10065              */
10066         select: true
10067     });
10068
10069     if(this.handler){
10070         this.on("select", this.handler,  this.scope || this);
10071     }
10072     // build the disabledDatesRE
10073     if(!this.disabledDatesRE && this.disabledDates){
10074         var dd = this.disabledDates;
10075         var re = "(?:";
10076         for(var i = 0; i < dd.length; i++){
10077             re += dd[i];
10078             if(i != dd.length-1) re += "|";
10079         }
10080         this.disabledDatesRE = new RegExp(re + ")");
10081     }
10082 };
10083
10084 Roo.extend(Roo.DatePicker, Roo.Component, {
10085     /**
10086      * @cfg {String} todayText
10087      * The text to display on the button that selects the current date (defaults to "Today")
10088      */
10089     todayText : "Today",
10090     /**
10091      * @cfg {String} okText
10092      * The text to display on the ok button
10093      */
10094     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10095     /**
10096      * @cfg {String} cancelText
10097      * The text to display on the cancel button
10098      */
10099     cancelText : "Cancel",
10100     /**
10101      * @cfg {String} todayTip
10102      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10103      */
10104     todayTip : "{0} (Spacebar)",
10105     /**
10106      * @cfg {Date} minDate
10107      * Minimum allowable date (JavaScript date object, defaults to null)
10108      */
10109     minDate : null,
10110     /**
10111      * @cfg {Date} maxDate
10112      * Maximum allowable date (JavaScript date object, defaults to null)
10113      */
10114     maxDate : null,
10115     /**
10116      * @cfg {String} minText
10117      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10118      */
10119     minText : "This date is before the minimum date",
10120     /**
10121      * @cfg {String} maxText
10122      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10123      */
10124     maxText : "This date is after the maximum date",
10125     /**
10126      * @cfg {String} format
10127      * The default date format string which can be overriden for localization support.  The format must be
10128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10129      */
10130     format : "m/d/y",
10131     /**
10132      * @cfg {Array} disabledDays
10133      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10134      */
10135     disabledDays : null,
10136     /**
10137      * @cfg {String} disabledDaysText
10138      * The tooltip to display when the date falls on a disabled day (defaults to "")
10139      */
10140     disabledDaysText : "",
10141     /**
10142      * @cfg {RegExp} disabledDatesRE
10143      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10144      */
10145     disabledDatesRE : null,
10146     /**
10147      * @cfg {String} disabledDatesText
10148      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10149      */
10150     disabledDatesText : "",
10151     /**
10152      * @cfg {Boolean} constrainToViewport
10153      * True to constrain the date picker to the viewport (defaults to true)
10154      */
10155     constrainToViewport : true,
10156     /**
10157      * @cfg {Array} monthNames
10158      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10159      */
10160     monthNames : Date.monthNames,
10161     /**
10162      * @cfg {Array} dayNames
10163      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10164      */
10165     dayNames : Date.dayNames,
10166     /**
10167      * @cfg {String} nextText
10168      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10169      */
10170     nextText: 'Next Month (Control+Right)',
10171     /**
10172      * @cfg {String} prevText
10173      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10174      */
10175     prevText: 'Previous Month (Control+Left)',
10176     /**
10177      * @cfg {String} monthYearText
10178      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10179      */
10180     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10181     /**
10182      * @cfg {Number} startDay
10183      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10184      */
10185     startDay : 0,
10186     /**
10187      * @cfg {Bool} showClear
10188      * Show a clear button (usefull for date form elements that can be blank.)
10189      */
10190     
10191     showClear: false,
10192     
10193     /**
10194      * Sets the value of the date field
10195      * @param {Date} value The date to set
10196      */
10197     setValue : function(value){
10198         var old = this.value;
10199         this.value = value.clearTime(true);
10200         if(this.el){
10201             this.update(this.value);
10202         }
10203     },
10204
10205     /**
10206      * Gets the current selected value of the date field
10207      * @return {Date} The selected date
10208      */
10209     getValue : function(){
10210         return this.value;
10211     },
10212
10213     // private
10214     focus : function(){
10215         if(this.el){
10216             this.update(this.activeDate);
10217         }
10218     },
10219
10220     // private
10221     onRender : function(container, position){
10222         var m = [
10223              '<table cellspacing="0">',
10224                 '<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>',
10225                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10226         var dn = this.dayNames;
10227         for(var i = 0; i < 7; i++){
10228             var d = this.startDay+i;
10229             if(d > 6){
10230                 d = d-7;
10231             }
10232             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10233         }
10234         m[m.length] = "</tr></thead><tbody><tr>";
10235         for(var i = 0; i < 42; i++) {
10236             if(i % 7 == 0 && i != 0){
10237                 m[m.length] = "</tr><tr>";
10238             }
10239             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10240         }
10241         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10242             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10243
10244         var el = document.createElement("div");
10245         el.className = "x-date-picker";
10246         el.innerHTML = m.join("");
10247
10248         container.dom.insertBefore(el, position);
10249
10250         this.el = Roo.get(el);
10251         this.eventEl = Roo.get(el.firstChild);
10252
10253         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10254             handler: this.showPrevMonth,
10255             scope: this,
10256             preventDefault:true,
10257             stopDefault:true
10258         });
10259
10260         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10261             handler: this.showNextMonth,
10262             scope: this,
10263             preventDefault:true,
10264             stopDefault:true
10265         });
10266
10267         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10268
10269         this.monthPicker = this.el.down('div.x-date-mp');
10270         this.monthPicker.enableDisplayMode('block');
10271         
10272         var kn = new Roo.KeyNav(this.eventEl, {
10273             "left" : function(e){
10274                 e.ctrlKey ?
10275                     this.showPrevMonth() :
10276                     this.update(this.activeDate.add("d", -1));
10277             },
10278
10279             "right" : function(e){
10280                 e.ctrlKey ?
10281                     this.showNextMonth() :
10282                     this.update(this.activeDate.add("d", 1));
10283             },
10284
10285             "up" : function(e){
10286                 e.ctrlKey ?
10287                     this.showNextYear() :
10288                     this.update(this.activeDate.add("d", -7));
10289             },
10290
10291             "down" : function(e){
10292                 e.ctrlKey ?
10293                     this.showPrevYear() :
10294                     this.update(this.activeDate.add("d", 7));
10295             },
10296
10297             "pageUp" : function(e){
10298                 this.showNextMonth();
10299             },
10300
10301             "pageDown" : function(e){
10302                 this.showPrevMonth();
10303             },
10304
10305             "enter" : function(e){
10306                 e.stopPropagation();
10307                 return true;
10308             },
10309
10310             scope : this
10311         });
10312
10313         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10314
10315         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10316
10317         this.el.unselectable();
10318         
10319         this.cells = this.el.select("table.x-date-inner tbody td");
10320         this.textNodes = this.el.query("table.x-date-inner tbody span");
10321
10322         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10323             text: "&#160;",
10324             tooltip: this.monthYearText
10325         });
10326
10327         this.mbtn.on('click', this.showMonthPicker, this);
10328         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10329
10330
10331         var today = (new Date()).dateFormat(this.format);
10332         
10333         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10334         if (this.showClear) {
10335             baseTb.add( new Roo.Toolbar.Fill());
10336         }
10337         baseTb.add({
10338             text: String.format(this.todayText, today),
10339             tooltip: String.format(this.todayTip, today),
10340             handler: this.selectToday,
10341             scope: this
10342         });
10343         
10344         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10345             
10346         //});
10347         if (this.showClear) {
10348             
10349             baseTb.add( new Roo.Toolbar.Fill());
10350             baseTb.add({
10351                 text: '&#160;',
10352                 cls: 'x-btn-icon x-btn-clear',
10353                 handler: function() {
10354                     //this.value = '';
10355                     this.fireEvent("select", this, '');
10356                 },
10357                 scope: this
10358             });
10359         }
10360         
10361         
10362         if(Roo.isIE){
10363             this.el.repaint();
10364         }
10365         this.update(this.value);
10366     },
10367
10368     createMonthPicker : function(){
10369         if(!this.monthPicker.dom.firstChild){
10370             var buf = ['<table border="0" cellspacing="0">'];
10371             for(var i = 0; i < 6; i++){
10372                 buf.push(
10373                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10374                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10375                     i == 0 ?
10376                     '<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>' :
10377                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10378                 );
10379             }
10380             buf.push(
10381                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10382                     this.okText,
10383                     '</button><button type="button" class="x-date-mp-cancel">',
10384                     this.cancelText,
10385                     '</button></td></tr>',
10386                 '</table>'
10387             );
10388             this.monthPicker.update(buf.join(''));
10389             this.monthPicker.on('click', this.onMonthClick, this);
10390             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10391
10392             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10393             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10394
10395             this.mpMonths.each(function(m, a, i){
10396                 i += 1;
10397                 if((i%2) == 0){
10398                     m.dom.xmonth = 5 + Math.round(i * .5);
10399                 }else{
10400                     m.dom.xmonth = Math.round((i-1) * .5);
10401                 }
10402             });
10403         }
10404     },
10405
10406     showMonthPicker : function(){
10407         this.createMonthPicker();
10408         var size = this.el.getSize();
10409         this.monthPicker.setSize(size);
10410         this.monthPicker.child('table').setSize(size);
10411
10412         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10413         this.updateMPMonth(this.mpSelMonth);
10414         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10415         this.updateMPYear(this.mpSelYear);
10416
10417         this.monthPicker.slideIn('t', {duration:.2});
10418     },
10419
10420     updateMPYear : function(y){
10421         this.mpyear = y;
10422         var ys = this.mpYears.elements;
10423         for(var i = 1; i <= 10; i++){
10424             var td = ys[i-1], y2;
10425             if((i%2) == 0){
10426                 y2 = y + Math.round(i * .5);
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }else{
10430                 y2 = y - (5-Math.round(i * .5));
10431                 td.firstChild.innerHTML = y2;
10432                 td.xyear = y2;
10433             }
10434             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10435         }
10436     },
10437
10438     updateMPMonth : function(sm){
10439         this.mpMonths.each(function(m, a, i){
10440             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10441         });
10442     },
10443
10444     selectMPMonth: function(m){
10445         
10446     },
10447
10448     onMonthClick : function(e, t){
10449         e.stopEvent();
10450         var el = new Roo.Element(t), pn;
10451         if(el.is('button.x-date-mp-cancel')){
10452             this.hideMonthPicker();
10453         }
10454         else if(el.is('button.x-date-mp-ok')){
10455             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10456             this.hideMonthPicker();
10457         }
10458         else if(pn = el.up('td.x-date-mp-month', 2)){
10459             this.mpMonths.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelMonth = pn.dom.xmonth;
10462         }
10463         else if(pn = el.up('td.x-date-mp-year', 2)){
10464             this.mpYears.removeClass('x-date-mp-sel');
10465             pn.addClass('x-date-mp-sel');
10466             this.mpSelYear = pn.dom.xyear;
10467         }
10468         else if(el.is('a.x-date-mp-prev')){
10469             this.updateMPYear(this.mpyear-10);
10470         }
10471         else if(el.is('a.x-date-mp-next')){
10472             this.updateMPYear(this.mpyear+10);
10473         }
10474     },
10475
10476     onMonthDblClick : function(e, t){
10477         e.stopEvent();
10478         var el = new Roo.Element(t), pn;
10479         if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483         else if(pn = el.up('td.x-date-mp-year', 2)){
10484             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10485             this.hideMonthPicker();
10486         }
10487     },
10488
10489     hideMonthPicker : function(disableAnim){
10490         if(this.monthPicker){
10491             if(disableAnim === true){
10492                 this.monthPicker.hide();
10493             }else{
10494                 this.monthPicker.slideOut('t', {duration:.2});
10495             }
10496         }
10497     },
10498
10499     // private
10500     showPrevMonth : function(e){
10501         this.update(this.activeDate.add("mo", -1));
10502     },
10503
10504     // private
10505     showNextMonth : function(e){
10506         this.update(this.activeDate.add("mo", 1));
10507     },
10508
10509     // private
10510     showPrevYear : function(){
10511         this.update(this.activeDate.add("y", -1));
10512     },
10513
10514     // private
10515     showNextYear : function(){
10516         this.update(this.activeDate.add("y", 1));
10517     },
10518
10519     // private
10520     handleMouseWheel : function(e){
10521         var delta = e.getWheelDelta();
10522         if(delta > 0){
10523             this.showPrevMonth();
10524             e.stopEvent();
10525         } else if(delta < 0){
10526             this.showNextMonth();
10527             e.stopEvent();
10528         }
10529     },
10530
10531     // private
10532     handleDateClick : function(e, t){
10533         e.stopEvent();
10534         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10535             this.setValue(new Date(t.dateValue));
10536             this.fireEvent("select", this, this.value);
10537         }
10538     },
10539
10540     // private
10541     selectToday : function(){
10542         this.setValue(new Date().clearTime());
10543         this.fireEvent("select", this, this.value);
10544     },
10545
10546     // private
10547     update : function(date){
10548         var vd = this.activeDate;
10549         this.activeDate = date;
10550         if(vd && this.el){
10551             var t = date.getTime();
10552             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10553                 this.cells.removeClass("x-date-selected");
10554                 this.cells.each(function(c){
10555                    if(c.dom.firstChild.dateValue == t){
10556                        c.addClass("x-date-selected");
10557                        setTimeout(function(){
10558                             try{c.dom.firstChild.focus();}catch(e){}
10559                        }, 50);
10560                        return false;
10561                    }
10562                 });
10563                 return;
10564             }
10565         }
10566         var days = date.getDaysInMonth();
10567         var firstOfMonth = date.getFirstDateOfMonth();
10568         var startingPos = firstOfMonth.getDay()-this.startDay;
10569
10570         if(startingPos <= this.startDay){
10571             startingPos += 7;
10572         }
10573
10574         var pm = date.add("mo", -1);
10575         var prevStart = pm.getDaysInMonth()-startingPos;
10576
10577         var cells = this.cells.elements;
10578         var textEls = this.textNodes;
10579         days += startingPos;
10580
10581         // convert everything to numbers so it's fast
10582         var day = 86400000;
10583         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10584         var today = new Date().clearTime().getTime();
10585         var sel = date.clearTime().getTime();
10586         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10587         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10588         var ddMatch = this.disabledDatesRE;
10589         var ddText = this.disabledDatesText;
10590         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10591         var ddaysText = this.disabledDaysText;
10592         var format = this.format;
10593
10594         var setCellClass = function(cal, cell){
10595             cell.title = "";
10596             var t = d.getTime();
10597             cell.firstChild.dateValue = t;
10598             if(t == today){
10599                 cell.className += " x-date-today";
10600                 cell.title = cal.todayText;
10601             }
10602             if(t == sel){
10603                 cell.className += " x-date-selected";
10604                 setTimeout(function(){
10605                     try{cell.firstChild.focus();}catch(e){}
10606                 }, 50);
10607             }
10608             // disabling
10609             if(t < min) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.minText;
10612                 return;
10613             }
10614             if(t > max) {
10615                 cell.className = " x-date-disabled";
10616                 cell.title = cal.maxText;
10617                 return;
10618             }
10619             if(ddays){
10620                 if(ddays.indexOf(d.getDay()) != -1){
10621                     cell.title = ddaysText;
10622                     cell.className = " x-date-disabled";
10623                 }
10624             }
10625             if(ddMatch && format){
10626                 var fvalue = d.dateFormat(format);
10627                 if(ddMatch.test(fvalue)){
10628                     cell.title = ddText.replace("%0", fvalue);
10629                     cell.className = " x-date-disabled";
10630                 }
10631             }
10632         };
10633
10634         var i = 0;
10635         for(; i < startingPos; i++) {
10636             textEls[i].innerHTML = (++prevStart);
10637             d.setDate(d.getDate()+1);
10638             cells[i].className = "x-date-prevday";
10639             setCellClass(this, cells[i]);
10640         }
10641         for(; i < days; i++){
10642             intDay = i - startingPos + 1;
10643             textEls[i].innerHTML = (intDay);
10644             d.setDate(d.getDate()+1);
10645             cells[i].className = "x-date-active";
10646             setCellClass(this, cells[i]);
10647         }
10648         var extraDays = 0;
10649         for(; i < 42; i++) {
10650              textEls[i].innerHTML = (++extraDays);
10651              d.setDate(d.getDate()+1);
10652              cells[i].className = "x-date-nextday";
10653              setCellClass(this, cells[i]);
10654         }
10655
10656         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10657
10658         if(!this.internalRender){
10659             var main = this.el.dom.firstChild;
10660             var w = main.offsetWidth;
10661             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10662             Roo.fly(main).setWidth(w);
10663             this.internalRender = true;
10664             // opera does not respect the auto grow header center column
10665             // then, after it gets a width opera refuses to recalculate
10666             // without a second pass
10667             if(Roo.isOpera && !this.secondPass){
10668                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10669                 this.secondPass = true;
10670                 this.update.defer(10, this, [date]);
10671             }
10672         }
10673     }
10674 });/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684 /**
10685  * @class Roo.TabPanel
10686  * @extends Roo.util.Observable
10687  * A lightweight tab container.
10688  * <br><br>
10689  * Usage:
10690  * <pre><code>
10691 // basic tabs 1, built from existing content
10692 var tabs = new Roo.TabPanel("tabs1");
10693 tabs.addTab("script", "View Script");
10694 tabs.addTab("markup", "View Markup");
10695 tabs.activate("script");
10696
10697 // more advanced tabs, built from javascript
10698 var jtabs = new Roo.TabPanel("jtabs");
10699 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10700
10701 // set up the UpdateManager
10702 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10703 var updater = tab2.getUpdateManager();
10704 updater.setDefaultUrl("ajax1.htm");
10705 tab2.on('activate', updater.refresh, updater, true);
10706
10707 // Use setUrl for Ajax loading
10708 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10709 tab3.setUrl("ajax2.htm", null, true);
10710
10711 // Disabled tab
10712 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10713 tab4.disable();
10714
10715 jtabs.activate("jtabs-1");
10716  * </code></pre>
10717  * @constructor
10718  * Create a new TabPanel.
10719  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10720  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10721  */
10722 Roo.TabPanel = function(container, config){
10723     /**
10724     * The container element for this TabPanel.
10725     * @type Roo.Element
10726     */
10727     this.el = Roo.get(container, true);
10728     if(config){
10729         if(typeof config == "boolean"){
10730             this.tabPosition = config ? "bottom" : "top";
10731         }else{
10732             Roo.apply(this, config);
10733         }
10734     }
10735     if(this.tabPosition == "bottom"){
10736         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10737         this.el.addClass("x-tabs-bottom");
10738     }
10739     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10740     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10741     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10742     if(Roo.isIE){
10743         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10744     }
10745     if(this.tabPosition != "bottom"){
10746     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10747      * @type Roo.Element
10748      */
10749       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750       this.el.addClass("x-tabs-top");
10751     }
10752     this.items = [];
10753
10754     this.bodyEl.setStyle("position", "relative");
10755
10756     this.active = null;
10757     this.activateDelegate = this.activate.createDelegate(this);
10758
10759     this.addEvents({
10760         /**
10761          * @event tabchange
10762          * Fires when the active tab changes
10763          * @param {Roo.TabPanel} this
10764          * @param {Roo.TabPanelItem} activePanel The new active tab
10765          */
10766         "tabchange": true,
10767         /**
10768          * @event beforetabchange
10769          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10770          * @param {Roo.TabPanel} this
10771          * @param {Object} e Set cancel to true on this object to cancel the tab change
10772          * @param {Roo.TabPanelItem} tab The tab being changed to
10773          */
10774         "beforetabchange" : true
10775     });
10776
10777     Roo.EventManager.onWindowResize(this.onResize, this);
10778     this.cpad = this.el.getPadding("lr");
10779     this.hiddenCount = 0;
10780
10781     Roo.TabPanel.superclass.constructor.call(this);
10782 };
10783
10784 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10785         /*
10786          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10787          */
10788     tabPosition : "top",
10789         /*
10790          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10791          */
10792     currentTabWidth : 0,
10793         /*
10794          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     minTabWidth : 40,
10797         /*
10798          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     maxTabWidth : 250,
10801         /*
10802          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10803          */
10804     preferredTabWidth : 175,
10805         /*
10806          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10807          */
10808     resizeTabs : false,
10809         /*
10810          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10811          */
10812     monitorResize : true,
10813
10814     /**
10815      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10816      * @param {String} id The id of the div to use <b>or create</b>
10817      * @param {String} text The text for the tab
10818      * @param {String} content (optional) Content to put in the TabPanelItem body
10819      * @param {Boolean} closable (optional) True to create a close icon on the tab
10820      * @return {Roo.TabPanelItem} The created TabPanelItem
10821      */
10822     addTab : function(id, text, content, closable){
10823         var item = new Roo.TabPanelItem(this, id, text, closable);
10824         this.addTabItem(item);
10825         if(content){
10826             item.setContent(content);
10827         }
10828         return item;
10829     },
10830
10831     /**
10832      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10834      * @return {Roo.TabPanelItem}
10835      */
10836     getTab : function(id){
10837         return this.items[id];
10838     },
10839
10840     /**
10841      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10842      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10843      */
10844     hideTab : function(id){
10845         var t = this.items[id];
10846         if(!t.isHidden()){
10847            t.setHidden(true);
10848            this.hiddenCount++;
10849            this.autoSizeTabs();
10850         }
10851     },
10852
10853     /**
10854      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10855      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10856      */
10857     unhideTab : function(id){
10858         var t = this.items[id];
10859         if(t.isHidden()){
10860            t.setHidden(false);
10861            this.hiddenCount--;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * Adds an existing {@link Roo.TabPanelItem}.
10868      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10869      */
10870     addTabItem : function(item){
10871         this.items[item.id] = item;
10872         this.items.push(item);
10873         if(this.resizeTabs){
10874            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10875            this.autoSizeTabs();
10876         }else{
10877             item.autoSize();
10878         }
10879     },
10880
10881     /**
10882      * Removes a {@link Roo.TabPanelItem}.
10883      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10884      */
10885     removeTab : function(id){
10886         var items = this.items;
10887         var tab = items[id];
10888         if(!tab) { return; }
10889         var index = items.indexOf(tab);
10890         if(this.active == tab && items.length > 1){
10891             var newTab = this.getNextAvailable(index);
10892             if(newTab) {
10893                 newTab.activate();
10894             }
10895         }
10896         this.stripEl.dom.removeChild(tab.pnode.dom);
10897         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10898             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10899         }
10900         items.splice(index, 1);
10901         delete this.items[tab.id];
10902         tab.fireEvent("close", tab);
10903         tab.purgeListeners();
10904         this.autoSizeTabs();
10905     },
10906
10907     getNextAvailable : function(start){
10908         var items = this.items;
10909         var index = start;
10910         // look for a next tab that will slide over to
10911         // replace the one being removed
10912         while(index < items.length){
10913             var item = items[++index];
10914             if(item && !item.isHidden()){
10915                 return item;
10916             }
10917         }
10918         // if one isn't found select the previous tab (on the left)
10919         index = start;
10920         while(index >= 0){
10921             var item = items[--index];
10922             if(item && !item.isHidden()){
10923                 return item;
10924             }
10925         }
10926         return null;
10927     },
10928
10929     /**
10930      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10931      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10932      */
10933     disableTab : function(id){
10934         var tab = this.items[id];
10935         if(tab && this.active != tab){
10936             tab.disable();
10937         }
10938     },
10939
10940     /**
10941      * Enables a {@link Roo.TabPanelItem} that is disabled.
10942      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10943      */
10944     enableTab : function(id){
10945         var tab = this.items[id];
10946         tab.enable();
10947     },
10948
10949     /**
10950      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10951      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10952      * @return {Roo.TabPanelItem} The TabPanelItem.
10953      */
10954     activate : function(id){
10955         var tab = this.items[id];
10956         if(!tab){
10957             return null;
10958         }
10959         if(tab == this.active || tab.disabled){
10960             return tab;
10961         }
10962         var e = {};
10963         this.fireEvent("beforetabchange", this, e, tab);
10964         if(e.cancel !== true && !tab.disabled){
10965             if(this.active){
10966                 this.active.hide();
10967             }
10968             this.active = this.items[id];
10969             this.active.show();
10970             this.fireEvent("tabchange", this, this.active);
10971         }
10972         return tab;
10973     },
10974
10975     /**
10976      * Gets the active {@link Roo.TabPanelItem}.
10977      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10978      */
10979     getActiveTab : function(){
10980         return this.active;
10981     },
10982
10983     /**
10984      * Updates the tab body element to fit the height of the container element
10985      * for overflow scrolling
10986      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10987      */
10988     syncHeight : function(targetHeight){
10989         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10990         var bm = this.bodyEl.getMargins();
10991         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10992         this.bodyEl.setHeight(newHeight);
10993         return newHeight;
10994     },
10995
10996     onResize : function(){
10997         if(this.monitorResize){
10998             this.autoSizeTabs();
10999         }
11000     },
11001
11002     /**
11003      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     beginUpdate : function(){
11006         this.updating = true;
11007     },
11008
11009     /**
11010      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11011      */
11012     endUpdate : function(){
11013         this.updating = false;
11014         this.autoSizeTabs();
11015     },
11016
11017     /**
11018      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11019      */
11020     autoSizeTabs : function(){
11021         var count = this.items.length;
11022         var vcount = count - this.hiddenCount;
11023         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11024         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11025         var availWidth = Math.floor(w / vcount);
11026         var b = this.stripBody;
11027         if(b.getWidth() > w){
11028             var tabs = this.items;
11029             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11030             if(availWidth < this.minTabWidth){
11031                 /*if(!this.sleft){    // incomplete scrolling code
11032                     this.createScrollButtons();
11033                 }
11034                 this.showScroll();
11035                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11036             }
11037         }else{
11038             if(this.currentTabWidth < this.preferredTabWidth){
11039                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11040             }
11041         }
11042     },
11043
11044     /**
11045      * Returns the number of tabs in this TabPanel.
11046      * @return {Number}
11047      */
11048      getCount : function(){
11049          return this.items.length;
11050      },
11051
11052     /**
11053      * Resizes all the tabs to the passed width
11054      * @param {Number} The new width
11055      */
11056     setTabWidth : function(width){
11057         this.currentTabWidth = width;
11058         for(var i = 0, len = this.items.length; i < len; i++) {
11059                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11060         }
11061     },
11062
11063     /**
11064      * Destroys this TabPanel
11065      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11066      */
11067     destroy : function(removeEl){
11068         Roo.EventManager.removeResizeListener(this.onResize, this);
11069         for(var i = 0, len = this.items.length; i < len; i++){
11070             this.items[i].purgeListeners();
11071         }
11072         if(removeEl === true){
11073             this.el.update("");
11074             this.el.remove();
11075         }
11076     }
11077 });
11078
11079 /**
11080  * @class Roo.TabPanelItem
11081  * @extends Roo.util.Observable
11082  * Represents an individual item (tab plus body) in a TabPanel.
11083  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11084  * @param {String} id The id of this TabPanelItem
11085  * @param {String} text The text for the tab of this TabPanelItem
11086  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11087  */
11088 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11089     /**
11090      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11091      * @type Roo.TabPanel
11092      */
11093     this.tabPanel = tabPanel;
11094     /**
11095      * The id for this TabPanelItem
11096      * @type String
11097      */
11098     this.id = id;
11099     /** @private */
11100     this.disabled = false;
11101     /** @private */
11102     this.text = text;
11103     /** @private */
11104     this.loaded = false;
11105     this.closable = closable;
11106
11107     /**
11108      * The body element for this TabPanelItem.
11109      * @type Roo.Element
11110      */
11111     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11112     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11113     this.bodyEl.setStyle("display", "block");
11114     this.bodyEl.setStyle("zoom", "1");
11115     this.hideAction();
11116
11117     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11118     /** @private */
11119     this.el = Roo.get(els.el, true);
11120     this.inner = Roo.get(els.inner, true);
11121     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11122     this.pnode = Roo.get(els.el.parentNode, true);
11123     this.el.on("mousedown", this.onTabMouseDown, this);
11124     this.el.on("click", this.onTabClick, this);
11125     /** @private */
11126     if(closable){
11127         var c = Roo.get(els.close, true);
11128         c.dom.title = this.closeText;
11129         c.addClassOnOver("close-over");
11130         c.on("click", this.closeClick, this);
11131      }
11132
11133     this.addEvents({
11134          /**
11135          * @event activate
11136          * Fires when this tab becomes the active tab.
11137          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11138          * @param {Roo.TabPanelItem} this
11139          */
11140         "activate": true,
11141         /**
11142          * @event beforeclose
11143          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11144          * @param {Roo.TabPanelItem} this
11145          * @param {Object} e Set cancel to true on this object to cancel the close.
11146          */
11147         "beforeclose": true,
11148         /**
11149          * @event close
11150          * Fires when this tab is closed.
11151          * @param {Roo.TabPanelItem} this
11152          */
11153          "close": true,
11154         /**
11155          * @event deactivate
11156          * Fires when this tab is no longer the active tab.
11157          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11158          * @param {Roo.TabPanelItem} this
11159          */
11160          "deactivate" : true
11161     });
11162     this.hidden = false;
11163
11164     Roo.TabPanelItem.superclass.constructor.call(this);
11165 };
11166
11167 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11168     purgeListeners : function(){
11169        Roo.util.Observable.prototype.purgeListeners.call(this);
11170        this.el.removeAllListeners();
11171     },
11172     /**
11173      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11174      */
11175     show : function(){
11176         this.pnode.addClass("on");
11177         this.showAction();
11178         if(Roo.isOpera){
11179             this.tabPanel.stripWrap.repaint();
11180         }
11181         this.fireEvent("activate", this.tabPanel, this);
11182     },
11183
11184     /**
11185      * Returns true if this tab is the active tab.
11186      * @return {Boolean}
11187      */
11188     isActive : function(){
11189         return this.tabPanel.getActiveTab() == this;
11190     },
11191
11192     /**
11193      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11194      */
11195     hide : function(){
11196         this.pnode.removeClass("on");
11197         this.hideAction();
11198         this.fireEvent("deactivate", this.tabPanel, this);
11199     },
11200
11201     hideAction : function(){
11202         this.bodyEl.hide();
11203         this.bodyEl.setStyle("position", "absolute");
11204         this.bodyEl.setLeft("-20000px");
11205         this.bodyEl.setTop("-20000px");
11206     },
11207
11208     showAction : function(){
11209         this.bodyEl.setStyle("position", "relative");
11210         this.bodyEl.setTop("");
11211         this.bodyEl.setLeft("");
11212         this.bodyEl.show();
11213     },
11214
11215     /**
11216      * Set the tooltip for the tab.
11217      * @param {String} tooltip The tab's tooltip
11218      */
11219     setTooltip : function(text){
11220         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11221             this.textEl.dom.qtip = text;
11222             this.textEl.dom.removeAttribute('title');
11223         }else{
11224             this.textEl.dom.title = text;
11225         }
11226     },
11227
11228     onTabClick : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     onTabMouseDown : function(e){
11234         e.preventDefault();
11235         this.tabPanel.activate(this.id);
11236     },
11237
11238     getWidth : function(){
11239         return this.inner.getWidth();
11240     },
11241
11242     setWidth : function(width){
11243         var iwidth = width - this.pnode.getPadding("lr");
11244         this.inner.setWidth(iwidth);
11245         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11246         this.pnode.setWidth(width);
11247     },
11248
11249     /**
11250      * Show or hide the tab
11251      * @param {Boolean} hidden True to hide or false to show.
11252      */
11253     setHidden : function(hidden){
11254         this.hidden = hidden;
11255         this.pnode.setStyle("display", hidden ? "none" : "");
11256     },
11257
11258     /**
11259      * Returns true if this tab is "hidden"
11260      * @return {Boolean}
11261      */
11262     isHidden : function(){
11263         return this.hidden;
11264     },
11265
11266     /**
11267      * Returns the text for this tab
11268      * @return {String}
11269      */
11270     getText : function(){
11271         return this.text;
11272     },
11273
11274     autoSize : function(){
11275         //this.el.beginMeasure();
11276         this.textEl.setWidth(1);
11277         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11278         //this.el.endMeasure();
11279     },
11280
11281     /**
11282      * Sets the text for the tab (Note: this also sets the tooltip text)
11283      * @param {String} text The tab's text and tooltip
11284      */
11285     setText : function(text){
11286         this.text = text;
11287         this.textEl.update(text);
11288         this.setTooltip(text);
11289         if(!this.tabPanel.resizeTabs){
11290             this.autoSize();
11291         }
11292     },
11293     /**
11294      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11295      */
11296     activate : function(){
11297         this.tabPanel.activate(this.id);
11298     },
11299
11300     /**
11301      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11302      */
11303     disable : function(){
11304         if(this.tabPanel.active != this){
11305             this.disabled = true;
11306             this.pnode.addClass("disabled");
11307         }
11308     },
11309
11310     /**
11311      * Enables this TabPanelItem if it was previously disabled.
11312      */
11313     enable : function(){
11314         this.disabled = false;
11315         this.pnode.removeClass("disabled");
11316     },
11317
11318     /**
11319      * Sets the content for this TabPanelItem.
11320      * @param {String} content The content
11321      * @param {Boolean} loadScripts true to look for and load scripts
11322      */
11323     setContent : function(content, loadScripts){
11324         this.bodyEl.update(content, loadScripts);
11325     },
11326
11327     /**
11328      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11329      * @return {Roo.UpdateManager} The UpdateManager
11330      */
11331     getUpdateManager : function(){
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /**
11336      * Set a URL to be used to load the content for this TabPanelItem.
11337      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11338      * @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)
11339      * @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)
11340      * @return {Roo.UpdateManager} The UpdateManager
11341      */
11342     setUrl : function(url, params, loadOnce){
11343         if(this.refreshDelegate){
11344             this.un('activate', this.refreshDelegate);
11345         }
11346         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11347         this.on("activate", this.refreshDelegate);
11348         return this.bodyEl.getUpdateManager();
11349     },
11350
11351     /** @private */
11352     _handleRefresh : function(url, params, loadOnce){
11353         if(!loadOnce || !this.loaded){
11354             var updater = this.bodyEl.getUpdateManager();
11355             updater.update(url, params, this._setLoaded.createDelegate(this));
11356         }
11357     },
11358
11359     /**
11360      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11361      *   Will fail silently if the setUrl method has not been called.
11362      *   This does not activate the panel, just updates its content.
11363      */
11364     refresh : function(){
11365         if(this.refreshDelegate){
11366            this.loaded = false;
11367            this.refreshDelegate();
11368         }
11369     },
11370
11371     /** @private */
11372     _setLoaded : function(){
11373         this.loaded = true;
11374     },
11375
11376     /** @private */
11377     closeClick : function(e){
11378         var o = {};
11379         e.stopEvent();
11380         this.fireEvent("beforeclose", this, o);
11381         if(o.cancel !== true){
11382             this.tabPanel.removeTab(this.id);
11383         }
11384     },
11385     /**
11386      * The text displayed in the tooltip for the close icon.
11387      * @type String
11388      */
11389     closeText : "Close this tab"
11390 });
11391
11392 /** @private */
11393 Roo.TabPanel.prototype.createStrip = function(container){
11394     var strip = document.createElement("div");
11395     strip.className = "x-tabs-wrap";
11396     container.appendChild(strip);
11397     return strip;
11398 };
11399 /** @private */
11400 Roo.TabPanel.prototype.createStripList = function(strip){
11401     // div wrapper for retard IE
11402     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>';
11403     return strip.firstChild.firstChild.firstChild.firstChild;
11404 };
11405 /** @private */
11406 Roo.TabPanel.prototype.createBody = function(container){
11407     var body = document.createElement("div");
11408     Roo.id(body, "tab-body");
11409     Roo.fly(body).addClass("x-tabs-body");
11410     container.appendChild(body);
11411     return body;
11412 };
11413 /** @private */
11414 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11415     var body = Roo.getDom(id);
11416     if(!body){
11417         body = document.createElement("div");
11418         body.id = id;
11419     }
11420     Roo.fly(body).addClass("x-tabs-item-body");
11421     bodyEl.insertBefore(body, bodyEl.firstChild);
11422     return body;
11423 };
11424 /** @private */
11425 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11426     var td = document.createElement("td");
11427     stripEl.appendChild(td);
11428     if(closable){
11429         td.className = "x-tabs-closable";
11430         if(!this.closeTpl){
11431             this.closeTpl = new Roo.Template(
11432                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11433                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11434                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11435             );
11436         }
11437         var el = this.closeTpl.overwrite(td, {"text": text});
11438         var close = el.getElementsByTagName("div")[0];
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "close": close, "inner": inner};
11441     } else {
11442         if(!this.tabTpl){
11443             this.tabTpl = new Roo.Template(
11444                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11445                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11446             );
11447         }
11448         var el = this.tabTpl.overwrite(td, {"text": text});
11449         var inner = el.getElementsByTagName("em")[0];
11450         return {"el": el, "inner": inner};
11451     }
11452 };/*
11453  * Based on:
11454  * Ext JS Library 1.1.1
11455  * Copyright(c) 2006-2007, Ext JS, LLC.
11456  *
11457  * Originally Released Under LGPL - original licence link has changed is not relivant.
11458  *
11459  * Fork - LGPL
11460  * <script type="text/javascript">
11461  */
11462
11463 /**
11464  * @class Roo.Button
11465  * @extends Roo.util.Observable
11466  * Simple Button class
11467  * @cfg {String} text The button text
11468  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11469  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11470  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11471  * @cfg {Object} scope The scope of the handler
11472  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11473  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11474  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11475  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11476  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11477  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11478    applies if enableToggle = true)
11479  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11480  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11481   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11482  * @constructor
11483  * Create a new button
11484  * @param {Object} config The config object
11485  */
11486 Roo.Button = function(renderTo, config)
11487 {
11488     if (!config) {
11489         config = renderTo;
11490         renderTo = config.renderTo || false;
11491     }
11492     
11493     Roo.apply(this, config);
11494     this.addEvents({
11495         /**
11496              * @event click
11497              * Fires when this button is clicked
11498              * @param {Button} this
11499              * @param {EventObject} e The click event
11500              */
11501             "click" : true,
11502         /**
11503              * @event toggle
11504              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11505              * @param {Button} this
11506              * @param {Boolean} pressed
11507              */
11508             "toggle" : true,
11509         /**
11510              * @event mouseover
11511              * Fires when the mouse hovers over the button
11512              * @param {Button} this
11513              * @param {Event} e The event object
11514              */
11515         'mouseover' : true,
11516         /**
11517              * @event mouseout
11518              * Fires when the mouse exits the button
11519              * @param {Button} this
11520              * @param {Event} e The event object
11521              */
11522         'mouseout': true,
11523          /**
11524              * @event render
11525              * Fires when the button is rendered
11526              * @param {Button} this
11527              */
11528         'render': true
11529     });
11530     if(this.menu){
11531         this.menu = Roo.menu.MenuMgr.get(this.menu);
11532     }
11533     // register listeners first!!  - so render can be captured..
11534     Roo.util.Observable.call(this);
11535     if(renderTo){
11536         this.render(renderTo);
11537     }
11538     
11539   
11540 };
11541
11542 Roo.extend(Roo.Button, Roo.util.Observable, {
11543     /**
11544      * 
11545      */
11546     
11547     /**
11548      * Read-only. True if this button is hidden
11549      * @type Boolean
11550      */
11551     hidden : false,
11552     /**
11553      * Read-only. True if this button is disabled
11554      * @type Boolean
11555      */
11556     disabled : false,
11557     /**
11558      * Read-only. True if this button is pressed (only if enableToggle = true)
11559      * @type Boolean
11560      */
11561     pressed : false,
11562
11563     /**
11564      * @cfg {Number} tabIndex 
11565      * The DOM tabIndex for this button (defaults to undefined)
11566      */
11567     tabIndex : undefined,
11568
11569     /**
11570      * @cfg {Boolean} enableToggle
11571      * True to enable pressed/not pressed toggling (defaults to false)
11572      */
11573     enableToggle: false,
11574     /**
11575      * @cfg {Mixed} menu
11576      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11577      */
11578     menu : undefined,
11579     /**
11580      * @cfg {String} menuAlign
11581      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11582      */
11583     menuAlign : "tl-bl?",
11584
11585     /**
11586      * @cfg {String} iconCls
11587      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11588      */
11589     iconCls : undefined,
11590     /**
11591      * @cfg {String} type
11592      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11593      */
11594     type : 'button',
11595
11596     // private
11597     menuClassTarget: 'tr',
11598
11599     /**
11600      * @cfg {String} clickEvent
11601      * The type of event to map to the button's event handler (defaults to 'click')
11602      */
11603     clickEvent : 'click',
11604
11605     /**
11606      * @cfg {Boolean} handleMouseEvents
11607      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11608      */
11609     handleMouseEvents : true,
11610
11611     /**
11612      * @cfg {String} tooltipType
11613      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11614      */
11615     tooltipType : 'qtip',
11616
11617     /**
11618      * @cfg {String} cls
11619      * A CSS class to apply to the button's main element.
11620      */
11621     
11622     /**
11623      * @cfg {Roo.Template} template (Optional)
11624      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11625      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11626      * require code modifications if required elements (e.g. a button) aren't present.
11627      */
11628
11629     // private
11630     render : function(renderTo){
11631         var btn;
11632         if(this.hideParent){
11633             this.parentEl = Roo.get(renderTo);
11634         }
11635         if(!this.dhconfig){
11636             if(!this.template){
11637                 if(!Roo.Button.buttonTemplate){
11638                     // hideous table template
11639                     Roo.Button.buttonTemplate = new Roo.Template(
11640                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11641                         '<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>',
11642                         "</tr></tbody></table>");
11643                 }
11644                 this.template = Roo.Button.buttonTemplate;
11645             }
11646             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11647             var btnEl = btn.child("button:first");
11648             btnEl.on('focus', this.onFocus, this);
11649             btnEl.on('blur', this.onBlur, this);
11650             if(this.cls){
11651                 btn.addClass(this.cls);
11652             }
11653             if(this.icon){
11654                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11655             }
11656             if(this.iconCls){
11657                 btnEl.addClass(this.iconCls);
11658                 if(!this.cls){
11659                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11660                 }
11661             }
11662             if(this.tabIndex !== undefined){
11663                 btnEl.dom.tabIndex = this.tabIndex;
11664             }
11665             if(this.tooltip){
11666                 if(typeof this.tooltip == 'object'){
11667                     Roo.QuickTips.tips(Roo.apply({
11668                           target: btnEl.id
11669                     }, this.tooltip));
11670                 } else {
11671                     btnEl.dom[this.tooltipType] = this.tooltip;
11672                 }
11673             }
11674         }else{
11675             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11676         }
11677         this.el = btn;
11678         if(this.id){
11679             this.el.dom.id = this.el.id = this.id;
11680         }
11681         if(this.menu){
11682             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11683             this.menu.on("show", this.onMenuShow, this);
11684             this.menu.on("hide", this.onMenuHide, this);
11685         }
11686         btn.addClass("x-btn");
11687         if(Roo.isIE && !Roo.isIE7){
11688             this.autoWidth.defer(1, this);
11689         }else{
11690             this.autoWidth();
11691         }
11692         if(this.handleMouseEvents){
11693             btn.on("mouseover", this.onMouseOver, this);
11694             btn.on("mouseout", this.onMouseOut, this);
11695             btn.on("mousedown", this.onMouseDown, this);
11696         }
11697         btn.on(this.clickEvent, this.onClick, this);
11698         //btn.on("mouseup", this.onMouseUp, this);
11699         if(this.hidden){
11700             this.hide();
11701         }
11702         if(this.disabled){
11703             this.disable();
11704         }
11705         Roo.ButtonToggleMgr.register(this);
11706         if(this.pressed){
11707             this.el.addClass("x-btn-pressed");
11708         }
11709         if(this.repeat){
11710             var repeater = new Roo.util.ClickRepeater(btn,
11711                 typeof this.repeat == "object" ? this.repeat : {}
11712             );
11713             repeater.on("click", this.onClick,  this);
11714         }
11715         
11716         this.fireEvent('render', this);
11717         
11718     },
11719     /**
11720      * Returns the button's underlying element
11721      * @return {Roo.Element} The element
11722      */
11723     getEl : function(){
11724         return this.el;  
11725     },
11726     
11727     /**
11728      * Destroys this Button and removes any listeners.
11729      */
11730     destroy : function(){
11731         Roo.ButtonToggleMgr.unregister(this);
11732         this.el.removeAllListeners();
11733         this.purgeListeners();
11734         this.el.remove();
11735     },
11736
11737     // private
11738     autoWidth : function(){
11739         if(this.el){
11740             this.el.setWidth("auto");
11741             if(Roo.isIE7 && Roo.isStrict){
11742                 var ib = this.el.child('button');
11743                 if(ib && ib.getWidth() > 20){
11744                     ib.clip();
11745                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11746                 }
11747             }
11748             if(this.minWidth){
11749                 if(this.hidden){
11750                     this.el.beginMeasure();
11751                 }
11752                 if(this.el.getWidth() < this.minWidth){
11753                     this.el.setWidth(this.minWidth);
11754                 }
11755                 if(this.hidden){
11756                     this.el.endMeasure();
11757                 }
11758             }
11759         }
11760     },
11761
11762     /**
11763      * Assigns this button's click handler
11764      * @param {Function} handler The function to call when the button is clicked
11765      * @param {Object} scope (optional) Scope for the function passed in
11766      */
11767     setHandler : function(handler, scope){
11768         this.handler = handler;
11769         this.scope = scope;  
11770     },
11771     
11772     /**
11773      * Sets this button's text
11774      * @param {String} text The button text
11775      */
11776     setText : function(text){
11777         this.text = text;
11778         if(this.el){
11779             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11780         }
11781         this.autoWidth();
11782     },
11783     
11784     /**
11785      * Gets the text for this button
11786      * @return {String} The button text
11787      */
11788     getText : function(){
11789         return this.text;  
11790     },
11791     
11792     /**
11793      * Show this button
11794      */
11795     show: function(){
11796         this.hidden = false;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11799         }
11800     },
11801     
11802     /**
11803      * Hide this button
11804      */
11805     hide: function(){
11806         this.hidden = true;
11807         if(this.el){
11808             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11809         }
11810     },
11811     
11812     /**
11813      * Convenience function for boolean show/hide
11814      * @param {Boolean} visible True to show, false to hide
11815      */
11816     setVisible: function(visible){
11817         if(visible) {
11818             this.show();
11819         }else{
11820             this.hide();
11821         }
11822     },
11823     
11824     /**
11825      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11826      * @param {Boolean} state (optional) Force a particular state
11827      */
11828     toggle : function(state){
11829         state = state === undefined ? !this.pressed : state;
11830         if(state != this.pressed){
11831             if(state){
11832                 this.el.addClass("x-btn-pressed");
11833                 this.pressed = true;
11834                 this.fireEvent("toggle", this, true);
11835             }else{
11836                 this.el.removeClass("x-btn-pressed");
11837                 this.pressed = false;
11838                 this.fireEvent("toggle", this, false);
11839             }
11840             if(this.toggleHandler){
11841                 this.toggleHandler.call(this.scope || this, this, state);
11842             }
11843         }
11844     },
11845     
11846     /**
11847      * Focus the button
11848      */
11849     focus : function(){
11850         this.el.child('button:first').focus();
11851     },
11852     
11853     /**
11854      * Disable this button
11855      */
11856     disable : function(){
11857         if(this.el){
11858             this.el.addClass("x-btn-disabled");
11859         }
11860         this.disabled = true;
11861     },
11862     
11863     /**
11864      * Enable this button
11865      */
11866     enable : function(){
11867         if(this.el){
11868             this.el.removeClass("x-btn-disabled");
11869         }
11870         this.disabled = false;
11871     },
11872
11873     /**
11874      * Convenience function for boolean enable/disable
11875      * @param {Boolean} enabled True to enable, false to disable
11876      */
11877     setDisabled : function(v){
11878         this[v !== true ? "enable" : "disable"]();
11879     },
11880
11881     // private
11882     onClick : function(e){
11883         if(e){
11884             e.preventDefault();
11885         }
11886         if(e.button != 0){
11887             return;
11888         }
11889         if(!this.disabled){
11890             if(this.enableToggle){
11891                 this.toggle();
11892             }
11893             if(this.menu && !this.menu.isVisible()){
11894                 this.menu.show(this.el, this.menuAlign);
11895             }
11896             this.fireEvent("click", this, e);
11897             if(this.handler){
11898                 this.el.removeClass("x-btn-over");
11899                 this.handler.call(this.scope || this, this, e);
11900             }
11901         }
11902     },
11903     // private
11904     onMouseOver : function(e){
11905         if(!this.disabled){
11906             this.el.addClass("x-btn-over");
11907             this.fireEvent('mouseover', this, e);
11908         }
11909     },
11910     // private
11911     onMouseOut : function(e){
11912         if(!e.within(this.el,  true)){
11913             this.el.removeClass("x-btn-over");
11914             this.fireEvent('mouseout', this, e);
11915         }
11916     },
11917     // private
11918     onFocus : function(e){
11919         if(!this.disabled){
11920             this.el.addClass("x-btn-focus");
11921         }
11922     },
11923     // private
11924     onBlur : function(e){
11925         this.el.removeClass("x-btn-focus");
11926     },
11927     // private
11928     onMouseDown : function(e){
11929         if(!this.disabled && e.button == 0){
11930             this.el.addClass("x-btn-click");
11931             Roo.get(document).on('mouseup', this.onMouseUp, this);
11932         }
11933     },
11934     // private
11935     onMouseUp : function(e){
11936         if(e.button == 0){
11937             this.el.removeClass("x-btn-click");
11938             Roo.get(document).un('mouseup', this.onMouseUp, this);
11939         }
11940     },
11941     // private
11942     onMenuShow : function(e){
11943         this.el.addClass("x-btn-menu-active");
11944     },
11945     // private
11946     onMenuHide : function(e){
11947         this.el.removeClass("x-btn-menu-active");
11948     }   
11949 });
11950
11951 // Private utility class used by Button
11952 Roo.ButtonToggleMgr = function(){
11953    var groups = {};
11954    
11955    function toggleGroup(btn, state){
11956        if(state){
11957            var g = groups[btn.toggleGroup];
11958            for(var i = 0, l = g.length; i < l; i++){
11959                if(g[i] != btn){
11960                    g[i].toggle(false);
11961                }
11962            }
11963        }
11964    }
11965    
11966    return {
11967        register : function(btn){
11968            if(!btn.toggleGroup){
11969                return;
11970            }
11971            var g = groups[btn.toggleGroup];
11972            if(!g){
11973                g = groups[btn.toggleGroup] = [];
11974            }
11975            g.push(btn);
11976            btn.on("toggle", toggleGroup);
11977        },
11978        
11979        unregister : function(btn){
11980            if(!btn.toggleGroup){
11981                return;
11982            }
11983            var g = groups[btn.toggleGroup];
11984            if(g){
11985                g.remove(btn);
11986                btn.un("toggle", toggleGroup);
11987            }
11988        }
11989    };
11990 }();/*
11991  * Based on:
11992  * Ext JS Library 1.1.1
11993  * Copyright(c) 2006-2007, Ext JS, LLC.
11994  *
11995  * Originally Released Under LGPL - original licence link has changed is not relivant.
11996  *
11997  * Fork - LGPL
11998  * <script type="text/javascript">
11999  */
12000  
12001 /**
12002  * @class Roo.SplitButton
12003  * @extends Roo.Button
12004  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12005  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12006  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12007  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12008  * @cfg {String} arrowTooltip The title attribute of the arrow
12009  * @constructor
12010  * Create a new menu button
12011  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12012  * @param {Object} config The config object
12013  */
12014 Roo.SplitButton = function(renderTo, config){
12015     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12016     /**
12017      * @event arrowclick
12018      * Fires when this button's arrow is clicked
12019      * @param {SplitButton} this
12020      * @param {EventObject} e The click event
12021      */
12022     this.addEvents({"arrowclick":true});
12023 };
12024
12025 Roo.extend(Roo.SplitButton, Roo.Button, {
12026     render : function(renderTo){
12027         // this is one sweet looking template!
12028         var tpl = new Roo.Template(
12029             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12031             '<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>',
12032             "</tbody></table></td><td>",
12033             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12034             '<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>',
12035             "</tbody></table></td></tr></table>"
12036         );
12037         var btn = tpl.append(renderTo, [this.text, this.type], true);
12038         var btnEl = btn.child("button");
12039         if(this.cls){
12040             btn.addClass(this.cls);
12041         }
12042         if(this.icon){
12043             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12044         }
12045         if(this.iconCls){
12046             btnEl.addClass(this.iconCls);
12047             if(!this.cls){
12048                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12049             }
12050         }
12051         this.el = btn;
12052         if(this.handleMouseEvents){
12053             btn.on("mouseover", this.onMouseOver, this);
12054             btn.on("mouseout", this.onMouseOut, this);
12055             btn.on("mousedown", this.onMouseDown, this);
12056             btn.on("mouseup", this.onMouseUp, this);
12057         }
12058         btn.on(this.clickEvent, this.onClick, this);
12059         if(this.tooltip){
12060             if(typeof this.tooltip == 'object'){
12061                 Roo.QuickTips.tips(Roo.apply({
12062                       target: btnEl.id
12063                 }, this.tooltip));
12064             } else {
12065                 btnEl.dom[this.tooltipType] = this.tooltip;
12066             }
12067         }
12068         if(this.arrowTooltip){
12069             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12070         }
12071         if(this.hidden){
12072             this.hide();
12073         }
12074         if(this.disabled){
12075             this.disable();
12076         }
12077         if(this.pressed){
12078             this.el.addClass("x-btn-pressed");
12079         }
12080         if(Roo.isIE && !Roo.isIE7){
12081             this.autoWidth.defer(1, this);
12082         }else{
12083             this.autoWidth();
12084         }
12085         if(this.menu){
12086             this.menu.on("show", this.onMenuShow, this);
12087             this.menu.on("hide", this.onMenuHide, this);
12088         }
12089         this.fireEvent('render', this);
12090     },
12091
12092     // private
12093     autoWidth : function(){
12094         if(this.el){
12095             var tbl = this.el.child("table:first");
12096             var tbl2 = this.el.child("table:last");
12097             this.el.setWidth("auto");
12098             tbl.setWidth("auto");
12099             if(Roo.isIE7 && Roo.isStrict){
12100                 var ib = this.el.child('button:first');
12101                 if(ib && ib.getWidth() > 20){
12102                     ib.clip();
12103                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12104                 }
12105             }
12106             if(this.minWidth){
12107                 if(this.hidden){
12108                     this.el.beginMeasure();
12109                 }
12110                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12111                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12112                 }
12113                 if(this.hidden){
12114                     this.el.endMeasure();
12115                 }
12116             }
12117             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12118         } 
12119     },
12120     /**
12121      * Sets this button's click handler
12122      * @param {Function} handler The function to call when the button is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setHandler : function(handler, scope){
12126         this.handler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Sets this button's arrow click handler
12132      * @param {Function} handler The function to call when the arrow is clicked
12133      * @param {Object} scope (optional) Scope for the function passed above
12134      */
12135     setArrowHandler : function(handler, scope){
12136         this.arrowHandler = handler;
12137         this.scope = scope;  
12138     },
12139     
12140     /**
12141      * Focus the button
12142      */
12143     focus : function(){
12144         if(this.el){
12145             this.el.child("button:first").focus();
12146         }
12147     },
12148
12149     // private
12150     onClick : function(e){
12151         e.preventDefault();
12152         if(!this.disabled){
12153             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12154                 if(this.menu && !this.menu.isVisible()){
12155                     this.menu.show(this.el, this.menuAlign);
12156                 }
12157                 this.fireEvent("arrowclick", this, e);
12158                 if(this.arrowHandler){
12159                     this.arrowHandler.call(this.scope || this, this, e);
12160                 }
12161             }else{
12162                 this.fireEvent("click", this, e);
12163                 if(this.handler){
12164                     this.handler.call(this.scope || this, this, e);
12165                 }
12166             }
12167         }
12168     },
12169     // private
12170     onMouseDown : function(e){
12171         if(!this.disabled){
12172             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12173         }
12174     },
12175     // private
12176     onMouseUp : function(e){
12177         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12178     }   
12179 });
12180
12181
12182 // backwards compat
12183 Roo.MenuButton = Roo.SplitButton;/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193
12194 /**
12195  * @class Roo.Toolbar
12196  * Basic Toolbar class.
12197  * @constructor
12198  * Creates a new Toolbar
12199  * @param {Object} config The config object
12200  */ 
12201 Roo.Toolbar = function(container, buttons, config)
12202 {
12203     /// old consturctor format still supported..
12204     if(container instanceof Array){ // omit the container for later rendering
12205         buttons = container;
12206         config = buttons;
12207         container = null;
12208     }
12209     if (typeof(container) == 'object' && container.xtype) {
12210         config = container;
12211         container = config.container;
12212         buttons = config.buttons; // not really - use items!!
12213     }
12214     var xitems = [];
12215     if (config && config.items) {
12216         xitems = config.items;
12217         delete config.items;
12218     }
12219     Roo.apply(this, config);
12220     this.buttons = buttons;
12221     
12222     if(container){
12223         this.render(container);
12224     }
12225     Roo.each(xitems, function(b) {
12226         this.add(b);
12227     }, this);
12228     
12229 };
12230
12231 Roo.Toolbar.prototype = {
12232     /**
12233      * @cfg {Roo.data.Store} items
12234      * array of button configs or elements to add
12235      */
12236     
12237     /**
12238      * @cfg {String/HTMLElement/Element} container
12239      * The id or element that will contain the toolbar
12240      */
12241     // private
12242     render : function(ct){
12243         this.el = Roo.get(ct);
12244         if(this.cls){
12245             this.el.addClass(this.cls);
12246         }
12247         // using a table allows for vertical alignment
12248         // 100% width is needed by Safari...
12249         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12250         this.tr = this.el.child("tr", true);
12251         var autoId = 0;
12252         this.items = new Roo.util.MixedCollection(false, function(o){
12253             return o.id || ("item" + (++autoId));
12254         });
12255         if(this.buttons){
12256             this.add.apply(this, this.buttons);
12257             delete this.buttons;
12258         }
12259     },
12260
12261     /**
12262      * Adds element(s) to the toolbar -- this function takes a variable number of 
12263      * arguments of mixed type and adds them to the toolbar.
12264      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12265      * <ul>
12266      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12267      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12268      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12269      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12270      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12271      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12272      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12273      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12274      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12275      * </ul>
12276      * @param {Mixed} arg2
12277      * @param {Mixed} etc.
12278      */
12279     add : function(){
12280         var a = arguments, l = a.length;
12281         for(var i = 0; i < l; i++){
12282             this._add(a[i]);
12283         }
12284     },
12285     // private..
12286     _add : function(el) {
12287         
12288         if (el.xtype) {
12289             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12290         }
12291         
12292         if (el.applyTo){ // some kind of form field
12293             return this.addField(el);
12294         } 
12295         if (el.render){ // some kind of Toolbar.Item
12296             return this.addItem(el);
12297         }
12298         if (typeof el == "string"){ // string
12299             if(el == "separator" || el == "-"){
12300                 return this.addSeparator();
12301             }
12302             if (el == " "){
12303                 return this.addSpacer();
12304             }
12305             if(el == "->"){
12306                 return this.addFill();
12307             }
12308             return this.addText(el);
12309             
12310         }
12311         if(el.tagName){ // element
12312             return this.addElement(el);
12313         }
12314         if(typeof el == "object"){ // must be button config?
12315             return this.addButton(el);
12316         }
12317         // and now what?!?!
12318         return false;
12319         
12320     },
12321     
12322     /**
12323      * Add an Xtype element
12324      * @param {Object} xtype Xtype Object
12325      * @return {Object} created Object
12326      */
12327     addxtype : function(e){
12328         return this.add(e);  
12329     },
12330     
12331     /**
12332      * Returns the Element for this toolbar.
12333      * @return {Roo.Element}
12334      */
12335     getEl : function(){
12336         return this.el;  
12337     },
12338     
12339     /**
12340      * Adds a separator
12341      * @return {Roo.Toolbar.Item} The separator item
12342      */
12343     addSeparator : function(){
12344         return this.addItem(new Roo.Toolbar.Separator());
12345     },
12346
12347     /**
12348      * Adds a spacer element
12349      * @return {Roo.Toolbar.Spacer} The spacer item
12350      */
12351     addSpacer : function(){
12352         return this.addItem(new Roo.Toolbar.Spacer());
12353     },
12354
12355     /**
12356      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12357      * @return {Roo.Toolbar.Fill} The fill item
12358      */
12359     addFill : function(){
12360         return this.addItem(new Roo.Toolbar.Fill());
12361     },
12362
12363     /**
12364      * Adds any standard HTML element to the toolbar
12365      * @param {String/HTMLElement/Element} el The element or id of the element to add
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addElement : function(el){
12369         return this.addItem(new Roo.Toolbar.Item(el));
12370     },
12371     /**
12372      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12373      * @type Roo.util.MixedCollection  
12374      */
12375     items : false,
12376      
12377     /**
12378      * Adds any Toolbar.Item or subclass
12379      * @param {Roo.Toolbar.Item} item
12380      * @return {Roo.Toolbar.Item} The item
12381      */
12382     addItem : function(item){
12383         var td = this.nextBlock();
12384         item.render(td);
12385         this.items.add(item);
12386         return item;
12387     },
12388     
12389     /**
12390      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12391      * @param {Object/Array} config A button config or array of configs
12392      * @return {Roo.Toolbar.Button/Array}
12393      */
12394     addButton : function(config){
12395         if(config instanceof Array){
12396             var buttons = [];
12397             for(var i = 0, len = config.length; i < len; i++) {
12398                 buttons.push(this.addButton(config[i]));
12399             }
12400             return buttons;
12401         }
12402         var b = config;
12403         if(!(config instanceof Roo.Toolbar.Button)){
12404             b = config.split ?
12405                 new Roo.Toolbar.SplitButton(config) :
12406                 new Roo.Toolbar.Button(config);
12407         }
12408         var td = this.nextBlock();
12409         b.render(td);
12410         this.items.add(b);
12411         return b;
12412     },
12413     
12414     /**
12415      * Adds text to the toolbar
12416      * @param {String} text The text to add
12417      * @return {Roo.Toolbar.Item} The element's item
12418      */
12419     addText : function(text){
12420         return this.addItem(new Roo.Toolbar.TextItem(text));
12421     },
12422     
12423     /**
12424      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12425      * @param {Number} index The index where the item is to be inserted
12426      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12427      * @return {Roo.Toolbar.Button/Item}
12428      */
12429     insertButton : function(index, item){
12430         if(item instanceof Array){
12431             var buttons = [];
12432             for(var i = 0, len = item.length; i < len; i++) {
12433                buttons.push(this.insertButton(index + i, item[i]));
12434             }
12435             return buttons;
12436         }
12437         if (!(item instanceof Roo.Toolbar.Button)){
12438            item = new Roo.Toolbar.Button(item);
12439         }
12440         var td = document.createElement("td");
12441         this.tr.insertBefore(td, this.tr.childNodes[index]);
12442         item.render(td);
12443         this.items.insert(index, item);
12444         return item;
12445     },
12446     
12447     /**
12448      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12449      * @param {Object} config
12450      * @return {Roo.Toolbar.Item} The element's item
12451      */
12452     addDom : function(config, returnEl){
12453         var td = this.nextBlock();
12454         Roo.DomHelper.overwrite(td, config);
12455         var ti = new Roo.Toolbar.Item(td.firstChild);
12456         ti.render(td);
12457         this.items.add(ti);
12458         return ti;
12459     },
12460
12461     /**
12462      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12463      * @type Roo.util.MixedCollection  
12464      */
12465     fields : false,
12466     
12467     /**
12468      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12469      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12470      * @param {Roo.form.Field} field
12471      * @return {Roo.ToolbarItem}
12472      */
12473      
12474       
12475     addField : function(field) {
12476         if (!this.fields) {
12477             var autoId = 0;
12478             this.fields = new Roo.util.MixedCollection(false, function(o){
12479                 return o.id || ("item" + (++autoId));
12480             });
12481
12482         }
12483         
12484         var td = this.nextBlock();
12485         field.render(td);
12486         var ti = new Roo.Toolbar.Item(td.firstChild);
12487         ti.render(td);
12488         this.items.add(ti);
12489         this.fields.add(field);
12490         return ti;
12491     },
12492     /**
12493      * Hide the toolbar
12494      * @method hide
12495      */
12496      
12497       
12498     hide : function()
12499     {
12500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12501         this.el.child('div').hide();
12502     },
12503     /**
12504      * Show the toolbar
12505      * @method show
12506      */
12507     show : function()
12508     {
12509         this.el.child('div').show();
12510     },
12511       
12512     // private
12513     nextBlock : function(){
12514         var td = document.createElement("td");
12515         this.tr.appendChild(td);
12516         return td;
12517     },
12518
12519     // private
12520     destroy : function(){
12521         if(this.items){ // rendered?
12522             Roo.destroy.apply(Roo, this.items.items);
12523         }
12524         if(this.fields){ // rendered?
12525             Roo.destroy.apply(Roo, this.fields.items);
12526         }
12527         Roo.Element.uncache(this.el, this.tr);
12528     }
12529 };
12530
12531 /**
12532  * @class Roo.Toolbar.Item
12533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12534  * @constructor
12535  * Creates a new Item
12536  * @param {HTMLElement} el 
12537  */
12538 Roo.Toolbar.Item = function(el){
12539     this.el = Roo.getDom(el);
12540     this.id = Roo.id(this.el);
12541     this.hidden = false;
12542 };
12543
12544 Roo.Toolbar.Item.prototype = {
12545     
12546     /**
12547      * Get this item's HTML Element
12548      * @return {HTMLElement}
12549      */
12550     getEl : function(){
12551        return this.el;  
12552     },
12553
12554     // private
12555     render : function(td){
12556         this.td = td;
12557         td.appendChild(this.el);
12558     },
12559     
12560     /**
12561      * Removes and destroys this item.
12562      */
12563     destroy : function(){
12564         this.td.parentNode.removeChild(this.td);
12565     },
12566     
12567     /**
12568      * Shows this item.
12569      */
12570     show: function(){
12571         this.hidden = false;
12572         this.td.style.display = "";
12573     },
12574     
12575     /**
12576      * Hides this item.
12577      */
12578     hide: function(){
12579         this.hidden = true;
12580         this.td.style.display = "none";
12581     },
12582     
12583     /**
12584      * Convenience function for boolean show/hide.
12585      * @param {Boolean} visible true to show/false to hide
12586      */
12587     setVisible: function(visible){
12588         if(visible) {
12589             this.show();
12590         }else{
12591             this.hide();
12592         }
12593     },
12594     
12595     /**
12596      * Try to focus this item.
12597      */
12598     focus : function(){
12599         Roo.fly(this.el).focus();
12600     },
12601     
12602     /**
12603      * Disables this item.
12604      */
12605     disable : function(){
12606         Roo.fly(this.td).addClass("x-item-disabled");
12607         this.disabled = true;
12608         this.el.disabled = true;
12609     },
12610     
12611     /**
12612      * Enables this item.
12613      */
12614     enable : function(){
12615         Roo.fly(this.td).removeClass("x-item-disabled");
12616         this.disabled = false;
12617         this.el.disabled = false;
12618     }
12619 };
12620
12621
12622 /**
12623  * @class Roo.Toolbar.Separator
12624  * @extends Roo.Toolbar.Item
12625  * A simple toolbar separator class
12626  * @constructor
12627  * Creates a new Separator
12628  */
12629 Roo.Toolbar.Separator = function(){
12630     var s = document.createElement("span");
12631     s.className = "ytb-sep";
12632     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12633 };
12634 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12635     enable:Roo.emptyFn,
12636     disable:Roo.emptyFn,
12637     focus:Roo.emptyFn
12638 });
12639
12640 /**
12641  * @class Roo.Toolbar.Spacer
12642  * @extends Roo.Toolbar.Item
12643  * A simple element that adds extra horizontal space to a toolbar.
12644  * @constructor
12645  * Creates a new Spacer
12646  */
12647 Roo.Toolbar.Spacer = function(){
12648     var s = document.createElement("div");
12649     s.className = "ytb-spacer";
12650     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12651 };
12652 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12653     enable:Roo.emptyFn,
12654     disable:Roo.emptyFn,
12655     focus:Roo.emptyFn
12656 });
12657
12658 /**
12659  * @class Roo.Toolbar.Fill
12660  * @extends Roo.Toolbar.Spacer
12661  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12662  * @constructor
12663  * Creates a new Spacer
12664  */
12665 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12666     // private
12667     render : function(td){
12668         td.style.width = '100%';
12669         Roo.Toolbar.Fill.superclass.render.call(this, td);
12670     }
12671 });
12672
12673 /**
12674  * @class Roo.Toolbar.TextItem
12675  * @extends Roo.Toolbar.Item
12676  * A simple class that renders text directly into a toolbar.
12677  * @constructor
12678  * Creates a new TextItem
12679  * @param {String} text
12680  */
12681 Roo.Toolbar.TextItem = function(text){
12682     if (typeof(text) == 'object') {
12683         text = text.text;
12684     }
12685     var s = document.createElement("span");
12686     s.className = "ytb-text";
12687     s.innerHTML = text;
12688     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12689 };
12690 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12691     enable:Roo.emptyFn,
12692     disable:Roo.emptyFn,
12693     focus:Roo.emptyFn
12694 });
12695
12696 /**
12697  * @class Roo.Toolbar.Button
12698  * @extends Roo.Button
12699  * A button that renders into a toolbar.
12700  * @constructor
12701  * Creates a new Button
12702  * @param {Object} config A standard {@link Roo.Button} config object
12703  */
12704 Roo.Toolbar.Button = function(config){
12705     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12706 };
12707 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12708     render : function(td){
12709         this.td = td;
12710         Roo.Toolbar.Button.superclass.render.call(this, td);
12711     },
12712     
12713     /**
12714      * Removes and destroys this button
12715      */
12716     destroy : function(){
12717         Roo.Toolbar.Button.superclass.destroy.call(this);
12718         this.td.parentNode.removeChild(this.td);
12719     },
12720     
12721     /**
12722      * Shows this button
12723      */
12724     show: function(){
12725         this.hidden = false;
12726         this.td.style.display = "";
12727     },
12728     
12729     /**
12730      * Hides this button
12731      */
12732     hide: function(){
12733         this.hidden = true;
12734         this.td.style.display = "none";
12735     },
12736
12737     /**
12738      * Disables this item
12739      */
12740     disable : function(){
12741         Roo.fly(this.td).addClass("x-item-disabled");
12742         this.disabled = true;
12743     },
12744
12745     /**
12746      * Enables this item
12747      */
12748     enable : function(){
12749         Roo.fly(this.td).removeClass("x-item-disabled");
12750         this.disabled = false;
12751     }
12752 });
12753 // backwards compat
12754 Roo.ToolbarButton = Roo.Toolbar.Button;
12755
12756 /**
12757  * @class Roo.Toolbar.SplitButton
12758  * @extends Roo.SplitButton
12759  * A menu button that renders into a toolbar.
12760  * @constructor
12761  * Creates a new SplitButton
12762  * @param {Object} config A standard {@link Roo.SplitButton} config object
12763  */
12764 Roo.Toolbar.SplitButton = function(config){
12765     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12766 };
12767 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12768     render : function(td){
12769         this.td = td;
12770         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12771     },
12772     
12773     /**
12774      * Removes and destroys this button
12775      */
12776     destroy : function(){
12777         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12778         this.td.parentNode.removeChild(this.td);
12779     },
12780     
12781     /**
12782      * Shows this button
12783      */
12784     show: function(){
12785         this.hidden = false;
12786         this.td.style.display = "";
12787     },
12788     
12789     /**
12790      * Hides this button
12791      */
12792     hide: function(){
12793         this.hidden = true;
12794         this.td.style.display = "none";
12795     }
12796 });
12797
12798 // backwards compat
12799 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809  
12810 /**
12811  * @class Roo.PagingToolbar
12812  * @extends Roo.Toolbar
12813  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12814  * @constructor
12815  * Create a new PagingToolbar
12816  * @param {Object} config The config object
12817  */
12818 Roo.PagingToolbar = function(el, ds, config)
12819 {
12820     // old args format still supported... - xtype is prefered..
12821     if (typeof(el) == 'object' && el.xtype) {
12822         // created from xtype...
12823         config = el;
12824         ds = el.dataSource;
12825         el = config.container;
12826     }
12827     var items = [];
12828     if (config.items) {
12829         items = config.items;
12830         config.items = [];
12831     }
12832     
12833     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12834     this.ds = ds;
12835     this.cursor = 0;
12836     this.renderButtons(this.el);
12837     this.bind(ds);
12838     
12839     // supprot items array.
12840    
12841     Roo.each(items, function(e) {
12842         this.add(Roo.factory(e));
12843     },this);
12844     
12845 };
12846
12847 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12848     /**
12849      * @cfg {Roo.data.Store} dataSource
12850      * The underlying data store providing the paged data
12851      */
12852     /**
12853      * @cfg {String/HTMLElement/Element} container
12854      * container The id or element that will contain the toolbar
12855      */
12856     /**
12857      * @cfg {Boolean} displayInfo
12858      * True to display the displayMsg (defaults to false)
12859      */
12860     /**
12861      * @cfg {Number} pageSize
12862      * The number of records to display per page (defaults to 20)
12863      */
12864     pageSize: 20,
12865     /**
12866      * @cfg {String} displayMsg
12867      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12868      */
12869     displayMsg : 'Displaying {0} - {1} of {2}',
12870     /**
12871      * @cfg {String} emptyMsg
12872      * The message to display when no records are found (defaults to "No data to display")
12873      */
12874     emptyMsg : 'No data to display',
12875     /**
12876      * Customizable piece of the default paging text (defaults to "Page")
12877      * @type String
12878      */
12879     beforePageText : "Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "of %0")
12882      * @type String
12883      */
12884     afterPageText : "of {0}",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "First Page")
12887      * @type String
12888      */
12889     firstText : "First Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Previous Page")
12892      * @type String
12893      */
12894     prevText : "Previous Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Next Page")
12897      * @type String
12898      */
12899     nextText : "Next Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Last Page")
12902      * @type String
12903      */
12904     lastText : "Last Page",
12905     /**
12906      * Customizable piece of the default paging text (defaults to "Refresh")
12907      * @type String
12908      */
12909     refreshText : "Refresh",
12910
12911     // private
12912     renderButtons : function(el){
12913         Roo.PagingToolbar.superclass.render.call(this, el);
12914         this.first = this.addButton({
12915             tooltip: this.firstText,
12916             cls: "x-btn-icon x-grid-page-first",
12917             disabled: true,
12918             handler: this.onClick.createDelegate(this, ["first"])
12919         });
12920         this.prev = this.addButton({
12921             tooltip: this.prevText,
12922             cls: "x-btn-icon x-grid-page-prev",
12923             disabled: true,
12924             handler: this.onClick.createDelegate(this, ["prev"])
12925         });
12926         //this.addSeparator();
12927         this.add(this.beforePageText);
12928         this.field = Roo.get(this.addDom({
12929            tag: "input",
12930            type: "text",
12931            size: "3",
12932            value: "1",
12933            cls: "x-grid-page-number"
12934         }).el);
12935         this.field.on("keydown", this.onPagingKeydown, this);
12936         this.field.on("focus", function(){this.dom.select();});
12937         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12938         this.field.setHeight(18);
12939         //this.addSeparator();
12940         this.next = this.addButton({
12941             tooltip: this.nextText,
12942             cls: "x-btn-icon x-grid-page-next",
12943             disabled: true,
12944             handler: this.onClick.createDelegate(this, ["next"])
12945         });
12946         this.last = this.addButton({
12947             tooltip: this.lastText,
12948             cls: "x-btn-icon x-grid-page-last",
12949             disabled: true,
12950             handler: this.onClick.createDelegate(this, ["last"])
12951         });
12952         //this.addSeparator();
12953         this.loading = this.addButton({
12954             tooltip: this.refreshText,
12955             cls: "x-btn-icon x-grid-loading",
12956             handler: this.onClick.createDelegate(this, ["refresh"])
12957         });
12958
12959         if(this.displayInfo){
12960             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12961         }
12962     },
12963
12964     // private
12965     updateInfo : function(){
12966         if(this.displayEl){
12967             var count = this.ds.getCount();
12968             var msg = count == 0 ?
12969                 this.emptyMsg :
12970                 String.format(
12971                     this.displayMsg,
12972                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12973                 );
12974             this.displayEl.update(msg);
12975         }
12976     },
12977
12978     // private
12979     onLoad : function(ds, r, o){
12980        this.cursor = o.params ? o.params.start : 0;
12981        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12982
12983        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12984        this.field.dom.value = ap;
12985        this.first.setDisabled(ap == 1);
12986        this.prev.setDisabled(ap == 1);
12987        this.next.setDisabled(ap == ps);
12988        this.last.setDisabled(ap == ps);
12989        this.loading.enable();
12990        this.updateInfo();
12991     },
12992
12993     // private
12994     getPageData : function(){
12995         var total = this.ds.getTotalCount();
12996         return {
12997             total : total,
12998             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12999             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13000         };
13001     },
13002
13003     // private
13004     onLoadError : function(){
13005         this.loading.enable();
13006     },
13007
13008     // private
13009     onPagingKeydown : function(e){
13010         var k = e.getKey();
13011         var d = this.getPageData();
13012         if(k == e.RETURN){
13013             var v = this.field.dom.value, pageNum;
13014             if(!v || isNaN(pageNum = parseInt(v, 10))){
13015                 this.field.dom.value = d.activePage;
13016                 return;
13017             }
13018             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13020             e.stopEvent();
13021         }
13022         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))
13023         {
13024           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13025           this.field.dom.value = pageNum;
13026           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13027           e.stopEvent();
13028         }
13029         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13030         {
13031           var v = this.field.dom.value, pageNum; 
13032           var increment = (e.shiftKey) ? 10 : 1;
13033           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13034             increment *= -1;
13035           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13036             this.field.dom.value = d.activePage;
13037             return;
13038           }
13039           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13040           {
13041             this.field.dom.value = parseInt(v, 10) + increment;
13042             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13043             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13044           }
13045           e.stopEvent();
13046         }
13047     },
13048
13049     // private
13050     beforeLoad : function(){
13051         if(this.loading){
13052             this.loading.disable();
13053         }
13054     },
13055
13056     // private
13057     onClick : function(which){
13058         var ds = this.ds;
13059         switch(which){
13060             case "first":
13061                 ds.load({params:{start: 0, limit: this.pageSize}});
13062             break;
13063             case "prev":
13064                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13065             break;
13066             case "next":
13067                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13068             break;
13069             case "last":
13070                 var total = ds.getTotalCount();
13071                 var extra = total % this.pageSize;
13072                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13073                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13074             break;
13075             case "refresh":
13076                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13077             break;
13078         }
13079     },
13080
13081     /**
13082      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13083      * @param {Roo.data.Store} store The data store to unbind
13084      */
13085     unbind : function(ds){
13086         ds.un("beforeload", this.beforeLoad, this);
13087         ds.un("load", this.onLoad, this);
13088         ds.un("loadexception", this.onLoadError, this);
13089         ds.un("remove", this.updateInfo, this);
13090         ds.un("add", this.updateInfo, this);
13091         this.ds = undefined;
13092     },
13093
13094     /**
13095      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13096      * @param {Roo.data.Store} store The data store to bind
13097      */
13098     bind : function(ds){
13099         ds.on("beforeload", this.beforeLoad, this);
13100         ds.on("load", this.onLoad, this);
13101         ds.on("loadexception", this.onLoadError, this);
13102         ds.on("remove", this.updateInfo, this);
13103         ds.on("add", this.updateInfo, this);
13104         this.ds = ds;
13105     }
13106 });/*
13107  * Based on:
13108  * Ext JS Library 1.1.1
13109  * Copyright(c) 2006-2007, Ext JS, LLC.
13110  *
13111  * Originally Released Under LGPL - original licence link has changed is not relivant.
13112  *
13113  * Fork - LGPL
13114  * <script type="text/javascript">
13115  */
13116
13117 /**
13118  * @class Roo.Resizable
13119  * @extends Roo.util.Observable
13120  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13121  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13122  * 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
13123  * the element will be wrapped for you automatically.</p>
13124  * <p>Here is the list of valid resize handles:</p>
13125  * <pre>
13126 Value   Description
13127 ------  -------------------
13128  'n'     north
13129  's'     south
13130  'e'     east
13131  'w'     west
13132  'nw'    northwest
13133  'sw'    southwest
13134  'se'    southeast
13135  'ne'    northeast
13136  'hd'    horizontal drag
13137  'all'   all
13138 </pre>
13139  * <p>Here's an example showing the creation of a typical Resizable:</p>
13140  * <pre><code>
13141 var resizer = new Roo.Resizable("element-id", {
13142     handles: 'all',
13143     minWidth: 200,
13144     minHeight: 100,
13145     maxWidth: 500,
13146     maxHeight: 400,
13147     pinned: true
13148 });
13149 resizer.on("resize", myHandler);
13150 </code></pre>
13151  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13152  * resizer.east.setDisplayed(false);</p>
13153  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13154  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13155  * resize operation's new size (defaults to [0, 0])
13156  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13157  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13158  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13159  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13160  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13161  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13162  * @cfg {Number} width The width of the element in pixels (defaults to null)
13163  * @cfg {Number} height The height of the element in pixels (defaults to null)
13164  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13165  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13166  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13167  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13168  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13169  * in favor of the handles config option (defaults to false)
13170  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13171  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13172  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13173  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13174  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13175  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13176  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13177  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13178  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13179  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13180  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13181  * @constructor
13182  * Create a new resizable component
13183  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13184  * @param {Object} config configuration options
13185   */
13186 Roo.Resizable = function(el, config)
13187 {
13188     this.el = Roo.get(el);
13189
13190     if(config && config.wrap){
13191         config.resizeChild = this.el;
13192         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13193         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13194         this.el.setStyle("overflow", "hidden");
13195         this.el.setPositioning(config.resizeChild.getPositioning());
13196         config.resizeChild.clearPositioning();
13197         if(!config.width || !config.height){
13198             var csize = config.resizeChild.getSize();
13199             this.el.setSize(csize.width, csize.height);
13200         }
13201         if(config.pinned && !config.adjustments){
13202             config.adjustments = "auto";
13203         }
13204     }
13205
13206     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13207     this.proxy.unselectable();
13208     this.proxy.enableDisplayMode('block');
13209
13210     Roo.apply(this, config);
13211
13212     if(this.pinned){
13213         this.disableTrackOver = true;
13214         this.el.addClass("x-resizable-pinned");
13215     }
13216     // if the element isn't positioned, make it relative
13217     var position = this.el.getStyle("position");
13218     if(position != "absolute" && position != "fixed"){
13219         this.el.setStyle("position", "relative");
13220     }
13221     if(!this.handles){ // no handles passed, must be legacy style
13222         this.handles = 's,e,se';
13223         if(this.multiDirectional){
13224             this.handles += ',n,w';
13225         }
13226     }
13227     if(this.handles == "all"){
13228         this.handles = "n s e w ne nw se sw";
13229     }
13230     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13231     var ps = Roo.Resizable.positions;
13232     for(var i = 0, len = hs.length; i < len; i++){
13233         if(hs[i] && ps[hs[i]]){
13234             var pos = ps[hs[i]];
13235             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13236         }
13237     }
13238     // legacy
13239     this.corner = this.southeast;
13240     
13241     // updateBox = the box can move..
13242     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13243         this.updateBox = true;
13244     }
13245
13246     this.activeHandle = null;
13247
13248     if(this.resizeChild){
13249         if(typeof this.resizeChild == "boolean"){
13250             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13251         }else{
13252             this.resizeChild = Roo.get(this.resizeChild, true);
13253         }
13254     }
13255     
13256     if(this.adjustments == "auto"){
13257         var rc = this.resizeChild;
13258         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13259         if(rc && (hw || hn)){
13260             rc.position("relative");
13261             rc.setLeft(hw ? hw.el.getWidth() : 0);
13262             rc.setTop(hn ? hn.el.getHeight() : 0);
13263         }
13264         this.adjustments = [
13265             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13266             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13267         ];
13268     }
13269
13270     if(this.draggable){
13271         this.dd = this.dynamic ?
13272             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13273         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13274     }
13275
13276     // public events
13277     this.addEvents({
13278         /**
13279          * @event beforeresize
13280          * Fired before resize is allowed. Set enabled to false to cancel resize.
13281          * @param {Roo.Resizable} this
13282          * @param {Roo.EventObject} e The mousedown event
13283          */
13284         "beforeresize" : true,
13285         /**
13286          * @event resize
13287          * Fired after a resize.
13288          * @param {Roo.Resizable} this
13289          * @param {Number} width The new width
13290          * @param {Number} height The new height
13291          * @param {Roo.EventObject} e The mouseup event
13292          */
13293         "resize" : true
13294     });
13295
13296     if(this.width !== null && this.height !== null){
13297         this.resizeTo(this.width, this.height);
13298     }else{
13299         this.updateChildSize();
13300     }
13301     if(Roo.isIE){
13302         this.el.dom.style.zoom = 1;
13303     }
13304     Roo.Resizable.superclass.constructor.call(this);
13305 };
13306
13307 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13308         resizeChild : false,
13309         adjustments : [0, 0],
13310         minWidth : 5,
13311         minHeight : 5,
13312         maxWidth : 10000,
13313         maxHeight : 10000,
13314         enabled : true,
13315         animate : false,
13316         duration : .35,
13317         dynamic : false,
13318         handles : false,
13319         multiDirectional : false,
13320         disableTrackOver : false,
13321         easing : 'easeOutStrong',
13322         widthIncrement : 0,
13323         heightIncrement : 0,
13324         pinned : false,
13325         width : null,
13326         height : null,
13327         preserveRatio : false,
13328         transparent: false,
13329         minX: 0,
13330         minY: 0,
13331         draggable: false,
13332
13333         /**
13334          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13335          */
13336         constrainTo: undefined,
13337         /**
13338          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13339          */
13340         resizeRegion: undefined,
13341
13342
13343     /**
13344      * Perform a manual resize
13345      * @param {Number} width
13346      * @param {Number} height
13347      */
13348     resizeTo : function(width, height){
13349         this.el.setSize(width, height);
13350         this.updateChildSize();
13351         this.fireEvent("resize", this, width, height, null);
13352     },
13353
13354     // private
13355     startSizing : function(e, handle){
13356         this.fireEvent("beforeresize", this, e);
13357         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13358
13359             if(!this.overlay){
13360                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13361                 this.overlay.unselectable();
13362                 this.overlay.enableDisplayMode("block");
13363                 this.overlay.on("mousemove", this.onMouseMove, this);
13364                 this.overlay.on("mouseup", this.onMouseUp, this);
13365             }
13366             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13367
13368             this.resizing = true;
13369             this.startBox = this.el.getBox();
13370             this.startPoint = e.getXY();
13371             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13372                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13373
13374             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13375             this.overlay.show();
13376
13377             if(this.constrainTo) {
13378                 var ct = Roo.get(this.constrainTo);
13379                 this.resizeRegion = ct.getRegion().adjust(
13380                     ct.getFrameWidth('t'),
13381                     ct.getFrameWidth('l'),
13382                     -ct.getFrameWidth('b'),
13383                     -ct.getFrameWidth('r')
13384                 );
13385             }
13386
13387             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13388             this.proxy.show();
13389             this.proxy.setBox(this.startBox);
13390             if(!this.dynamic){
13391                 this.proxy.setStyle('visibility', 'visible');
13392             }
13393         }
13394     },
13395
13396     // private
13397     onMouseDown : function(handle, e){
13398         if(this.enabled){
13399             e.stopEvent();
13400             this.activeHandle = handle;
13401             this.startSizing(e, handle);
13402         }
13403     },
13404
13405     // private
13406     onMouseUp : function(e){
13407         var size = this.resizeElement();
13408         this.resizing = false;
13409         this.handleOut();
13410         this.overlay.hide();
13411         this.proxy.hide();
13412         this.fireEvent("resize", this, size.width, size.height, e);
13413     },
13414
13415     // private
13416     updateChildSize : function(){
13417         if(this.resizeChild){
13418             var el = this.el;
13419             var child = this.resizeChild;
13420             var adj = this.adjustments;
13421             if(el.dom.offsetWidth){
13422                 var b = el.getSize(true);
13423                 child.setSize(b.width+adj[0], b.height+adj[1]);
13424             }
13425             // Second call here for IE
13426             // The first call enables instant resizing and
13427             // the second call corrects scroll bars if they
13428             // exist
13429             if(Roo.isIE){
13430                 setTimeout(function(){
13431                     if(el.dom.offsetWidth){
13432                         var b = el.getSize(true);
13433                         child.setSize(b.width+adj[0], b.height+adj[1]);
13434                     }
13435                 }, 10);
13436             }
13437         }
13438     },
13439
13440     // private
13441     snap : function(value, inc, min){
13442         if(!inc || !value) return value;
13443         var newValue = value;
13444         var m = value % inc;
13445         if(m > 0){
13446             if(m > (inc/2)){
13447                 newValue = value + (inc-m);
13448             }else{
13449                 newValue = value - m;
13450             }
13451         }
13452         return Math.max(min, newValue);
13453     },
13454
13455     // private
13456     resizeElement : function(){
13457         var box = this.proxy.getBox();
13458         if(this.updateBox){
13459             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13460         }else{
13461             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13462         }
13463         this.updateChildSize();
13464         if(!this.dynamic){
13465             this.proxy.hide();
13466         }
13467         return box;
13468     },
13469
13470     // private
13471     constrain : function(v, diff, m, mx){
13472         if(v - diff < m){
13473             diff = v - m;
13474         }else if(v - diff > mx){
13475             diff = mx - v;
13476         }
13477         return diff;
13478     },
13479
13480     // private
13481     onMouseMove : function(e){
13482         if(this.enabled){
13483             try{// try catch so if something goes wrong the user doesn't get hung
13484
13485             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13486                 return;
13487             }
13488
13489             //var curXY = this.startPoint;
13490             var curSize = this.curSize || this.startBox;
13491             var x = this.startBox.x, y = this.startBox.y;
13492             var ox = x, oy = y;
13493             var w = curSize.width, h = curSize.height;
13494             var ow = w, oh = h;
13495             var mw = this.minWidth, mh = this.minHeight;
13496             var mxw = this.maxWidth, mxh = this.maxHeight;
13497             var wi = this.widthIncrement;
13498             var hi = this.heightIncrement;
13499
13500             var eventXY = e.getXY();
13501             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13502             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13503
13504             var pos = this.activeHandle.position;
13505
13506             switch(pos){
13507                 case "east":
13508                     w += diffX;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     break;
13511              
13512                 case "south":
13513                     h += diffY;
13514                     h = Math.min(Math.max(mh, h), mxh);
13515                     break;
13516                 case "southeast":
13517                     w += diffX;
13518                     h += diffY;
13519                     w = Math.min(Math.max(mw, w), mxw);
13520                     h = Math.min(Math.max(mh, h), mxh);
13521                     break;
13522                 case "north":
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "hdrag":
13528                     
13529                     if (wi) {
13530                         var adiffX = Math.abs(diffX);
13531                         var sub = (adiffX % wi); // how much 
13532                         if (sub > (wi/2)) { // far enough to snap
13533                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13534                         } else {
13535                             // remove difference.. 
13536                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13537                         }
13538                     }
13539                     x += diffX;
13540                     x = Math.max(this.minX, x);
13541                     break;
13542                 case "west":
13543                     diffX = this.constrain(w, diffX, mw, mxw);
13544                     x += diffX;
13545                     w -= diffX;
13546                     break;
13547                 case "northeast":
13548                     w += diffX;
13549                     w = Math.min(Math.max(mw, w), mxw);
13550                     diffY = this.constrain(h, diffY, mh, mxh);
13551                     y += diffY;
13552                     h -= diffY;
13553                     break;
13554                 case "northwest":
13555                     diffX = this.constrain(w, diffX, mw, mxw);
13556                     diffY = this.constrain(h, diffY, mh, mxh);
13557                     y += diffY;
13558                     h -= diffY;
13559                     x += diffX;
13560                     w -= diffX;
13561                     break;
13562                case "southwest":
13563                     diffX = this.constrain(w, diffX, mw, mxw);
13564                     h += diffY;
13565                     h = Math.min(Math.max(mh, h), mxh);
13566                     x += diffX;
13567                     w -= diffX;
13568                     break;
13569             }
13570
13571             var sw = this.snap(w, wi, mw);
13572             var sh = this.snap(h, hi, mh);
13573             if(sw != w || sh != h){
13574                 switch(pos){
13575                     case "northeast":
13576                         y -= sh - h;
13577                     break;
13578                     case "north":
13579                         y -= sh - h;
13580                         break;
13581                     case "southwest":
13582                         x -= sw - w;
13583                     break;
13584                     case "west":
13585                         x -= sw - w;
13586                         break;
13587                     case "northwest":
13588                         x -= sw - w;
13589                         y -= sh - h;
13590                     break;
13591                 }
13592                 w = sw;
13593                 h = sh;
13594             }
13595
13596             if(this.preserveRatio){
13597                 switch(pos){
13598                     case "southeast":
13599                     case "east":
13600                         h = oh * (w/ow);
13601                         h = Math.min(Math.max(mh, h), mxh);
13602                         w = ow * (h/oh);
13603                        break;
13604                     case "south":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                         break;
13609                     case "northeast":
13610                         w = ow * (h/oh);
13611                         w = Math.min(Math.max(mw, w), mxw);
13612                         h = oh * (w/ow);
13613                     break;
13614                     case "north":
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         w = Math.min(Math.max(mw, w), mxw);
13618                         h = oh * (w/ow);
13619                         x += (tw - w) / 2;
13620                         break;
13621                     case "southwest":
13622                         h = oh * (w/ow);
13623                         h = Math.min(Math.max(mh, h), mxh);
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                         break;
13628                     case "west":
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         y += (th - h) / 2;
13633                         var tw = w;
13634                         w = ow * (h/oh);
13635                         x += tw - w;
13636                        break;
13637                     case "northwest":
13638                         var tw = w;
13639                         var th = h;
13640                         h = oh * (w/ow);
13641                         h = Math.min(Math.max(mh, h), mxh);
13642                         w = ow * (h/oh);
13643                         y += th - h;
13644                         x += tw - w;
13645                        break;
13646
13647                 }
13648             }
13649             if (pos == 'hdrag') {
13650                 w = ow;
13651             }
13652             this.proxy.setBounds(x, y, w, h);
13653             if(this.dynamic){
13654                 this.resizeElement();
13655             }
13656             }catch(e){}
13657         }
13658     },
13659
13660     // private
13661     handleOver : function(){
13662         if(this.enabled){
13663             this.el.addClass("x-resizable-over");
13664         }
13665     },
13666
13667     // private
13668     handleOut : function(){
13669         if(!this.resizing){
13670             this.el.removeClass("x-resizable-over");
13671         }
13672     },
13673
13674     /**
13675      * Returns the element this component is bound to.
13676      * @return {Roo.Element}
13677      */
13678     getEl : function(){
13679         return this.el;
13680     },
13681
13682     /**
13683      * Returns the resizeChild element (or null).
13684      * @return {Roo.Element}
13685      */
13686     getResizeChild : function(){
13687         return this.resizeChild;
13688     },
13689
13690     /**
13691      * Destroys this resizable. If the element was wrapped and
13692      * removeEl is not true then the element remains.
13693      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13694      */
13695     destroy : function(removeEl){
13696         this.proxy.remove();
13697         if(this.overlay){
13698             this.overlay.removeAllListeners();
13699             this.overlay.remove();
13700         }
13701         var ps = Roo.Resizable.positions;
13702         for(var k in ps){
13703             if(typeof ps[k] != "function" && this[ps[k]]){
13704                 var h = this[ps[k]];
13705                 h.el.removeAllListeners();
13706                 h.el.remove();
13707             }
13708         }
13709         if(removeEl){
13710             this.el.update("");
13711             this.el.remove();
13712         }
13713     }
13714 });
13715
13716 // private
13717 // hash to map config positions to true positions
13718 Roo.Resizable.positions = {
13719     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13720     hd: "hdrag"
13721 };
13722
13723 // private
13724 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13725     if(!this.tpl){
13726         // only initialize the template if resizable is used
13727         var tpl = Roo.DomHelper.createTemplate(
13728             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13729         );
13730         tpl.compile();
13731         Roo.Resizable.Handle.prototype.tpl = tpl;
13732     }
13733     this.position = pos;
13734     this.rz = rz;
13735     // show north drag fro topdra
13736     var handlepos = pos == 'hdrag' ? 'north' : pos;
13737     
13738     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13739     if (pos == 'hdrag') {
13740         this.el.setStyle('cursor', 'pointer');
13741     }
13742     this.el.unselectable();
13743     if(transparent){
13744         this.el.setOpacity(0);
13745     }
13746     this.el.on("mousedown", this.onMouseDown, this);
13747     if(!disableTrackOver){
13748         this.el.on("mouseover", this.onMouseOver, this);
13749         this.el.on("mouseout", this.onMouseOut, this);
13750     }
13751 };
13752
13753 // private
13754 Roo.Resizable.Handle.prototype = {
13755     afterResize : function(rz){
13756         // do nothing
13757     },
13758     // private
13759     onMouseDown : function(e){
13760         this.rz.onMouseDown(this, e);
13761     },
13762     // private
13763     onMouseOver : function(e){
13764         this.rz.handleOver(this, e);
13765     },
13766     // private
13767     onMouseOut : function(e){
13768         this.rz.handleOut(this, e);
13769     }
13770 };/*
13771  * Based on:
13772  * Ext JS Library 1.1.1
13773  * Copyright(c) 2006-2007, Ext JS, LLC.
13774  *
13775  * Originally Released Under LGPL - original licence link has changed is not relivant.
13776  *
13777  * Fork - LGPL
13778  * <script type="text/javascript">
13779  */
13780
13781 /**
13782  * @class Roo.Editor
13783  * @extends Roo.Component
13784  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13785  * @constructor
13786  * Create a new Editor
13787  * @param {Roo.form.Field} field The Field object (or descendant)
13788  * @param {Object} config The config object
13789  */
13790 Roo.Editor = function(field, config){
13791     Roo.Editor.superclass.constructor.call(this, config);
13792     this.field = field;
13793     this.addEvents({
13794         /**
13795              * @event beforestartedit
13796              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13797              * false from the handler of this event.
13798              * @param {Editor} this
13799              * @param {Roo.Element} boundEl The underlying element bound to this editor
13800              * @param {Mixed} value The field value being set
13801              */
13802         "beforestartedit" : true,
13803         /**
13804              * @event startedit
13805              * Fires when this editor is displayed
13806              * @param {Roo.Element} boundEl The underlying element bound to this editor
13807              * @param {Mixed} value The starting field value
13808              */
13809         "startedit" : true,
13810         /**
13811              * @event beforecomplete
13812              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13813              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13814              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13815              * event will not fire since no edit actually occurred.
13816              * @param {Editor} this
13817              * @param {Mixed} value The current field value
13818              * @param {Mixed} startValue The original field value
13819              */
13820         "beforecomplete" : true,
13821         /**
13822              * @event complete
13823              * Fires after editing is complete and any changed value has been written to the underlying field.
13824              * @param {Editor} this
13825              * @param {Mixed} value The current field value
13826              * @param {Mixed} startValue The original field value
13827              */
13828         "complete" : true,
13829         /**
13830          * @event specialkey
13831          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13832          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13833          * @param {Roo.form.Field} this
13834          * @param {Roo.EventObject} e The event object
13835          */
13836         "specialkey" : true
13837     });
13838 };
13839
13840 Roo.extend(Roo.Editor, Roo.Component, {
13841     /**
13842      * @cfg {Boolean/String} autosize
13843      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13844      * or "height" to adopt the height only (defaults to false)
13845      */
13846     /**
13847      * @cfg {Boolean} revertInvalid
13848      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13849      * validation fails (defaults to true)
13850      */
13851     /**
13852      * @cfg {Boolean} ignoreNoChange
13853      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13854      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13855      * will never be ignored.
13856      */
13857     /**
13858      * @cfg {Boolean} hideEl
13859      * False to keep the bound element visible while the editor is displayed (defaults to true)
13860      */
13861     /**
13862      * @cfg {Mixed} value
13863      * The data value of the underlying field (defaults to "")
13864      */
13865     value : "",
13866     /**
13867      * @cfg {String} alignment
13868      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13869      */
13870     alignment: "c-c?",
13871     /**
13872      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13873      * for bottom-right shadow (defaults to "frame")
13874      */
13875     shadow : "frame",
13876     /**
13877      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13878      */
13879     constrain : false,
13880     /**
13881      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13882      */
13883     completeOnEnter : false,
13884     /**
13885      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13886      */
13887     cancelOnEsc : false,
13888     /**
13889      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13890      */
13891     updateEl : false,
13892
13893     // private
13894     onRender : function(ct, position){
13895         this.el = new Roo.Layer({
13896             shadow: this.shadow,
13897             cls: "x-editor",
13898             parentEl : ct,
13899             shim : this.shim,
13900             shadowOffset:4,
13901             id: this.id,
13902             constrain: this.constrain
13903         });
13904         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13905         if(this.field.msgTarget != 'title'){
13906             this.field.msgTarget = 'qtip';
13907         }
13908         this.field.render(this.el);
13909         if(Roo.isGecko){
13910             this.field.el.dom.setAttribute('autocomplete', 'off');
13911         }
13912         this.field.on("specialkey", this.onSpecialKey, this);
13913         if(this.swallowKeys){
13914             this.field.el.swallowEvent(['keydown','keypress']);
13915         }
13916         this.field.show();
13917         this.field.on("blur", this.onBlur, this);
13918         if(this.field.grow){
13919             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13920         }
13921     },
13922
13923     onSpecialKey : function(field, e)
13924     {
13925         //Roo.log('editor onSpecialKey');
13926         if(this.completeOnEnter && e.getKey() == e.ENTER){
13927             e.stopEvent();
13928             this.completeEdit();
13929             return;
13930         }
13931         // do not fire special key otherwise it might hide close the editor...
13932         if(e.getKey() == e.ENTER){    
13933             return;
13934         }
13935         if(this.cancelOnEsc && e.getKey() == e.ESC){
13936             this.cancelEdit();
13937             return;
13938         } 
13939         this.fireEvent('specialkey', field, e);
13940     
13941     },
13942
13943     /**
13944      * Starts the editing process and shows the editor.
13945      * @param {String/HTMLElement/Element} el The element to edit
13946      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13947       * to the innerHTML of el.
13948      */
13949     startEdit : function(el, value){
13950         if(this.editing){
13951             this.completeEdit();
13952         }
13953         this.boundEl = Roo.get(el);
13954         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13955         if(!this.rendered){
13956             this.render(this.parentEl || document.body);
13957         }
13958         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13959             return;
13960         }
13961         this.startValue = v;
13962         this.field.setValue(v);
13963         if(this.autoSize){
13964             var sz = this.boundEl.getSize();
13965             switch(this.autoSize){
13966                 case "width":
13967                 this.setSize(sz.width,  "");
13968                 break;
13969                 case "height":
13970                 this.setSize("",  sz.height);
13971                 break;
13972                 default:
13973                 this.setSize(sz.width,  sz.height);
13974             }
13975         }
13976         this.el.alignTo(this.boundEl, this.alignment);
13977         this.editing = true;
13978         if(Roo.QuickTips){
13979             Roo.QuickTips.disable();
13980         }
13981         this.show();
13982     },
13983
13984     /**
13985      * Sets the height and width of this editor.
13986      * @param {Number} width The new width
13987      * @param {Number} height The new height
13988      */
13989     setSize : function(w, h){
13990         this.field.setSize(w, h);
13991         if(this.el){
13992             this.el.sync();
13993         }
13994     },
13995
13996     /**
13997      * Realigns the editor to the bound field based on the current alignment config value.
13998      */
13999     realign : function(){
14000         this.el.alignTo(this.boundEl, this.alignment);
14001     },
14002
14003     /**
14004      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14005      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14006      */
14007     completeEdit : function(remainVisible){
14008         if(!this.editing){
14009             return;
14010         }
14011         var v = this.getValue();
14012         if(this.revertInvalid !== false && !this.field.isValid()){
14013             v = this.startValue;
14014             this.cancelEdit(true);
14015         }
14016         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14017             this.editing = false;
14018             this.hide();
14019             return;
14020         }
14021         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14022             this.editing = false;
14023             if(this.updateEl && this.boundEl){
14024                 this.boundEl.update(v);
14025             }
14026             if(remainVisible !== true){
14027                 this.hide();
14028             }
14029             this.fireEvent("complete", this, v, this.startValue);
14030         }
14031     },
14032
14033     // private
14034     onShow : function(){
14035         this.el.show();
14036         if(this.hideEl !== false){
14037             this.boundEl.hide();
14038         }
14039         this.field.show();
14040         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14041             this.fixIEFocus = true;
14042             this.deferredFocus.defer(50, this);
14043         }else{
14044             this.field.focus();
14045         }
14046         this.fireEvent("startedit", this.boundEl, this.startValue);
14047     },
14048
14049     deferredFocus : function(){
14050         if(this.editing){
14051             this.field.focus();
14052         }
14053     },
14054
14055     /**
14056      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14057      * reverted to the original starting value.
14058      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14059      * cancel (defaults to false)
14060      */
14061     cancelEdit : function(remainVisible){
14062         if(this.editing){
14063             this.setValue(this.startValue);
14064             if(remainVisible !== true){
14065                 this.hide();
14066             }
14067         }
14068     },
14069
14070     // private
14071     onBlur : function(){
14072         if(this.allowBlur !== true && this.editing){
14073             this.completeEdit();
14074         }
14075     },
14076
14077     // private
14078     onHide : function(){
14079         if(this.editing){
14080             this.completeEdit();
14081             return;
14082         }
14083         this.field.blur();
14084         if(this.field.collapse){
14085             this.field.collapse();
14086         }
14087         this.el.hide();
14088         if(this.hideEl !== false){
14089             this.boundEl.show();
14090         }
14091         if(Roo.QuickTips){
14092             Roo.QuickTips.enable();
14093         }
14094     },
14095
14096     /**
14097      * Sets the data value of the editor
14098      * @param {Mixed} value Any valid value supported by the underlying field
14099      */
14100     setValue : function(v){
14101         this.field.setValue(v);
14102     },
14103
14104     /**
14105      * Gets the data value of the editor
14106      * @return {Mixed} The data value
14107      */
14108     getValue : function(){
14109         return this.field.getValue();
14110     }
14111 });/*
14112  * Based on:
14113  * Ext JS Library 1.1.1
14114  * Copyright(c) 2006-2007, Ext JS, LLC.
14115  *
14116  * Originally Released Under LGPL - original licence link has changed is not relivant.
14117  *
14118  * Fork - LGPL
14119  * <script type="text/javascript">
14120  */
14121  
14122 /**
14123  * @class Roo.BasicDialog
14124  * @extends Roo.util.Observable
14125  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14126  * <pre><code>
14127 var dlg = new Roo.BasicDialog("my-dlg", {
14128     height: 200,
14129     width: 300,
14130     minHeight: 100,
14131     minWidth: 150,
14132     modal: true,
14133     proxyDrag: true,
14134     shadow: true
14135 });
14136 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14137 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14138 dlg.addButton('Cancel', dlg.hide, dlg);
14139 dlg.show();
14140 </code></pre>
14141   <b>A Dialog should always be a direct child of the body element.</b>
14142  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14143  * @cfg {String} title Default text to display in the title bar (defaults to null)
14144  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14145  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14146  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14147  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14148  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14149  * (defaults to null with no animation)
14150  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14151  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14152  * property for valid values (defaults to 'all')
14153  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14154  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14155  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14156  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14157  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14158  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14159  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14160  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14161  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14162  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14163  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14164  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14165  * draggable = true (defaults to false)
14166  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14167  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14168  * shadow (defaults to false)
14169  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14170  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14171  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14172  * @cfg {Array} buttons Array of buttons
14173  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14174  * @constructor
14175  * Create a new BasicDialog.
14176  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14177  * @param {Object} config Configuration options
14178  */
14179 Roo.BasicDialog = function(el, config){
14180     this.el = Roo.get(el);
14181     var dh = Roo.DomHelper;
14182     if(!this.el && config && config.autoCreate){
14183         if(typeof config.autoCreate == "object"){
14184             if(!config.autoCreate.id){
14185                 config.autoCreate.id = el;
14186             }
14187             this.el = dh.append(document.body,
14188                         config.autoCreate, true);
14189         }else{
14190             this.el = dh.append(document.body,
14191                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14192         }
14193     }
14194     el = this.el;
14195     el.setDisplayed(true);
14196     el.hide = this.hideAction;
14197     this.id = el.id;
14198     el.addClass("x-dlg");
14199
14200     Roo.apply(this, config);
14201
14202     this.proxy = el.createProxy("x-dlg-proxy");
14203     this.proxy.hide = this.hideAction;
14204     this.proxy.setOpacity(.5);
14205     this.proxy.hide();
14206
14207     if(config.width){
14208         el.setWidth(config.width);
14209     }
14210     if(config.height){
14211         el.setHeight(config.height);
14212     }
14213     this.size = el.getSize();
14214     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14215         this.xy = [config.x,config.y];
14216     }else{
14217         this.xy = el.getCenterXY(true);
14218     }
14219     /** The header element @type Roo.Element */
14220     this.header = el.child("> .x-dlg-hd");
14221     /** The body element @type Roo.Element */
14222     this.body = el.child("> .x-dlg-bd");
14223     /** The footer element @type Roo.Element */
14224     this.footer = el.child("> .x-dlg-ft");
14225
14226     if(!this.header){
14227         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14228     }
14229     if(!this.body){
14230         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14231     }
14232
14233     this.header.unselectable();
14234     if(this.title){
14235         this.header.update(this.title);
14236     }
14237     // this element allows the dialog to be focused for keyboard event
14238     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14239     this.focusEl.swallowEvent("click", true);
14240
14241     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14242
14243     // wrap the body and footer for special rendering
14244     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14245     if(this.footer){
14246         this.bwrap.dom.appendChild(this.footer.dom);
14247     }
14248
14249     this.bg = this.el.createChild({
14250         tag: "div", cls:"x-dlg-bg",
14251         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14252     });
14253     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14254
14255
14256     if(this.autoScroll !== false && !this.autoTabs){
14257         this.body.setStyle("overflow", "auto");
14258     }
14259
14260     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14261
14262     if(this.closable !== false){
14263         this.el.addClass("x-dlg-closable");
14264         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14265         this.close.on("click", this.closeClick, this);
14266         this.close.addClassOnOver("x-dlg-close-over");
14267     }
14268     if(this.collapsible !== false){
14269         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14270         this.collapseBtn.on("click", this.collapseClick, this);
14271         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14272         this.header.on("dblclick", this.collapseClick, this);
14273     }
14274     if(this.resizable !== false){
14275         this.el.addClass("x-dlg-resizable");
14276         this.resizer = new Roo.Resizable(el, {
14277             minWidth: this.minWidth || 80,
14278             minHeight:this.minHeight || 80,
14279             handles: this.resizeHandles || "all",
14280             pinned: true
14281         });
14282         this.resizer.on("beforeresize", this.beforeResize, this);
14283         this.resizer.on("resize", this.onResize, this);
14284     }
14285     if(this.draggable !== false){
14286         el.addClass("x-dlg-draggable");
14287         if (!this.proxyDrag) {
14288             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14289         }
14290         else {
14291             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14292         }
14293         dd.setHandleElId(this.header.id);
14294         dd.endDrag = this.endMove.createDelegate(this);
14295         dd.startDrag = this.startMove.createDelegate(this);
14296         dd.onDrag = this.onDrag.createDelegate(this);
14297         dd.scroll = false;
14298         this.dd = dd;
14299     }
14300     if(this.modal){
14301         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14302         this.mask.enableDisplayMode("block");
14303         this.mask.hide();
14304         this.el.addClass("x-dlg-modal");
14305     }
14306     if(this.shadow){
14307         this.shadow = new Roo.Shadow({
14308             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14309             offset : this.shadowOffset
14310         });
14311     }else{
14312         this.shadowOffset = 0;
14313     }
14314     if(Roo.useShims && this.shim !== false){
14315         this.shim = this.el.createShim();
14316         this.shim.hide = this.hideAction;
14317         this.shim.hide();
14318     }else{
14319         this.shim = false;
14320     }
14321     if(this.autoTabs){
14322         this.initTabs();
14323     }
14324     if (this.buttons) { 
14325         var bts= this.buttons;
14326         this.buttons = [];
14327         Roo.each(bts, function(b) {
14328             this.addButton(b);
14329         }, this);
14330     }
14331     
14332     
14333     this.addEvents({
14334         /**
14335          * @event keydown
14336          * Fires when a key is pressed
14337          * @param {Roo.BasicDialog} this
14338          * @param {Roo.EventObject} e
14339          */
14340         "keydown" : true,
14341         /**
14342          * @event move
14343          * Fires when this dialog is moved by the user.
14344          * @param {Roo.BasicDialog} this
14345          * @param {Number} x The new page X
14346          * @param {Number} y The new page Y
14347          */
14348         "move" : true,
14349         /**
14350          * @event resize
14351          * Fires when this dialog is resized by the user.
14352          * @param {Roo.BasicDialog} this
14353          * @param {Number} width The new width
14354          * @param {Number} height The new height
14355          */
14356         "resize" : true,
14357         /**
14358          * @event beforehide
14359          * Fires before this dialog is hidden.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "beforehide" : true,
14363         /**
14364          * @event hide
14365          * Fires when this dialog is hidden.
14366          * @param {Roo.BasicDialog} this
14367          */
14368         "hide" : true,
14369         /**
14370          * @event beforeshow
14371          * Fires before this dialog is shown.
14372          * @param {Roo.BasicDialog} this
14373          */
14374         "beforeshow" : true,
14375         /**
14376          * @event show
14377          * Fires when this dialog is shown.
14378          * @param {Roo.BasicDialog} this
14379          */
14380         "show" : true
14381     });
14382     el.on("keydown", this.onKeyDown, this);
14383     el.on("mousedown", this.toFront, this);
14384     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14385     this.el.hide();
14386     Roo.DialogManager.register(this);
14387     Roo.BasicDialog.superclass.constructor.call(this);
14388 };
14389
14390 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14391     shadowOffset: Roo.isIE ? 6 : 5,
14392     minHeight: 80,
14393     minWidth: 200,
14394     minButtonWidth: 75,
14395     defaultButton: null,
14396     buttonAlign: "right",
14397     tabTag: 'div',
14398     firstShow: true,
14399
14400     /**
14401      * Sets the dialog title text
14402      * @param {String} text The title text to display
14403      * @return {Roo.BasicDialog} this
14404      */
14405     setTitle : function(text){
14406         this.header.update(text);
14407         return this;
14408     },
14409
14410     // private
14411     closeClick : function(){
14412         this.hide();
14413     },
14414
14415     // private
14416     collapseClick : function(){
14417         this[this.collapsed ? "expand" : "collapse"]();
14418     },
14419
14420     /**
14421      * Collapses the dialog to its minimized state (only the title bar is visible).
14422      * Equivalent to the user clicking the collapse dialog button.
14423      */
14424     collapse : function(){
14425         if(!this.collapsed){
14426             this.collapsed = true;
14427             this.el.addClass("x-dlg-collapsed");
14428             this.restoreHeight = this.el.getHeight();
14429             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14430         }
14431     },
14432
14433     /**
14434      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14435      * clicking the expand dialog button.
14436      */
14437     expand : function(){
14438         if(this.collapsed){
14439             this.collapsed = false;
14440             this.el.removeClass("x-dlg-collapsed");
14441             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14442         }
14443     },
14444
14445     /**
14446      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14447      * @return {Roo.TabPanel} The tabs component
14448      */
14449     initTabs : function(){
14450         var tabs = this.getTabs();
14451         while(tabs.getTab(0)){
14452             tabs.removeTab(0);
14453         }
14454         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14455             var dom = el.dom;
14456             tabs.addTab(Roo.id(dom), dom.title);
14457             dom.title = "";
14458         });
14459         tabs.activate(0);
14460         return tabs;
14461     },
14462
14463     // private
14464     beforeResize : function(){
14465         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14466     },
14467
14468     // private
14469     onResize : function(){
14470         this.refreshSize();
14471         this.syncBodyHeight();
14472         this.adjustAssets();
14473         this.focus();
14474         this.fireEvent("resize", this, this.size.width, this.size.height);
14475     },
14476
14477     // private
14478     onKeyDown : function(e){
14479         if(this.isVisible()){
14480             this.fireEvent("keydown", this, e);
14481         }
14482     },
14483
14484     /**
14485      * Resizes the dialog.
14486      * @param {Number} width
14487      * @param {Number} height
14488      * @return {Roo.BasicDialog} this
14489      */
14490     resizeTo : function(width, height){
14491         this.el.setSize(width, height);
14492         this.size = {width: width, height: height};
14493         this.syncBodyHeight();
14494         if(this.fixedcenter){
14495             this.center();
14496         }
14497         if(this.isVisible()){
14498             this.constrainXY();
14499             this.adjustAssets();
14500         }
14501         this.fireEvent("resize", this, width, height);
14502         return this;
14503     },
14504
14505
14506     /**
14507      * Resizes the dialog to fit the specified content size.
14508      * @param {Number} width
14509      * @param {Number} height
14510      * @return {Roo.BasicDialog} this
14511      */
14512     setContentSize : function(w, h){
14513         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14514         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14515         //if(!this.el.isBorderBox()){
14516             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14517             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14518         //}
14519         if(this.tabs){
14520             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14521             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14522         }
14523         this.resizeTo(w, h);
14524         return this;
14525     },
14526
14527     /**
14528      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14529      * executed in response to a particular key being pressed while the dialog is active.
14530      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14531      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14532      * @param {Function} fn The function to call
14533      * @param {Object} scope (optional) The scope of the function
14534      * @return {Roo.BasicDialog} this
14535      */
14536     addKeyListener : function(key, fn, scope){
14537         var keyCode, shift, ctrl, alt;
14538         if(typeof key == "object" && !(key instanceof Array)){
14539             keyCode = key["key"];
14540             shift = key["shift"];
14541             ctrl = key["ctrl"];
14542             alt = key["alt"];
14543         }else{
14544             keyCode = key;
14545         }
14546         var handler = function(dlg, e){
14547             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14548                 var k = e.getKey();
14549                 if(keyCode instanceof Array){
14550                     for(var i = 0, len = keyCode.length; i < len; i++){
14551                         if(keyCode[i] == k){
14552                           fn.call(scope || window, dlg, k, e);
14553                           return;
14554                         }
14555                     }
14556                 }else{
14557                     if(k == keyCode){
14558                         fn.call(scope || window, dlg, k, e);
14559                     }
14560                 }
14561             }
14562         };
14563         this.on("keydown", handler);
14564         return this;
14565     },
14566
14567     /**
14568      * Returns the TabPanel component (creates it if it doesn't exist).
14569      * Note: If you wish to simply check for the existence of tabs without creating them,
14570      * check for a null 'tabs' property.
14571      * @return {Roo.TabPanel} The tabs component
14572      */
14573     getTabs : function(){
14574         if(!this.tabs){
14575             this.el.addClass("x-dlg-auto-tabs");
14576             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14577             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14578         }
14579         return this.tabs;
14580     },
14581
14582     /**
14583      * Adds a button to the footer section of the dialog.
14584      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14585      * object or a valid Roo.DomHelper element config
14586      * @param {Function} handler The function called when the button is clicked
14587      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14588      * @return {Roo.Button} The new button
14589      */
14590     addButton : function(config, handler, scope){
14591         var dh = Roo.DomHelper;
14592         if(!this.footer){
14593             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14594         }
14595         if(!this.btnContainer){
14596             var tb = this.footer.createChild({
14597
14598                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14599                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14600             }, null, true);
14601             this.btnContainer = tb.firstChild.firstChild.firstChild;
14602         }
14603         var bconfig = {
14604             handler: handler,
14605             scope: scope,
14606             minWidth: this.minButtonWidth,
14607             hideParent:true
14608         };
14609         if(typeof config == "string"){
14610             bconfig.text = config;
14611         }else{
14612             if(config.tag){
14613                 bconfig.dhconfig = config;
14614             }else{
14615                 Roo.apply(bconfig, config);
14616             }
14617         }
14618         var fc = false;
14619         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14620             bconfig.position = Math.max(0, bconfig.position);
14621             fc = this.btnContainer.childNodes[bconfig.position];
14622         }
14623          
14624         var btn = new Roo.Button(
14625             fc ? 
14626                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14627                 : this.btnContainer.appendChild(document.createElement("td")),
14628             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14629             bconfig
14630         );
14631         this.syncBodyHeight();
14632         if(!this.buttons){
14633             /**
14634              * Array of all the buttons that have been added to this dialog via addButton
14635              * @type Array
14636              */
14637             this.buttons = [];
14638         }
14639         this.buttons.push(btn);
14640         return btn;
14641     },
14642
14643     /**
14644      * Sets the default button to be focused when the dialog is displayed.
14645      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14646      * @return {Roo.BasicDialog} this
14647      */
14648     setDefaultButton : function(btn){
14649         this.defaultButton = btn;
14650         return this;
14651     },
14652
14653     // private
14654     getHeaderFooterHeight : function(safe){
14655         var height = 0;
14656         if(this.header){
14657            height += this.header.getHeight();
14658         }
14659         if(this.footer){
14660            var fm = this.footer.getMargins();
14661             height += (this.footer.getHeight()+fm.top+fm.bottom);
14662         }
14663         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14664         height += this.centerBg.getPadding("tb");
14665         return height;
14666     },
14667
14668     // private
14669     syncBodyHeight : function(){
14670         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14671         var height = this.size.height - this.getHeaderFooterHeight(false);
14672         bd.setHeight(height-bd.getMargins("tb"));
14673         var hh = this.header.getHeight();
14674         var h = this.size.height-hh;
14675         cb.setHeight(h);
14676         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14677         bw.setHeight(h-cb.getPadding("tb"));
14678         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14679         bd.setWidth(bw.getWidth(true));
14680         if(this.tabs){
14681             this.tabs.syncHeight();
14682             if(Roo.isIE){
14683                 this.tabs.el.repaint();
14684             }
14685         }
14686     },
14687
14688     /**
14689      * Restores the previous state of the dialog if Roo.state is configured.
14690      * @return {Roo.BasicDialog} this
14691      */
14692     restoreState : function(){
14693         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14694         if(box && box.width){
14695             this.xy = [box.x, box.y];
14696             this.resizeTo(box.width, box.height);
14697         }
14698         return this;
14699     },
14700
14701     // private
14702     beforeShow : function(){
14703         this.expand();
14704         if(this.fixedcenter){
14705             this.xy = this.el.getCenterXY(true);
14706         }
14707         if(this.modal){
14708             Roo.get(document.body).addClass("x-body-masked");
14709             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14710             this.mask.show();
14711         }
14712         this.constrainXY();
14713     },
14714
14715     // private
14716     animShow : function(){
14717         var b = Roo.get(this.animateTarget).getBox();
14718         this.proxy.setSize(b.width, b.height);
14719         this.proxy.setLocation(b.x, b.y);
14720         this.proxy.show();
14721         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14722                     true, .35, this.showEl.createDelegate(this));
14723     },
14724
14725     /**
14726      * Shows the dialog.
14727      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14728      * @return {Roo.BasicDialog} this
14729      */
14730     show : function(animateTarget){
14731         if (this.fireEvent("beforeshow", this) === false){
14732             return;
14733         }
14734         if(this.syncHeightBeforeShow){
14735             this.syncBodyHeight();
14736         }else if(this.firstShow){
14737             this.firstShow = false;
14738             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14739         }
14740         this.animateTarget = animateTarget || this.animateTarget;
14741         if(!this.el.isVisible()){
14742             this.beforeShow();
14743             if(this.animateTarget && Roo.get(this.animateTarget)){
14744                 this.animShow();
14745             }else{
14746                 this.showEl();
14747             }
14748         }
14749         return this;
14750     },
14751
14752     // private
14753     showEl : function(){
14754         this.proxy.hide();
14755         this.el.setXY(this.xy);
14756         this.el.show();
14757         this.adjustAssets(true);
14758         this.toFront();
14759         this.focus();
14760         // IE peekaboo bug - fix found by Dave Fenwick
14761         if(Roo.isIE){
14762             this.el.repaint();
14763         }
14764         this.fireEvent("show", this);
14765     },
14766
14767     /**
14768      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14769      * dialog itself will receive focus.
14770      */
14771     focus : function(){
14772         if(this.defaultButton){
14773             this.defaultButton.focus();
14774         }else{
14775             this.focusEl.focus();
14776         }
14777     },
14778
14779     // private
14780     constrainXY : function(){
14781         if(this.constraintoviewport !== false){
14782             if(!this.viewSize){
14783                 if(this.container){
14784                     var s = this.container.getSize();
14785                     this.viewSize = [s.width, s.height];
14786                 }else{
14787                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14788                 }
14789             }
14790             var s = Roo.get(this.container||document).getScroll();
14791
14792             var x = this.xy[0], y = this.xy[1];
14793             var w = this.size.width, h = this.size.height;
14794             var vw = this.viewSize[0], vh = this.viewSize[1];
14795             // only move it if it needs it
14796             var moved = false;
14797             // first validate right/bottom
14798             if(x + w > vw+s.left){
14799                 x = vw - w;
14800                 moved = true;
14801             }
14802             if(y + h > vh+s.top){
14803                 y = vh - h;
14804                 moved = true;
14805             }
14806             // then make sure top/left isn't negative
14807             if(x < s.left){
14808                 x = s.left;
14809                 moved = true;
14810             }
14811             if(y < s.top){
14812                 y = s.top;
14813                 moved = true;
14814             }
14815             if(moved){
14816                 // cache xy
14817                 this.xy = [x, y];
14818                 if(this.isVisible()){
14819                     this.el.setLocation(x, y);
14820                     this.adjustAssets();
14821                 }
14822             }
14823         }
14824     },
14825
14826     // private
14827     onDrag : function(){
14828         if(!this.proxyDrag){
14829             this.xy = this.el.getXY();
14830             this.adjustAssets();
14831         }
14832     },
14833
14834     // private
14835     adjustAssets : function(doShow){
14836         var x = this.xy[0], y = this.xy[1];
14837         var w = this.size.width, h = this.size.height;
14838         if(doShow === true){
14839             if(this.shadow){
14840                 this.shadow.show(this.el);
14841             }
14842             if(this.shim){
14843                 this.shim.show();
14844             }
14845         }
14846         if(this.shadow && this.shadow.isVisible()){
14847             this.shadow.show(this.el);
14848         }
14849         if(this.shim && this.shim.isVisible()){
14850             this.shim.setBounds(x, y, w, h);
14851         }
14852     },
14853
14854     // private
14855     adjustViewport : function(w, h){
14856         if(!w || !h){
14857             w = Roo.lib.Dom.getViewWidth();
14858             h = Roo.lib.Dom.getViewHeight();
14859         }
14860         // cache the size
14861         this.viewSize = [w, h];
14862         if(this.modal && this.mask.isVisible()){
14863             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14864             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14865         }
14866         if(this.isVisible()){
14867             this.constrainXY();
14868         }
14869     },
14870
14871     /**
14872      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14873      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14874      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14875      */
14876     destroy : function(removeEl){
14877         if(this.isVisible()){
14878             this.animateTarget = null;
14879             this.hide();
14880         }
14881         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14882         if(this.tabs){
14883             this.tabs.destroy(removeEl);
14884         }
14885         Roo.destroy(
14886              this.shim,
14887              this.proxy,
14888              this.resizer,
14889              this.close,
14890              this.mask
14891         );
14892         if(this.dd){
14893             this.dd.unreg();
14894         }
14895         if(this.buttons){
14896            for(var i = 0, len = this.buttons.length; i < len; i++){
14897                this.buttons[i].destroy();
14898            }
14899         }
14900         this.el.removeAllListeners();
14901         if(removeEl === true){
14902             this.el.update("");
14903             this.el.remove();
14904         }
14905         Roo.DialogManager.unregister(this);
14906     },
14907
14908     // private
14909     startMove : function(){
14910         if(this.proxyDrag){
14911             this.proxy.show();
14912         }
14913         if(this.constraintoviewport !== false){
14914             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14915         }
14916     },
14917
14918     // private
14919     endMove : function(){
14920         if(!this.proxyDrag){
14921             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14922         }else{
14923             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14924             this.proxy.hide();
14925         }
14926         this.refreshSize();
14927         this.adjustAssets();
14928         this.focus();
14929         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14930     },
14931
14932     /**
14933      * Brings this dialog to the front of any other visible dialogs
14934      * @return {Roo.BasicDialog} this
14935      */
14936     toFront : function(){
14937         Roo.DialogManager.bringToFront(this);
14938         return this;
14939     },
14940
14941     /**
14942      * Sends this dialog to the back (under) of any other visible dialogs
14943      * @return {Roo.BasicDialog} this
14944      */
14945     toBack : function(){
14946         Roo.DialogManager.sendToBack(this);
14947         return this;
14948     },
14949
14950     /**
14951      * Centers this dialog in the viewport
14952      * @return {Roo.BasicDialog} this
14953      */
14954     center : function(){
14955         var xy = this.el.getCenterXY(true);
14956         this.moveTo(xy[0], xy[1]);
14957         return this;
14958     },
14959
14960     /**
14961      * Moves the dialog's top-left corner to the specified point
14962      * @param {Number} x
14963      * @param {Number} y
14964      * @return {Roo.BasicDialog} this
14965      */
14966     moveTo : function(x, y){
14967         this.xy = [x,y];
14968         if(this.isVisible()){
14969             this.el.setXY(this.xy);
14970             this.adjustAssets();
14971         }
14972         return this;
14973     },
14974
14975     /**
14976      * Aligns the dialog to the specified element
14977      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14978      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14979      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14980      * @return {Roo.BasicDialog} this
14981      */
14982     alignTo : function(element, position, offsets){
14983         this.xy = this.el.getAlignToXY(element, position, offsets);
14984         if(this.isVisible()){
14985             this.el.setXY(this.xy);
14986             this.adjustAssets();
14987         }
14988         return this;
14989     },
14990
14991     /**
14992      * Anchors an element to another element and realigns it when the window is resized.
14993      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14994      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14995      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14996      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14997      * is a number, it is used as the buffer delay (defaults to 50ms).
14998      * @return {Roo.BasicDialog} this
14999      */
15000     anchorTo : function(el, alignment, offsets, monitorScroll){
15001         var action = function(){
15002             this.alignTo(el, alignment, offsets);
15003         };
15004         Roo.EventManager.onWindowResize(action, this);
15005         var tm = typeof monitorScroll;
15006         if(tm != 'undefined'){
15007             Roo.EventManager.on(window, 'scroll', action, this,
15008                 {buffer: tm == 'number' ? monitorScroll : 50});
15009         }
15010         action.call(this);
15011         return this;
15012     },
15013
15014     /**
15015      * Returns true if the dialog is visible
15016      * @return {Boolean}
15017      */
15018     isVisible : function(){
15019         return this.el.isVisible();
15020     },
15021
15022     // private
15023     animHide : function(callback){
15024         var b = Roo.get(this.animateTarget).getBox();
15025         this.proxy.show();
15026         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15027         this.el.hide();
15028         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15029                     this.hideEl.createDelegate(this, [callback]));
15030     },
15031
15032     /**
15033      * Hides the dialog.
15034      * @param {Function} callback (optional) Function to call when the dialog is hidden
15035      * @return {Roo.BasicDialog} this
15036      */
15037     hide : function(callback){
15038         if (this.fireEvent("beforehide", this) === false){
15039             return;
15040         }
15041         if(this.shadow){
15042             this.shadow.hide();
15043         }
15044         if(this.shim) {
15045           this.shim.hide();
15046         }
15047         // sometimes animateTarget seems to get set.. causing problems...
15048         // this just double checks..
15049         if(this.animateTarget && Roo.get(this.animateTarget)) {
15050            this.animHide(callback);
15051         }else{
15052             this.el.hide();
15053             this.hideEl(callback);
15054         }
15055         return this;
15056     },
15057
15058     // private
15059     hideEl : function(callback){
15060         this.proxy.hide();
15061         if(this.modal){
15062             this.mask.hide();
15063             Roo.get(document.body).removeClass("x-body-masked");
15064         }
15065         this.fireEvent("hide", this);
15066         if(typeof callback == "function"){
15067             callback();
15068         }
15069     },
15070
15071     // private
15072     hideAction : function(){
15073         this.setLeft("-10000px");
15074         this.setTop("-10000px");
15075         this.setStyle("visibility", "hidden");
15076     },
15077
15078     // private
15079     refreshSize : function(){
15080         this.size = this.el.getSize();
15081         this.xy = this.el.getXY();
15082         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15083     },
15084
15085     // private
15086     // z-index is managed by the DialogManager and may be overwritten at any time
15087     setZIndex : function(index){
15088         if(this.modal){
15089             this.mask.setStyle("z-index", index);
15090         }
15091         if(this.shim){
15092             this.shim.setStyle("z-index", ++index);
15093         }
15094         if(this.shadow){
15095             this.shadow.setZIndex(++index);
15096         }
15097         this.el.setStyle("z-index", ++index);
15098         if(this.proxy){
15099             this.proxy.setStyle("z-index", ++index);
15100         }
15101         if(this.resizer){
15102             this.resizer.proxy.setStyle("z-index", ++index);
15103         }
15104
15105         this.lastZIndex = index;
15106     },
15107
15108     /**
15109      * Returns the element for this dialog
15110      * @return {Roo.Element} The underlying dialog Element
15111      */
15112     getEl : function(){
15113         return this.el;
15114     }
15115 });
15116
15117 /**
15118  * @class Roo.DialogManager
15119  * Provides global access to BasicDialogs that have been created and
15120  * support for z-indexing (layering) multiple open dialogs.
15121  */
15122 Roo.DialogManager = function(){
15123     var list = {};
15124     var accessList = [];
15125     var front = null;
15126
15127     // private
15128     var sortDialogs = function(d1, d2){
15129         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15130     };
15131
15132     // private
15133     var orderDialogs = function(){
15134         accessList.sort(sortDialogs);
15135         var seed = Roo.DialogManager.zseed;
15136         for(var i = 0, len = accessList.length; i < len; i++){
15137             var dlg = accessList[i];
15138             if(dlg){
15139                 dlg.setZIndex(seed + (i*10));
15140             }
15141         }
15142     };
15143
15144     return {
15145         /**
15146          * The starting z-index for BasicDialogs (defaults to 9000)
15147          * @type Number The z-index value
15148          */
15149         zseed : 9000,
15150
15151         // private
15152         register : function(dlg){
15153             list[dlg.id] = dlg;
15154             accessList.push(dlg);
15155         },
15156
15157         // private
15158         unregister : function(dlg){
15159             delete list[dlg.id];
15160             var i=0;
15161             var len=0;
15162             if(!accessList.indexOf){
15163                 for(  i = 0, len = accessList.length; i < len; i++){
15164                     if(accessList[i] == dlg){
15165                         accessList.splice(i, 1);
15166                         return;
15167                     }
15168                 }
15169             }else{
15170                  i = accessList.indexOf(dlg);
15171                 if(i != -1){
15172                     accessList.splice(i, 1);
15173                 }
15174             }
15175         },
15176
15177         /**
15178          * Gets a registered dialog by id
15179          * @param {String/Object} id The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         get : function(id){
15183             return typeof id == "object" ? id : list[id];
15184         },
15185
15186         /**
15187          * Brings the specified dialog to the front
15188          * @param {String/Object} dlg The id of the dialog or a dialog
15189          * @return {Roo.BasicDialog} this
15190          */
15191         bringToFront : function(dlg){
15192             dlg = this.get(dlg);
15193             if(dlg != front){
15194                 front = dlg;
15195                 dlg._lastAccess = new Date().getTime();
15196                 orderDialogs();
15197             }
15198             return dlg;
15199         },
15200
15201         /**
15202          * Sends the specified dialog to the back
15203          * @param {String/Object} dlg The id of the dialog or a dialog
15204          * @return {Roo.BasicDialog} this
15205          */
15206         sendToBack : function(dlg){
15207             dlg = this.get(dlg);
15208             dlg._lastAccess = -(new Date().getTime());
15209             orderDialogs();
15210             return dlg;
15211         },
15212
15213         /**
15214          * Hides all dialogs
15215          */
15216         hideAll : function(){
15217             for(var id in list){
15218                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15219                     list[id].hide();
15220                 }
15221             }
15222         }
15223     };
15224 }();
15225
15226 /**
15227  * @class Roo.LayoutDialog
15228  * @extends Roo.BasicDialog
15229  * Dialog which provides adjustments for working with a layout in a Dialog.
15230  * Add your necessary layout config options to the dialog's config.<br>
15231  * Example usage (including a nested layout):
15232  * <pre><code>
15233 if(!dialog){
15234     dialog = new Roo.LayoutDialog("download-dlg", {
15235         modal: true,
15236         width:600,
15237         height:450,
15238         shadow:true,
15239         minWidth:500,
15240         minHeight:350,
15241         autoTabs:true,
15242         proxyDrag:true,
15243         // layout config merges with the dialog config
15244         center:{
15245             tabPosition: "top",
15246             alwaysShowTabs: true
15247         }
15248     });
15249     dialog.addKeyListener(27, dialog.hide, dialog);
15250     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15251     dialog.addButton("Build It!", this.getDownload, this);
15252
15253     // we can even add nested layouts
15254     var innerLayout = new Roo.BorderLayout("dl-inner", {
15255         east: {
15256             initialSize: 200,
15257             autoScroll:true,
15258             split:true
15259         },
15260         center: {
15261             autoScroll:true
15262         }
15263     });
15264     innerLayout.beginUpdate();
15265     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15266     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15267     innerLayout.endUpdate(true);
15268
15269     var layout = dialog.getLayout();
15270     layout.beginUpdate();
15271     layout.add("center", new Roo.ContentPanel("standard-panel",
15272                         {title: "Download the Source", fitToFrame:true}));
15273     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15274                {title: "Build your own roo.js"}));
15275     layout.getRegion("center").showPanel(sp);
15276     layout.endUpdate();
15277 }
15278 </code></pre>
15279     * @constructor
15280     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15281     * @param {Object} config configuration options
15282   */
15283 Roo.LayoutDialog = function(el, cfg){
15284     
15285     var config=  cfg;
15286     if (typeof(cfg) == 'undefined') {
15287         config = Roo.apply({}, el);
15288         // not sure why we use documentElement here.. - it should always be body.
15289         // IE7 borks horribly if we use documentElement.
15290         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15291         //config.autoCreate = true;
15292     }
15293     
15294     
15295     config.autoTabs = false;
15296     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15297     this.body.setStyle({overflow:"hidden", position:"relative"});
15298     this.layout = new Roo.BorderLayout(this.body.dom, config);
15299     this.layout.monitorWindowResize = false;
15300     this.el.addClass("x-dlg-auto-layout");
15301     // fix case when center region overwrites center function
15302     this.center = Roo.BasicDialog.prototype.center;
15303     this.on("show", this.layout.layout, this.layout, true);
15304     if (config.items) {
15305         var xitems = config.items;
15306         delete config.items;
15307         Roo.each(xitems, this.addxtype, this);
15308     }
15309     
15310     
15311 };
15312 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15313     /**
15314      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15315      * @deprecated
15316      */
15317     endUpdate : function(){
15318         this.layout.endUpdate();
15319     },
15320
15321     /**
15322      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15323      *  @deprecated
15324      */
15325     beginUpdate : function(){
15326         this.layout.beginUpdate();
15327     },
15328
15329     /**
15330      * Get the BorderLayout for this dialog
15331      * @return {Roo.BorderLayout}
15332      */
15333     getLayout : function(){
15334         return this.layout;
15335     },
15336
15337     showEl : function(){
15338         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15339         if(Roo.isIE7){
15340             this.layout.layout();
15341         }
15342     },
15343
15344     // private
15345     // Use the syncHeightBeforeShow config option to control this automatically
15346     syncBodyHeight : function(){
15347         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15348         if(this.layout){this.layout.layout();}
15349     },
15350     
15351       /**
15352      * Add an xtype element (actually adds to the layout.)
15353      * @return {Object} xdata xtype object data.
15354      */
15355     
15356     addxtype : function(c) {
15357         return this.layout.addxtype(c);
15358     }
15359 });/*
15360  * Based on:
15361  * Ext JS Library 1.1.1
15362  * Copyright(c) 2006-2007, Ext JS, LLC.
15363  *
15364  * Originally Released Under LGPL - original licence link has changed is not relivant.
15365  *
15366  * Fork - LGPL
15367  * <script type="text/javascript">
15368  */
15369  
15370 /**
15371  * @class Roo.MessageBox
15372  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15373  * Example usage:
15374  *<pre><code>
15375 // Basic alert:
15376 Roo.Msg.alert('Status', 'Changes saved successfully.');
15377
15378 // Prompt for user data:
15379 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15380     if (btn == 'ok'){
15381         // process text value...
15382     }
15383 });
15384
15385 // Show a dialog using config options:
15386 Roo.Msg.show({
15387    title:'Save Changes?',
15388    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15389    buttons: Roo.Msg.YESNOCANCEL,
15390    fn: processResult,
15391    animEl: 'elId'
15392 });
15393 </code></pre>
15394  * @singleton
15395  */
15396 Roo.MessageBox = function(){
15397     var dlg, opt, mask, waitTimer;
15398     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15399     var buttons, activeTextEl, bwidth;
15400
15401     // private
15402     var handleButton = function(button){
15403         dlg.hide();
15404         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15405     };
15406
15407     // private
15408     var handleHide = function(){
15409         if(opt && opt.cls){
15410             dlg.el.removeClass(opt.cls);
15411         }
15412         if(waitTimer){
15413             Roo.TaskMgr.stop(waitTimer);
15414             waitTimer = null;
15415         }
15416     };
15417
15418     // private
15419     var updateButtons = function(b){
15420         var width = 0;
15421         if(!b){
15422             buttons["ok"].hide();
15423             buttons["cancel"].hide();
15424             buttons["yes"].hide();
15425             buttons["no"].hide();
15426             dlg.footer.dom.style.display = 'none';
15427             return width;
15428         }
15429         dlg.footer.dom.style.display = '';
15430         for(var k in buttons){
15431             if(typeof buttons[k] != "function"){
15432                 if(b[k]){
15433                     buttons[k].show();
15434                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15435                     width += buttons[k].el.getWidth()+15;
15436                 }else{
15437                     buttons[k].hide();
15438                 }
15439             }
15440         }
15441         return width;
15442     };
15443
15444     // private
15445     var handleEsc = function(d, k, e){
15446         if(opt && opt.closable !== false){
15447             dlg.hide();
15448         }
15449         if(e){
15450             e.stopEvent();
15451         }
15452     };
15453
15454     return {
15455         /**
15456          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15457          * @return {Roo.BasicDialog} The BasicDialog element
15458          */
15459         getDialog : function(){
15460            if(!dlg){
15461                 dlg = new Roo.BasicDialog("x-msg-box", {
15462                     autoCreate : true,
15463                     shadow: true,
15464                     draggable: true,
15465                     resizable:false,
15466                     constraintoviewport:false,
15467                     fixedcenter:true,
15468                     collapsible : false,
15469                     shim:true,
15470                     modal: true,
15471                     width:400, height:100,
15472                     buttonAlign:"center",
15473                     closeClick : function(){
15474                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15475                             handleButton("no");
15476                         }else{
15477                             handleButton("cancel");
15478                         }
15479                     }
15480                 });
15481                 dlg.on("hide", handleHide);
15482                 mask = dlg.mask;
15483                 dlg.addKeyListener(27, handleEsc);
15484                 buttons = {};
15485                 var bt = this.buttonText;
15486                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15487                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15488                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15489                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15490                 bodyEl = dlg.body.createChild({
15491
15492                     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>'
15493                 });
15494                 msgEl = bodyEl.dom.firstChild;
15495                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15496                 textboxEl.enableDisplayMode();
15497                 textboxEl.addKeyListener([10,13], function(){
15498                     if(dlg.isVisible() && opt && opt.buttons){
15499                         if(opt.buttons.ok){
15500                             handleButton("ok");
15501                         }else if(opt.buttons.yes){
15502                             handleButton("yes");
15503                         }
15504                     }
15505                 });
15506                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15507                 textareaEl.enableDisplayMode();
15508                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15509                 progressEl.enableDisplayMode();
15510                 var pf = progressEl.dom.firstChild;
15511                 if (pf) {
15512                     pp = Roo.get(pf.firstChild);
15513                     pp.setHeight(pf.offsetHeight);
15514                 }
15515                 
15516             }
15517             return dlg;
15518         },
15519
15520         /**
15521          * Updates the message box body text
15522          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15523          * the XHTML-compliant non-breaking space character '&amp;#160;')
15524          * @return {Roo.MessageBox} This message box
15525          */
15526         updateText : function(text){
15527             if(!dlg.isVisible() && !opt.width){
15528                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15529             }
15530             msgEl.innerHTML = text || '&#160;';
15531             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15532                         Math.max(opt.minWidth || this.minWidth, bwidth));
15533             if(opt.prompt){
15534                 activeTextEl.setWidth(w);
15535             }
15536             if(dlg.isVisible()){
15537                 dlg.fixedcenter = false;
15538             }
15539             dlg.setContentSize(w, bodyEl.getHeight());
15540             if(dlg.isVisible()){
15541                 dlg.fixedcenter = true;
15542             }
15543             return this;
15544         },
15545
15546         /**
15547          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15548          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15549          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15550          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15551          * @return {Roo.MessageBox} This message box
15552          */
15553         updateProgress : function(value, text){
15554             if(text){
15555                 this.updateText(text);
15556             }
15557             if (pp) { // weird bug on my firefox - for some reason this is not defined
15558                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15559             }
15560             return this;
15561         },        
15562
15563         /**
15564          * Returns true if the message box is currently displayed
15565          * @return {Boolean} True if the message box is visible, else false
15566          */
15567         isVisible : function(){
15568             return dlg && dlg.isVisible();  
15569         },
15570
15571         /**
15572          * Hides the message box if it is displayed
15573          */
15574         hide : function(){
15575             if(this.isVisible()){
15576                 dlg.hide();
15577             }  
15578         },
15579
15580         /**
15581          * Displays a new message box, or reinitializes an existing message box, based on the config options
15582          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15583          * The following config object properties are supported:
15584          * <pre>
15585 Property    Type             Description
15586 ----------  ---------------  ------------------------------------------------------------------------------------
15587 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15588                                    closes (defaults to undefined)
15589 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15590                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15591 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15592                                    progress and wait dialogs will ignore this property and always hide the
15593                                    close button as they can only be closed programmatically.
15594 cls               String           A custom CSS class to apply to the message box element
15595 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15596                                    displayed (defaults to 75)
15597 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15598                                    function will be btn (the name of the button that was clicked, if applicable,
15599                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15600                                    Progress and wait dialogs will ignore this option since they do not respond to
15601                                    user actions and can only be closed programmatically, so any required function
15602                                    should be called by the same code after it closes the dialog.
15603 icon              String           A CSS class that provides a background image to be used as an icon for
15604                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15605 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15606 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15607 modal             Boolean          False to allow user interaction with the page while the message box is
15608                                    displayed (defaults to true)
15609 msg               String           A string that will replace the existing message box body text (defaults
15610                                    to the XHTML-compliant non-breaking space character '&#160;')
15611 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15612 progress          Boolean          True to display a progress bar (defaults to false)
15613 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15614 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15615 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15616 title             String           The title text
15617 value             String           The string value to set into the active textbox element if displayed
15618 wait              Boolean          True to display a progress bar (defaults to false)
15619 width             Number           The width of the dialog in pixels
15620 </pre>
15621          *
15622          * Example usage:
15623          * <pre><code>
15624 Roo.Msg.show({
15625    title: 'Address',
15626    msg: 'Please enter your address:',
15627    width: 300,
15628    buttons: Roo.MessageBox.OKCANCEL,
15629    multiline: true,
15630    fn: saveAddress,
15631    animEl: 'addAddressBtn'
15632 });
15633 </code></pre>
15634          * @param {Object} config Configuration options
15635          * @return {Roo.MessageBox} This message box
15636          */
15637         show : function(options){
15638             if(this.isVisible()){
15639                 this.hide();
15640             }
15641             var d = this.getDialog();
15642             opt = options;
15643             d.setTitle(opt.title || "&#160;");
15644             d.close.setDisplayed(opt.closable !== false);
15645             activeTextEl = textboxEl;
15646             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15647             if(opt.prompt){
15648                 if(opt.multiline){
15649                     textboxEl.hide();
15650                     textareaEl.show();
15651                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15652                         opt.multiline : this.defaultTextHeight);
15653                     activeTextEl = textareaEl;
15654                 }else{
15655                     textboxEl.show();
15656                     textareaEl.hide();
15657                 }
15658             }else{
15659                 textboxEl.hide();
15660                 textareaEl.hide();
15661             }
15662             progressEl.setDisplayed(opt.progress === true);
15663             this.updateProgress(0);
15664             activeTextEl.dom.value = opt.value || "";
15665             if(opt.prompt){
15666                 dlg.setDefaultButton(activeTextEl);
15667             }else{
15668                 var bs = opt.buttons;
15669                 var db = null;
15670                 if(bs && bs.ok){
15671                     db = buttons["ok"];
15672                 }else if(bs && bs.yes){
15673                     db = buttons["yes"];
15674                 }
15675                 dlg.setDefaultButton(db);
15676             }
15677             bwidth = updateButtons(opt.buttons);
15678             this.updateText(opt.msg);
15679             if(opt.cls){
15680                 d.el.addClass(opt.cls);
15681             }
15682             d.proxyDrag = opt.proxyDrag === true;
15683             d.modal = opt.modal !== false;
15684             d.mask = opt.modal !== false ? mask : false;
15685             if(!d.isVisible()){
15686                 // force it to the end of the z-index stack so it gets a cursor in FF
15687                 document.body.appendChild(dlg.el.dom);
15688                 d.animateTarget = null;
15689                 d.show(options.animEl);
15690             }
15691             return this;
15692         },
15693
15694         /**
15695          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15696          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15697          * and closing the message box when the process is complete.
15698          * @param {String} title The title bar text
15699          * @param {String} msg The message box body text
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         progress : function(title, msg){
15703             this.show({
15704                 title : title,
15705                 msg : msg,
15706                 buttons: false,
15707                 progress:true,
15708                 closable:false,
15709                 minWidth: this.minProgressWidth,
15710                 modal : true
15711             });
15712             return this;
15713         },
15714
15715         /**
15716          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15717          * If a callback function is passed it will be called after the user clicks the button, and the
15718          * id of the button that was clicked will be passed as the only parameter to the callback
15719          * (could also be the top-right close button).
15720          * @param {String} title The title bar text
15721          * @param {String} msg The message box body text
15722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15723          * @param {Object} scope (optional) The scope of the callback function
15724          * @return {Roo.MessageBox} This message box
15725          */
15726         alert : function(title, msg, fn, scope){
15727             this.show({
15728                 title : title,
15729                 msg : msg,
15730                 buttons: this.OK,
15731                 fn: fn,
15732                 scope : scope,
15733                 modal : true
15734             });
15735             return this;
15736         },
15737
15738         /**
15739          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15740          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15741          * You are responsible for closing the message box when the process is complete.
15742          * @param {String} msg The message box body text
15743          * @param {String} title (optional) The title bar text
15744          * @return {Roo.MessageBox} This message box
15745          */
15746         wait : function(msg, title){
15747             this.show({
15748                 title : title,
15749                 msg : msg,
15750                 buttons: false,
15751                 closable:false,
15752                 progress:true,
15753                 modal:true,
15754                 width:300,
15755                 wait:true
15756             });
15757             waitTimer = Roo.TaskMgr.start({
15758                 run: function(i){
15759                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15760                 },
15761                 interval: 1000
15762             });
15763             return this;
15764         },
15765
15766         /**
15767          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15768          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15769          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15770          * @param {String} title The title bar text
15771          * @param {String} msg The message box body text
15772          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15773          * @param {Object} scope (optional) The scope of the callback function
15774          * @return {Roo.MessageBox} This message box
15775          */
15776         confirm : function(title, msg, fn, scope){
15777             this.show({
15778                 title : title,
15779                 msg : msg,
15780                 buttons: this.YESNO,
15781                 fn: fn,
15782                 scope : scope,
15783                 modal : true
15784             });
15785             return this;
15786         },
15787
15788         /**
15789          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15790          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15791          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15792          * (could also be the top-right close button) and the text that was entered will be passed as the two
15793          * parameters to the callback.
15794          * @param {String} title The title bar text
15795          * @param {String} msg The message box body text
15796          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15797          * @param {Object} scope (optional) The scope of the callback function
15798          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15799          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15800          * @return {Roo.MessageBox} This message box
15801          */
15802         prompt : function(title, msg, fn, scope, multiline){
15803             this.show({
15804                 title : title,
15805                 msg : msg,
15806                 buttons: this.OKCANCEL,
15807                 fn: fn,
15808                 minWidth:250,
15809                 scope : scope,
15810                 prompt:true,
15811                 multiline: multiline,
15812                 modal : true
15813             });
15814             return this;
15815         },
15816
15817         /**
15818          * Button config that displays a single OK button
15819          * @type Object
15820          */
15821         OK : {ok:true},
15822         /**
15823          * Button config that displays Yes and No buttons
15824          * @type Object
15825          */
15826         YESNO : {yes:true, no:true},
15827         /**
15828          * Button config that displays OK and Cancel buttons
15829          * @type Object
15830          */
15831         OKCANCEL : {ok:true, cancel:true},
15832         /**
15833          * Button config that displays Yes, No and Cancel buttons
15834          * @type Object
15835          */
15836         YESNOCANCEL : {yes:true, no:true, cancel:true},
15837
15838         /**
15839          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15840          * @type Number
15841          */
15842         defaultTextHeight : 75,
15843         /**
15844          * The maximum width in pixels of the message box (defaults to 600)
15845          * @type Number
15846          */
15847         maxWidth : 600,
15848         /**
15849          * The minimum width in pixels of the message box (defaults to 100)
15850          * @type Number
15851          */
15852         minWidth : 100,
15853         /**
15854          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15855          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15856          * @type Number
15857          */
15858         minProgressWidth : 250,
15859         /**
15860          * An object containing the default button text strings that can be overriden for localized language support.
15861          * Supported properties are: ok, cancel, yes and no.
15862          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15863          * @type Object
15864          */
15865         buttonText : {
15866             ok : "OK",
15867             cancel : "Cancel",
15868             yes : "Yes",
15869             no : "No"
15870         }
15871     };
15872 }();
15873
15874 /**
15875  * Shorthand for {@link Roo.MessageBox}
15876  */
15877 Roo.Msg = Roo.MessageBox;/*
15878  * Based on:
15879  * Ext JS Library 1.1.1
15880  * Copyright(c) 2006-2007, Ext JS, LLC.
15881  *
15882  * Originally Released Under LGPL - original licence link has changed is not relivant.
15883  *
15884  * Fork - LGPL
15885  * <script type="text/javascript">
15886  */
15887 /**
15888  * @class Roo.QuickTips
15889  * Provides attractive and customizable tooltips for any element.
15890  * @singleton
15891  */
15892 Roo.QuickTips = function(){
15893     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15894     var ce, bd, xy, dd;
15895     var visible = false, disabled = true, inited = false;
15896     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15897     
15898     var onOver = function(e){
15899         if(disabled){
15900             return;
15901         }
15902         var t = e.getTarget();
15903         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15904             return;
15905         }
15906         if(ce && t == ce.el){
15907             clearTimeout(hideProc);
15908             return;
15909         }
15910         if(t && tagEls[t.id]){
15911             tagEls[t.id].el = t;
15912             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15913             return;
15914         }
15915         var ttp, et = Roo.fly(t);
15916         var ns = cfg.namespace;
15917         if(tm.interceptTitles && t.title){
15918             ttp = t.title;
15919             t.qtip = ttp;
15920             t.removeAttribute("title");
15921             e.preventDefault();
15922         }else{
15923             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15924         }
15925         if(ttp){
15926             showProc = show.defer(tm.showDelay, tm, [{
15927                 el: t, 
15928                 text: ttp, 
15929                 width: et.getAttributeNS(ns, cfg.width),
15930                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15931                 title: et.getAttributeNS(ns, cfg.title),
15932                     cls: et.getAttributeNS(ns, cfg.cls)
15933             }]);
15934         }
15935     };
15936     
15937     var onOut = function(e){
15938         clearTimeout(showProc);
15939         var t = e.getTarget();
15940         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15941             hideProc = setTimeout(hide, tm.hideDelay);
15942         }
15943     };
15944     
15945     var onMove = function(e){
15946         if(disabled){
15947             return;
15948         }
15949         xy = e.getXY();
15950         xy[1] += 18;
15951         if(tm.trackMouse && ce){
15952             el.setXY(xy);
15953         }
15954     };
15955     
15956     var onDown = function(e){
15957         clearTimeout(showProc);
15958         clearTimeout(hideProc);
15959         if(!e.within(el)){
15960             if(tm.hideOnClick){
15961                 hide();
15962                 tm.disable();
15963                 tm.enable.defer(100, tm);
15964             }
15965         }
15966     };
15967     
15968     var getPad = function(){
15969         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15970     };
15971
15972     var show = function(o){
15973         if(disabled){
15974             return;
15975         }
15976         clearTimeout(dismissProc);
15977         ce = o;
15978         if(removeCls){ // in case manually hidden
15979             el.removeClass(removeCls);
15980             removeCls = null;
15981         }
15982         if(ce.cls){
15983             el.addClass(ce.cls);
15984             removeCls = ce.cls;
15985         }
15986         if(ce.title){
15987             tipTitle.update(ce.title);
15988             tipTitle.show();
15989         }else{
15990             tipTitle.update('');
15991             tipTitle.hide();
15992         }
15993         el.dom.style.width  = tm.maxWidth+'px';
15994         //tipBody.dom.style.width = '';
15995         tipBodyText.update(o.text);
15996         var p = getPad(), w = ce.width;
15997         if(!w){
15998             var td = tipBodyText.dom;
15999             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16000             if(aw > tm.maxWidth){
16001                 w = tm.maxWidth;
16002             }else if(aw < tm.minWidth){
16003                 w = tm.minWidth;
16004             }else{
16005                 w = aw;
16006             }
16007         }
16008         //tipBody.setWidth(w);
16009         el.setWidth(parseInt(w, 10) + p);
16010         if(ce.autoHide === false){
16011             close.setDisplayed(true);
16012             if(dd){
16013                 dd.unlock();
16014             }
16015         }else{
16016             close.setDisplayed(false);
16017             if(dd){
16018                 dd.lock();
16019             }
16020         }
16021         if(xy){
16022             el.avoidY = xy[1]-18;
16023             el.setXY(xy);
16024         }
16025         if(tm.animate){
16026             el.setOpacity(.1);
16027             el.setStyle("visibility", "visible");
16028             el.fadeIn({callback: afterShow});
16029         }else{
16030             afterShow();
16031         }
16032     };
16033     
16034     var afterShow = function(){
16035         if(ce){
16036             el.show();
16037             esc.enable();
16038             if(tm.autoDismiss && ce.autoHide !== false){
16039                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16040             }
16041         }
16042     };
16043     
16044     var hide = function(noanim){
16045         clearTimeout(dismissProc);
16046         clearTimeout(hideProc);
16047         ce = null;
16048         if(el.isVisible()){
16049             esc.disable();
16050             if(noanim !== true && tm.animate){
16051                 el.fadeOut({callback: afterHide});
16052             }else{
16053                 afterHide();
16054             } 
16055         }
16056     };
16057     
16058     var afterHide = function(){
16059         el.hide();
16060         if(removeCls){
16061             el.removeClass(removeCls);
16062             removeCls = null;
16063         }
16064     };
16065     
16066     return {
16067         /**
16068         * @cfg {Number} minWidth
16069         * The minimum width of the quick tip (defaults to 40)
16070         */
16071        minWidth : 40,
16072         /**
16073         * @cfg {Number} maxWidth
16074         * The maximum width of the quick tip (defaults to 300)
16075         */
16076        maxWidth : 300,
16077         /**
16078         * @cfg {Boolean} interceptTitles
16079         * True to automatically use the element's DOM title value if available (defaults to false)
16080         */
16081        interceptTitles : false,
16082         /**
16083         * @cfg {Boolean} trackMouse
16084         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16085         */
16086        trackMouse : false,
16087         /**
16088         * @cfg {Boolean} hideOnClick
16089         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16090         */
16091        hideOnClick : true,
16092         /**
16093         * @cfg {Number} showDelay
16094         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16095         */
16096        showDelay : 500,
16097         /**
16098         * @cfg {Number} hideDelay
16099         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16100         */
16101        hideDelay : 200,
16102         /**
16103         * @cfg {Boolean} autoHide
16104         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16105         * Used in conjunction with hideDelay.
16106         */
16107        autoHide : true,
16108         /**
16109         * @cfg {Boolean}
16110         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16111         * (defaults to true).  Used in conjunction with autoDismissDelay.
16112         */
16113        autoDismiss : true,
16114         /**
16115         * @cfg {Number}
16116         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16117         */
16118        autoDismissDelay : 5000,
16119        /**
16120         * @cfg {Boolean} animate
16121         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16122         */
16123        animate : false,
16124
16125        /**
16126         * @cfg {String} title
16127         * Title text to display (defaults to '').  This can be any valid HTML markup.
16128         */
16129         title: '',
16130        /**
16131         * @cfg {String} text
16132         * Body text to display (defaults to '').  This can be any valid HTML markup.
16133         */
16134         text : '',
16135        /**
16136         * @cfg {String} cls
16137         * A CSS class to apply to the base quick tip element (defaults to '').
16138         */
16139         cls : '',
16140        /**
16141         * @cfg {Number} width
16142         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16143         * minWidth or maxWidth.
16144         */
16145         width : null,
16146
16147     /**
16148      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16149      * or display QuickTips in a page.
16150      */
16151        init : function(){
16152           tm = Roo.QuickTips;
16153           cfg = tm.tagConfig;
16154           if(!inited){
16155               if(!Roo.isReady){ // allow calling of init() before onReady
16156                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16157                   return;
16158               }
16159               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16160               el.fxDefaults = {stopFx: true};
16161               // maximum custom styling
16162               //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>');
16163               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>');              
16164               tipTitle = el.child('h3');
16165               tipTitle.enableDisplayMode("block");
16166               tipBody = el.child('div.x-tip-bd');
16167               tipBodyText = el.child('div.x-tip-bd-inner');
16168               //bdLeft = el.child('div.x-tip-bd-left');
16169               //bdRight = el.child('div.x-tip-bd-right');
16170               close = el.child('div.x-tip-close');
16171               close.enableDisplayMode("block");
16172               close.on("click", hide);
16173               var d = Roo.get(document);
16174               d.on("mousedown", onDown);
16175               d.on("mouseover", onOver);
16176               d.on("mouseout", onOut);
16177               d.on("mousemove", onMove);
16178               esc = d.addKeyListener(27, hide);
16179               esc.disable();
16180               if(Roo.dd.DD){
16181                   dd = el.initDD("default", null, {
16182                       onDrag : function(){
16183                           el.sync();  
16184                       }
16185                   });
16186                   dd.setHandleElId(tipTitle.id);
16187                   dd.lock();
16188               }
16189               inited = true;
16190           }
16191           this.enable(); 
16192        },
16193
16194     /**
16195      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16196      * are supported:
16197      * <pre>
16198 Property    Type                   Description
16199 ----------  ---------------------  ------------------------------------------------------------------------
16200 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16201      * </ul>
16202      * @param {Object} config The config object
16203      */
16204        register : function(config){
16205            var cs = config instanceof Array ? config : arguments;
16206            for(var i = 0, len = cs.length; i < len; i++) {
16207                var c = cs[i];
16208                var target = c.target;
16209                if(target){
16210                    if(target instanceof Array){
16211                        for(var j = 0, jlen = target.length; j < jlen; j++){
16212                            tagEls[target[j]] = c;
16213                        }
16214                    }else{
16215                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16216                    }
16217                }
16218            }
16219        },
16220
16221     /**
16222      * Removes this quick tip from its element and destroys it.
16223      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16224      */
16225        unregister : function(el){
16226            delete tagEls[Roo.id(el)];
16227        },
16228
16229     /**
16230      * Enable this quick tip.
16231      */
16232        enable : function(){
16233            if(inited && disabled){
16234                locks.pop();
16235                if(locks.length < 1){
16236                    disabled = false;
16237                }
16238            }
16239        },
16240
16241     /**
16242      * Disable this quick tip.
16243      */
16244        disable : function(){
16245           disabled = true;
16246           clearTimeout(showProc);
16247           clearTimeout(hideProc);
16248           clearTimeout(dismissProc);
16249           if(ce){
16250               hide(true);
16251           }
16252           locks.push(1);
16253        },
16254
16255     /**
16256      * Returns true if the quick tip is enabled, else false.
16257      */
16258        isEnabled : function(){
16259             return !disabled;
16260        },
16261
16262         // private
16263        tagConfig : {
16264            namespace : "ext",
16265            attribute : "qtip",
16266            width : "width",
16267            target : "target",
16268            title : "qtitle",
16269            hide : "hide",
16270            cls : "qclass"
16271        }
16272    };
16273 }();
16274
16275 // backwards compat
16276 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16277  * Based on:
16278  * Ext JS Library 1.1.1
16279  * Copyright(c) 2006-2007, Ext JS, LLC.
16280  *
16281  * Originally Released Under LGPL - original licence link has changed is not relivant.
16282  *
16283  * Fork - LGPL
16284  * <script type="text/javascript">
16285  */
16286  
16287
16288 /**
16289  * @class Roo.tree.TreePanel
16290  * @extends Roo.data.Tree
16291
16292  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16293  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16294  * @cfg {Boolean} enableDD true to enable drag and drop
16295  * @cfg {Boolean} enableDrag true to enable just drag
16296  * @cfg {Boolean} enableDrop true to enable just drop
16297  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16298  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16299  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16300  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16301  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16302  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16303  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16304  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16305  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16306  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16307  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16308  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16309  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16310  * @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>
16311  * @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>
16312  * 
16313  * @constructor
16314  * @param {String/HTMLElement/Element} el The container element
16315  * @param {Object} config
16316  */
16317 Roo.tree.TreePanel = function(el, config){
16318     var root = false;
16319     var loader = false;
16320     if (config.root) {
16321         root = config.root;
16322         delete config.root;
16323     }
16324     if (config.loader) {
16325         loader = config.loader;
16326         delete config.loader;
16327     }
16328     
16329     Roo.apply(this, config);
16330     Roo.tree.TreePanel.superclass.constructor.call(this);
16331     this.el = Roo.get(el);
16332     this.el.addClass('x-tree');
16333     //console.log(root);
16334     if (root) {
16335         this.setRootNode( Roo.factory(root, Roo.tree));
16336     }
16337     if (loader) {
16338         this.loader = Roo.factory(loader, Roo.tree);
16339     }
16340    /**
16341     * Read-only. The id of the container element becomes this TreePanel's id.
16342     */
16343    this.id = this.el.id;
16344    this.addEvents({
16345         /**
16346         * @event beforeload
16347         * Fires before a node is loaded, return false to cancel
16348         * @param {Node} node The node being loaded
16349         */
16350         "beforeload" : true,
16351         /**
16352         * @event load
16353         * Fires when a node is loaded
16354         * @param {Node} node The node that was loaded
16355         */
16356         "load" : true,
16357         /**
16358         * @event textchange
16359         * Fires when the text for a node is changed
16360         * @param {Node} node The node
16361         * @param {String} text The new text
16362         * @param {String} oldText The old text
16363         */
16364         "textchange" : true,
16365         /**
16366         * @event beforeexpand
16367         * Fires before a node is expanded, return false to cancel.
16368         * @param {Node} node The node
16369         * @param {Boolean} deep
16370         * @param {Boolean} anim
16371         */
16372         "beforeexpand" : true,
16373         /**
16374         * @event beforecollapse
16375         * Fires before a node is collapsed, return false to cancel.
16376         * @param {Node} node The node
16377         * @param {Boolean} deep
16378         * @param {Boolean} anim
16379         */
16380         "beforecollapse" : true,
16381         /**
16382         * @event expand
16383         * Fires when a node is expanded
16384         * @param {Node} node The node
16385         */
16386         "expand" : true,
16387         /**
16388         * @event disabledchange
16389         * Fires when the disabled status of a node changes
16390         * @param {Node} node The node
16391         * @param {Boolean} disabled
16392         */
16393         "disabledchange" : true,
16394         /**
16395         * @event collapse
16396         * Fires when a node is collapsed
16397         * @param {Node} node The node
16398         */
16399         "collapse" : true,
16400         /**
16401         * @event beforeclick
16402         * Fires before click processing on a node. Return false to cancel the default action.
16403         * @param {Node} node The node
16404         * @param {Roo.EventObject} e The event object
16405         */
16406         "beforeclick":true,
16407         /**
16408         * @event checkchange
16409         * Fires when a node with a checkbox's checked property changes
16410         * @param {Node} this This node
16411         * @param {Boolean} checked
16412         */
16413         "checkchange":true,
16414         /**
16415         * @event click
16416         * Fires when a node is clicked
16417         * @param {Node} node The node
16418         * @param {Roo.EventObject} e The event object
16419         */
16420         "click":true,
16421         /**
16422         * @event dblclick
16423         * Fires when a node is double clicked
16424         * @param {Node} node The node
16425         * @param {Roo.EventObject} e The event object
16426         */
16427         "dblclick":true,
16428         /**
16429         * @event contextmenu
16430         * Fires when a node is right clicked
16431         * @param {Node} node The node
16432         * @param {Roo.EventObject} e The event object
16433         */
16434         "contextmenu":true,
16435         /**
16436         * @event beforechildrenrendered
16437         * Fires right before the child nodes for a node are rendered
16438         * @param {Node} node The node
16439         */
16440         "beforechildrenrendered":true,
16441        /**
16442              * @event startdrag
16443              * Fires when a node starts being dragged
16444              * @param {Roo.tree.TreePanel} this
16445              * @param {Roo.tree.TreeNode} node
16446              * @param {event} e The raw browser event
16447              */ 
16448             "startdrag" : true,
16449             /**
16450              * @event enddrag
16451              * Fires when a drag operation is complete
16452              * @param {Roo.tree.TreePanel} this
16453              * @param {Roo.tree.TreeNode} node
16454              * @param {event} e The raw browser event
16455              */
16456             "enddrag" : true,
16457             /**
16458              * @event dragdrop
16459              * Fires when a dragged node is dropped on a valid DD target
16460              * @param {Roo.tree.TreePanel} this
16461              * @param {Roo.tree.TreeNode} node
16462              * @param {DD} dd The dd it was dropped on
16463              * @param {event} e The raw browser event
16464              */
16465             "dragdrop" : true,
16466             /**
16467              * @event beforenodedrop
16468              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16469              * passed to handlers has the following properties:<br />
16470              * <ul style="padding:5px;padding-left:16px;">
16471              * <li>tree - The TreePanel</li>
16472              * <li>target - The node being targeted for the drop</li>
16473              * <li>data - The drag data from the drag source</li>
16474              * <li>point - The point of the drop - append, above or below</li>
16475              * <li>source - The drag source</li>
16476              * <li>rawEvent - Raw mouse event</li>
16477              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16478              * to be inserted by setting them on this object.</li>
16479              * <li>cancel - Set this to true to cancel the drop.</li>
16480              * </ul>
16481              * @param {Object} dropEvent
16482              */
16483             "beforenodedrop" : true,
16484             /**
16485              * @event nodedrop
16486              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16487              * passed to handlers has the following properties:<br />
16488              * <ul style="padding:5px;padding-left:16px;">
16489              * <li>tree - The TreePanel</li>
16490              * <li>target - The node being targeted for the drop</li>
16491              * <li>data - The drag data from the drag source</li>
16492              * <li>point - The point of the drop - append, above or below</li>
16493              * <li>source - The drag source</li>
16494              * <li>rawEvent - Raw mouse event</li>
16495              * <li>dropNode - Dropped node(s).</li>
16496              * </ul>
16497              * @param {Object} dropEvent
16498              */
16499             "nodedrop" : true,
16500              /**
16501              * @event nodedragover
16502              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16503              * passed to handlers has the following properties:<br />
16504              * <ul style="padding:5px;padding-left:16px;">
16505              * <li>tree - The TreePanel</li>
16506              * <li>target - The node being targeted for the drop</li>
16507              * <li>data - The drag data from the drag source</li>
16508              * <li>point - The point of the drop - append, above or below</li>
16509              * <li>source - The drag source</li>
16510              * <li>rawEvent - Raw mouse event</li>
16511              * <li>dropNode - Drop node(s) provided by the source.</li>
16512              * <li>cancel - Set this to true to signal drop not allowed.</li>
16513              * </ul>
16514              * @param {Object} dragOverEvent
16515              */
16516             "nodedragover" : true
16517         
16518    });
16519    if(this.singleExpand){
16520        this.on("beforeexpand", this.restrictExpand, this);
16521    }
16522 };
16523 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16524     rootVisible : true,
16525     animate: Roo.enableFx,
16526     lines : true,
16527     enableDD : false,
16528     hlDrop : Roo.enableFx,
16529   
16530     renderer: false,
16531     
16532     rendererTip: false,
16533     // private
16534     restrictExpand : function(node){
16535         var p = node.parentNode;
16536         if(p){
16537             if(p.expandedChild && p.expandedChild.parentNode == p){
16538                 p.expandedChild.collapse();
16539             }
16540             p.expandedChild = node;
16541         }
16542     },
16543
16544     // private override
16545     setRootNode : function(node){
16546         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16547         if(!this.rootVisible){
16548             node.ui = new Roo.tree.RootTreeNodeUI(node);
16549         }
16550         return node;
16551     },
16552
16553     /**
16554      * Returns the container element for this TreePanel
16555      */
16556     getEl : function(){
16557         return this.el;
16558     },
16559
16560     /**
16561      * Returns the default TreeLoader for this TreePanel
16562      */
16563     getLoader : function(){
16564         return this.loader;
16565     },
16566
16567     /**
16568      * Expand all nodes
16569      */
16570     expandAll : function(){
16571         this.root.expand(true);
16572     },
16573
16574     /**
16575      * Collapse all nodes
16576      */
16577     collapseAll : function(){
16578         this.root.collapse(true);
16579     },
16580
16581     /**
16582      * Returns the selection model used by this TreePanel
16583      */
16584     getSelectionModel : function(){
16585         if(!this.selModel){
16586             this.selModel = new Roo.tree.DefaultSelectionModel();
16587         }
16588         return this.selModel;
16589     },
16590
16591     /**
16592      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16593      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16594      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16595      * @return {Array}
16596      */
16597     getChecked : function(a, startNode){
16598         startNode = startNode || this.root;
16599         var r = [];
16600         var f = function(){
16601             if(this.attributes.checked){
16602                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16603             }
16604         }
16605         startNode.cascade(f);
16606         return r;
16607     },
16608
16609     /**
16610      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16611      * @param {String} path
16612      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16613      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16614      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16615      */
16616     expandPath : function(path, attr, callback){
16617         attr = attr || "id";
16618         var keys = path.split(this.pathSeparator);
16619         var curNode = this.root;
16620         if(curNode.attributes[attr] != keys[1]){ // invalid root
16621             if(callback){
16622                 callback(false, null);
16623             }
16624             return;
16625         }
16626         var index = 1;
16627         var f = function(){
16628             if(++index == keys.length){
16629                 if(callback){
16630                     callback(true, curNode);
16631                 }
16632                 return;
16633             }
16634             var c = curNode.findChild(attr, keys[index]);
16635             if(!c){
16636                 if(callback){
16637                     callback(false, curNode);
16638                 }
16639                 return;
16640             }
16641             curNode = c;
16642             c.expand(false, false, f);
16643         };
16644         curNode.expand(false, false, f);
16645     },
16646
16647     /**
16648      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16649      * @param {String} path
16650      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16651      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16652      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16653      */
16654     selectPath : function(path, attr, callback){
16655         attr = attr || "id";
16656         var keys = path.split(this.pathSeparator);
16657         var v = keys.pop();
16658         if(keys.length > 0){
16659             var f = function(success, node){
16660                 if(success && node){
16661                     var n = node.findChild(attr, v);
16662                     if(n){
16663                         n.select();
16664                         if(callback){
16665                             callback(true, n);
16666                         }
16667                     }else if(callback){
16668                         callback(false, n);
16669                     }
16670                 }else{
16671                     if(callback){
16672                         callback(false, n);
16673                     }
16674                 }
16675             };
16676             this.expandPath(keys.join(this.pathSeparator), attr, f);
16677         }else{
16678             this.root.select();
16679             if(callback){
16680                 callback(true, this.root);
16681             }
16682         }
16683     },
16684
16685     getTreeEl : function(){
16686         return this.el;
16687     },
16688
16689     /**
16690      * Trigger rendering of this TreePanel
16691      */
16692     render : function(){
16693         if (this.innerCt) {
16694             return this; // stop it rendering more than once!!
16695         }
16696         
16697         this.innerCt = this.el.createChild({tag:"ul",
16698                cls:"x-tree-root-ct " +
16699                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16700
16701         if(this.containerScroll){
16702             Roo.dd.ScrollManager.register(this.el);
16703         }
16704         if((this.enableDD || this.enableDrop) && !this.dropZone){
16705            /**
16706             * The dropZone used by this tree if drop is enabled
16707             * @type Roo.tree.TreeDropZone
16708             */
16709              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16710                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16711            });
16712         }
16713         if((this.enableDD || this.enableDrag) && !this.dragZone){
16714            /**
16715             * The dragZone used by this tree if drag is enabled
16716             * @type Roo.tree.TreeDragZone
16717             */
16718             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16719                ddGroup: this.ddGroup || "TreeDD",
16720                scroll: this.ddScroll
16721            });
16722         }
16723         this.getSelectionModel().init(this);
16724         if (!this.root) {
16725             console.log("ROOT not set in tree");
16726             return;
16727         }
16728         this.root.render();
16729         if(!this.rootVisible){
16730             this.root.renderChildren();
16731         }
16732         return this;
16733     }
16734 });/*
16735  * Based on:
16736  * Ext JS Library 1.1.1
16737  * Copyright(c) 2006-2007, Ext JS, LLC.
16738  *
16739  * Originally Released Under LGPL - original licence link has changed is not relivant.
16740  *
16741  * Fork - LGPL
16742  * <script type="text/javascript">
16743  */
16744  
16745
16746 /**
16747  * @class Roo.tree.DefaultSelectionModel
16748  * @extends Roo.util.Observable
16749  * The default single selection for a TreePanel.
16750  */
16751 Roo.tree.DefaultSelectionModel = function(){
16752    this.selNode = null;
16753    
16754    this.addEvents({
16755        /**
16756         * @event selectionchange
16757         * Fires when the selected node changes
16758         * @param {DefaultSelectionModel} this
16759         * @param {TreeNode} node the new selection
16760         */
16761        "selectionchange" : true,
16762
16763        /**
16764         * @event beforeselect
16765         * Fires before the selected node changes, return false to cancel the change
16766         * @param {DefaultSelectionModel} this
16767         * @param {TreeNode} node the new selection
16768         * @param {TreeNode} node the old selection
16769         */
16770        "beforeselect" : true
16771    });
16772 };
16773
16774 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16775     init : function(tree){
16776         this.tree = tree;
16777         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16778         tree.on("click", this.onNodeClick, this);
16779     },
16780     
16781     onNodeClick : function(node, e){
16782         if (e.ctrlKey && this.selNode == node)  {
16783             this.unselect(node);
16784             return;
16785         }
16786         this.select(node);
16787     },
16788     
16789     /**
16790      * Select a node.
16791      * @param {TreeNode} node The node to select
16792      * @return {TreeNode} The selected node
16793      */
16794     select : function(node){
16795         var last = this.selNode;
16796         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16797             if(last){
16798                 last.ui.onSelectedChange(false);
16799             }
16800             this.selNode = node;
16801             node.ui.onSelectedChange(true);
16802             this.fireEvent("selectionchange", this, node, last);
16803         }
16804         return node;
16805     },
16806     
16807     /**
16808      * Deselect a node.
16809      * @param {TreeNode} node The node to unselect
16810      */
16811     unselect : function(node){
16812         if(this.selNode == node){
16813             this.clearSelections();
16814         }    
16815     },
16816     
16817     /**
16818      * Clear all selections
16819      */
16820     clearSelections : function(){
16821         var n = this.selNode;
16822         if(n){
16823             n.ui.onSelectedChange(false);
16824             this.selNode = null;
16825             this.fireEvent("selectionchange", this, null);
16826         }
16827         return n;
16828     },
16829     
16830     /**
16831      * Get the selected node
16832      * @return {TreeNode} The selected node
16833      */
16834     getSelectedNode : function(){
16835         return this.selNode;    
16836     },
16837     
16838     /**
16839      * Returns true if the node is selected
16840      * @param {TreeNode} node The node to check
16841      * @return {Boolean}
16842      */
16843     isSelected : function(node){
16844         return this.selNode == node;  
16845     },
16846
16847     /**
16848      * Selects the node above the selected node in the tree, intelligently walking the nodes
16849      * @return TreeNode The new selection
16850      */
16851     selectPrevious : function(){
16852         var s = this.selNode || this.lastSelNode;
16853         if(!s){
16854             return null;
16855         }
16856         var ps = s.previousSibling;
16857         if(ps){
16858             if(!ps.isExpanded() || ps.childNodes.length < 1){
16859                 return this.select(ps);
16860             } else{
16861                 var lc = ps.lastChild;
16862                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16863                     lc = lc.lastChild;
16864                 }
16865                 return this.select(lc);
16866             }
16867         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16868             return this.select(s.parentNode);
16869         }
16870         return null;
16871     },
16872
16873     /**
16874      * Selects the node above the selected node in the tree, intelligently walking the nodes
16875      * @return TreeNode The new selection
16876      */
16877     selectNext : function(){
16878         var s = this.selNode || this.lastSelNode;
16879         if(!s){
16880             return null;
16881         }
16882         if(s.firstChild && s.isExpanded()){
16883              return this.select(s.firstChild);
16884          }else if(s.nextSibling){
16885              return this.select(s.nextSibling);
16886          }else if(s.parentNode){
16887             var newS = null;
16888             s.parentNode.bubble(function(){
16889                 if(this.nextSibling){
16890                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16891                     return false;
16892                 }
16893             });
16894             return newS;
16895          }
16896         return null;
16897     },
16898
16899     onKeyDown : function(e){
16900         var s = this.selNode || this.lastSelNode;
16901         // undesirable, but required
16902         var sm = this;
16903         if(!s){
16904             return;
16905         }
16906         var k = e.getKey();
16907         switch(k){
16908              case e.DOWN:
16909                  e.stopEvent();
16910                  this.selectNext();
16911              break;
16912              case e.UP:
16913                  e.stopEvent();
16914                  this.selectPrevious();
16915              break;
16916              case e.RIGHT:
16917                  e.preventDefault();
16918                  if(s.hasChildNodes()){
16919                      if(!s.isExpanded()){
16920                          s.expand();
16921                      }else if(s.firstChild){
16922                          this.select(s.firstChild, e);
16923                      }
16924                  }
16925              break;
16926              case e.LEFT:
16927                  e.preventDefault();
16928                  if(s.hasChildNodes() && s.isExpanded()){
16929                      s.collapse();
16930                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16931                      this.select(s.parentNode, e);
16932                  }
16933              break;
16934         };
16935     }
16936 });
16937
16938 /**
16939  * @class Roo.tree.MultiSelectionModel
16940  * @extends Roo.util.Observable
16941  * Multi selection for a TreePanel.
16942  */
16943 Roo.tree.MultiSelectionModel = function(){
16944    this.selNodes = [];
16945    this.selMap = {};
16946    this.addEvents({
16947        /**
16948         * @event selectionchange
16949         * Fires when the selected nodes change
16950         * @param {MultiSelectionModel} this
16951         * @param {Array} nodes Array of the selected nodes
16952         */
16953        "selectionchange" : true
16954    });
16955 };
16956
16957 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16958     init : function(tree){
16959         this.tree = tree;
16960         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16961         tree.on("click", this.onNodeClick, this);
16962     },
16963     
16964     onNodeClick : function(node, e){
16965         this.select(node, e, e.ctrlKey);
16966     },
16967     
16968     /**
16969      * Select a node.
16970      * @param {TreeNode} node The node to select
16971      * @param {EventObject} e (optional) An event associated with the selection
16972      * @param {Boolean} keepExisting True to retain existing selections
16973      * @return {TreeNode} The selected node
16974      */
16975     select : function(node, e, keepExisting){
16976         if(keepExisting !== true){
16977             this.clearSelections(true);
16978         }
16979         if(this.isSelected(node)){
16980             this.lastSelNode = node;
16981             return node;
16982         }
16983         this.selNodes.push(node);
16984         this.selMap[node.id] = node;
16985         this.lastSelNode = node;
16986         node.ui.onSelectedChange(true);
16987         this.fireEvent("selectionchange", this, this.selNodes);
16988         return node;
16989     },
16990     
16991     /**
16992      * Deselect a node.
16993      * @param {TreeNode} node The node to unselect
16994      */
16995     unselect : function(node){
16996         if(this.selMap[node.id]){
16997             node.ui.onSelectedChange(false);
16998             var sn = this.selNodes;
16999             var index = -1;
17000             if(sn.indexOf){
17001                 index = sn.indexOf(node);
17002             }else{
17003                 for(var i = 0, len = sn.length; i < len; i++){
17004                     if(sn[i] == node){
17005                         index = i;
17006                         break;
17007                     }
17008                 }
17009             }
17010             if(index != -1){
17011                 this.selNodes.splice(index, 1);
17012             }
17013             delete this.selMap[node.id];
17014             this.fireEvent("selectionchange", this, this.selNodes);
17015         }
17016     },
17017     
17018     /**
17019      * Clear all selections
17020      */
17021     clearSelections : function(suppressEvent){
17022         var sn = this.selNodes;
17023         if(sn.length > 0){
17024             for(var i = 0, len = sn.length; i < len; i++){
17025                 sn[i].ui.onSelectedChange(false);
17026             }
17027             this.selNodes = [];
17028             this.selMap = {};
17029             if(suppressEvent !== true){
17030                 this.fireEvent("selectionchange", this, this.selNodes);
17031             }
17032         }
17033     },
17034     
17035     /**
17036      * Returns true if the node is selected
17037      * @param {TreeNode} node The node to check
17038      * @return {Boolean}
17039      */
17040     isSelected : function(node){
17041         return this.selMap[node.id] ? true : false;  
17042     },
17043     
17044     /**
17045      * Returns an array of the selected nodes
17046      * @return {Array}
17047      */
17048     getSelectedNodes : function(){
17049         return this.selNodes;    
17050     },
17051
17052     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17053
17054     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17055
17056     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17057 });/*
17058  * Based on:
17059  * Ext JS Library 1.1.1
17060  * Copyright(c) 2006-2007, Ext JS, LLC.
17061  *
17062  * Originally Released Under LGPL - original licence link has changed is not relivant.
17063  *
17064  * Fork - LGPL
17065  * <script type="text/javascript">
17066  */
17067  
17068 /**
17069  * @class Roo.tree.TreeNode
17070  * @extends Roo.data.Node
17071  * @cfg {String} text The text for this node
17072  * @cfg {Boolean} expanded true to start the node expanded
17073  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17074  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17075  * @cfg {Boolean} disabled true to start the node disabled
17076  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17077  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17078  * @cfg {String} cls A css class to be added to the node
17079  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17080  * @cfg {String} href URL of the link used for the node (defaults to #)
17081  * @cfg {String} hrefTarget target frame for the link
17082  * @cfg {String} qtip An Ext QuickTip for the node
17083  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17084  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17085  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17086  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17087  * (defaults to undefined with no checkbox rendered)
17088  * @constructor
17089  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17090  */
17091 Roo.tree.TreeNode = function(attributes){
17092     attributes = attributes || {};
17093     if(typeof attributes == "string"){
17094         attributes = {text: attributes};
17095     }
17096     this.childrenRendered = false;
17097     this.rendered = false;
17098     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17099     this.expanded = attributes.expanded === true;
17100     this.isTarget = attributes.isTarget !== false;
17101     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17102     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17103
17104     /**
17105      * Read-only. The text for this node. To change it use setText().
17106      * @type String
17107      */
17108     this.text = attributes.text;
17109     /**
17110      * True if this node is disabled.
17111      * @type Boolean
17112      */
17113     this.disabled = attributes.disabled === true;
17114
17115     this.addEvents({
17116         /**
17117         * @event textchange
17118         * Fires when the text for this node is changed
17119         * @param {Node} this This node
17120         * @param {String} text The new text
17121         * @param {String} oldText The old text
17122         */
17123         "textchange" : true,
17124         /**
17125         * @event beforeexpand
17126         * Fires before this node is expanded, return false to cancel.
17127         * @param {Node} this This node
17128         * @param {Boolean} deep
17129         * @param {Boolean} anim
17130         */
17131         "beforeexpand" : true,
17132         /**
17133         * @event beforecollapse
17134         * Fires before this node is collapsed, return false to cancel.
17135         * @param {Node} this This node
17136         * @param {Boolean} deep
17137         * @param {Boolean} anim
17138         */
17139         "beforecollapse" : true,
17140         /**
17141         * @event expand
17142         * Fires when this node is expanded
17143         * @param {Node} this This node
17144         */
17145         "expand" : true,
17146         /**
17147         * @event disabledchange
17148         * Fires when the disabled status of this node changes
17149         * @param {Node} this This node
17150         * @param {Boolean} disabled
17151         */
17152         "disabledchange" : true,
17153         /**
17154         * @event collapse
17155         * Fires when this node is collapsed
17156         * @param {Node} this This node
17157         */
17158         "collapse" : true,
17159         /**
17160         * @event beforeclick
17161         * Fires before click processing. Return false to cancel the default action.
17162         * @param {Node} this This node
17163         * @param {Roo.EventObject} e The event object
17164         */
17165         "beforeclick":true,
17166         /**
17167         * @event checkchange
17168         * Fires when a node with a checkbox's checked property changes
17169         * @param {Node} this This node
17170         * @param {Boolean} checked
17171         */
17172         "checkchange":true,
17173         /**
17174         * @event click
17175         * Fires when this node is clicked
17176         * @param {Node} this This node
17177         * @param {Roo.EventObject} e The event object
17178         */
17179         "click":true,
17180         /**
17181         * @event dblclick
17182         * Fires when this node is double clicked
17183         * @param {Node} this This node
17184         * @param {Roo.EventObject} e The event object
17185         */
17186         "dblclick":true,
17187         /**
17188         * @event contextmenu
17189         * Fires when this node is right clicked
17190         * @param {Node} this This node
17191         * @param {Roo.EventObject} e The event object
17192         */
17193         "contextmenu":true,
17194         /**
17195         * @event beforechildrenrendered
17196         * Fires right before the child nodes for this node are rendered
17197         * @param {Node} this This node
17198         */
17199         "beforechildrenrendered":true
17200     });
17201
17202     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17203
17204     /**
17205      * Read-only. The UI for this node
17206      * @type TreeNodeUI
17207      */
17208     this.ui = new uiClass(this);
17209 };
17210 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17211     preventHScroll: true,
17212     /**
17213      * Returns true if this node is expanded
17214      * @return {Boolean}
17215      */
17216     isExpanded : function(){
17217         return this.expanded;
17218     },
17219
17220     /**
17221      * Returns the UI object for this node
17222      * @return {TreeNodeUI}
17223      */
17224     getUI : function(){
17225         return this.ui;
17226     },
17227
17228     // private override
17229     setFirstChild : function(node){
17230         var of = this.firstChild;
17231         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17232         if(this.childrenRendered && of && node != of){
17233             of.renderIndent(true, true);
17234         }
17235         if(this.rendered){
17236             this.renderIndent(true, true);
17237         }
17238     },
17239
17240     // private override
17241     setLastChild : function(node){
17242         var ol = this.lastChild;
17243         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17244         if(this.childrenRendered && ol && node != ol){
17245             ol.renderIndent(true, true);
17246         }
17247         if(this.rendered){
17248             this.renderIndent(true, true);
17249         }
17250     },
17251
17252     // these methods are overridden to provide lazy rendering support
17253     // private override
17254     appendChild : function(){
17255         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17256         if(node && this.childrenRendered){
17257             node.render();
17258         }
17259         this.ui.updateExpandIcon();
17260         return node;
17261     },
17262
17263     // private override
17264     removeChild : function(node){
17265         this.ownerTree.getSelectionModel().unselect(node);
17266         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17267         // if it's been rendered remove dom node
17268         if(this.childrenRendered){
17269             node.ui.remove();
17270         }
17271         if(this.childNodes.length < 1){
17272             this.collapse(false, false);
17273         }else{
17274             this.ui.updateExpandIcon();
17275         }
17276         if(!this.firstChild) {
17277             this.childrenRendered = false;
17278         }
17279         return node;
17280     },
17281
17282     // private override
17283     insertBefore : function(node, refNode){
17284         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17285         if(newNode && refNode && this.childrenRendered){
17286             node.render();
17287         }
17288         this.ui.updateExpandIcon();
17289         return newNode;
17290     },
17291
17292     /**
17293      * Sets the text for this node
17294      * @param {String} text
17295      */
17296     setText : function(text){
17297         var oldText = this.text;
17298         this.text = text;
17299         this.attributes.text = text;
17300         if(this.rendered){ // event without subscribing
17301             this.ui.onTextChange(this, text, oldText);
17302         }
17303         this.fireEvent("textchange", this, text, oldText);
17304     },
17305
17306     /**
17307      * Triggers selection of this node
17308      */
17309     select : function(){
17310         this.getOwnerTree().getSelectionModel().select(this);
17311     },
17312
17313     /**
17314      * Triggers deselection of this node
17315      */
17316     unselect : function(){
17317         this.getOwnerTree().getSelectionModel().unselect(this);
17318     },
17319
17320     /**
17321      * Returns true if this node is selected
17322      * @return {Boolean}
17323      */
17324     isSelected : function(){
17325         return this.getOwnerTree().getSelectionModel().isSelected(this);
17326     },
17327
17328     /**
17329      * Expand this node.
17330      * @param {Boolean} deep (optional) True to expand all children as well
17331      * @param {Boolean} anim (optional) false to cancel the default animation
17332      * @param {Function} callback (optional) A callback to be called when
17333      * expanding this node completes (does not wait for deep expand to complete).
17334      * Called with 1 parameter, this node.
17335      */
17336     expand : function(deep, anim, callback){
17337         if(!this.expanded){
17338             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17339                 return;
17340             }
17341             if(!this.childrenRendered){
17342                 this.renderChildren();
17343             }
17344             this.expanded = true;
17345             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17346                 this.ui.animExpand(function(){
17347                     this.fireEvent("expand", this);
17348                     if(typeof callback == "function"){
17349                         callback(this);
17350                     }
17351                     if(deep === true){
17352                         this.expandChildNodes(true);
17353                     }
17354                 }.createDelegate(this));
17355                 return;
17356             }else{
17357                 this.ui.expand();
17358                 this.fireEvent("expand", this);
17359                 if(typeof callback == "function"){
17360                     callback(this);
17361                 }
17362             }
17363         }else{
17364            if(typeof callback == "function"){
17365                callback(this);
17366            }
17367         }
17368         if(deep === true){
17369             this.expandChildNodes(true);
17370         }
17371     },
17372
17373     isHiddenRoot : function(){
17374         return this.isRoot && !this.getOwnerTree().rootVisible;
17375     },
17376
17377     /**
17378      * Collapse this node.
17379      * @param {Boolean} deep (optional) True to collapse all children as well
17380      * @param {Boolean} anim (optional) false to cancel the default animation
17381      */
17382     collapse : function(deep, anim){
17383         if(this.expanded && !this.isHiddenRoot()){
17384             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17385                 return;
17386             }
17387             this.expanded = false;
17388             if((this.getOwnerTree().animate && anim !== false) || anim){
17389                 this.ui.animCollapse(function(){
17390                     this.fireEvent("collapse", this);
17391                     if(deep === true){
17392                         this.collapseChildNodes(true);
17393                     }
17394                 }.createDelegate(this));
17395                 return;
17396             }else{
17397                 this.ui.collapse();
17398                 this.fireEvent("collapse", this);
17399             }
17400         }
17401         if(deep === true){
17402             var cs = this.childNodes;
17403             for(var i = 0, len = cs.length; i < len; i++) {
17404                 cs[i].collapse(true, false);
17405             }
17406         }
17407     },
17408
17409     // private
17410     delayedExpand : function(delay){
17411         if(!this.expandProcId){
17412             this.expandProcId = this.expand.defer(delay, this);
17413         }
17414     },
17415
17416     // private
17417     cancelExpand : function(){
17418         if(this.expandProcId){
17419             clearTimeout(this.expandProcId);
17420         }
17421         this.expandProcId = false;
17422     },
17423
17424     /**
17425      * Toggles expanded/collapsed state of the node
17426      */
17427     toggle : function(){
17428         if(this.expanded){
17429             this.collapse();
17430         }else{
17431             this.expand();
17432         }
17433     },
17434
17435     /**
17436      * Ensures all parent nodes are expanded
17437      */
17438     ensureVisible : function(callback){
17439         var tree = this.getOwnerTree();
17440         tree.expandPath(this.parentNode.getPath(), false, function(){
17441             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17442             Roo.callback(callback);
17443         }.createDelegate(this));
17444     },
17445
17446     /**
17447      * Expand all child nodes
17448      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17449      */
17450     expandChildNodes : function(deep){
17451         var cs = this.childNodes;
17452         for(var i = 0, len = cs.length; i < len; i++) {
17453                 cs[i].expand(deep);
17454         }
17455     },
17456
17457     /**
17458      * Collapse all child nodes
17459      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17460      */
17461     collapseChildNodes : function(deep){
17462         var cs = this.childNodes;
17463         for(var i = 0, len = cs.length; i < len; i++) {
17464                 cs[i].collapse(deep);
17465         }
17466     },
17467
17468     /**
17469      * Disables this node
17470      */
17471     disable : function(){
17472         this.disabled = true;
17473         this.unselect();
17474         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17475             this.ui.onDisableChange(this, true);
17476         }
17477         this.fireEvent("disabledchange", this, true);
17478     },
17479
17480     /**
17481      * Enables this node
17482      */
17483     enable : function(){
17484         this.disabled = false;
17485         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17486             this.ui.onDisableChange(this, false);
17487         }
17488         this.fireEvent("disabledchange", this, false);
17489     },
17490
17491     // private
17492     renderChildren : function(suppressEvent){
17493         if(suppressEvent !== false){
17494             this.fireEvent("beforechildrenrendered", this);
17495         }
17496         var cs = this.childNodes;
17497         for(var i = 0, len = cs.length; i < len; i++){
17498             cs[i].render(true);
17499         }
17500         this.childrenRendered = true;
17501     },
17502
17503     // private
17504     sort : function(fn, scope){
17505         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17506         if(this.childrenRendered){
17507             var cs = this.childNodes;
17508             for(var i = 0, len = cs.length; i < len; i++){
17509                 cs[i].render(true);
17510             }
17511         }
17512     },
17513
17514     // private
17515     render : function(bulkRender){
17516         this.ui.render(bulkRender);
17517         if(!this.rendered){
17518             this.rendered = true;
17519             if(this.expanded){
17520                 this.expanded = false;
17521                 this.expand(false, false);
17522             }
17523         }
17524     },
17525
17526     // private
17527     renderIndent : function(deep, refresh){
17528         if(refresh){
17529             this.ui.childIndent = null;
17530         }
17531         this.ui.renderIndent();
17532         if(deep === true && this.childrenRendered){
17533             var cs = this.childNodes;
17534             for(var i = 0, len = cs.length; i < len; i++){
17535                 cs[i].renderIndent(true, refresh);
17536             }
17537         }
17538     }
17539 });/*
17540  * Based on:
17541  * Ext JS Library 1.1.1
17542  * Copyright(c) 2006-2007, Ext JS, LLC.
17543  *
17544  * Originally Released Under LGPL - original licence link has changed is not relivant.
17545  *
17546  * Fork - LGPL
17547  * <script type="text/javascript">
17548  */
17549  
17550 /**
17551  * @class Roo.tree.AsyncTreeNode
17552  * @extends Roo.tree.TreeNode
17553  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17554  * @constructor
17555  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17556  */
17557  Roo.tree.AsyncTreeNode = function(config){
17558     this.loaded = false;
17559     this.loading = false;
17560     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17561     /**
17562     * @event beforeload
17563     * Fires before this node is loaded, return false to cancel
17564     * @param {Node} this This node
17565     */
17566     this.addEvents({'beforeload':true, 'load': true});
17567     /**
17568     * @event load
17569     * Fires when this node is loaded
17570     * @param {Node} this This node
17571     */
17572     /**
17573      * The loader used by this node (defaults to using the tree's defined loader)
17574      * @type TreeLoader
17575      * @property loader
17576      */
17577 };
17578 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17579     expand : function(deep, anim, callback){
17580         if(this.loading){ // if an async load is already running, waiting til it's done
17581             var timer;
17582             var f = function(){
17583                 if(!this.loading){ // done loading
17584                     clearInterval(timer);
17585                     this.expand(deep, anim, callback);
17586                 }
17587             }.createDelegate(this);
17588             timer = setInterval(f, 200);
17589             return;
17590         }
17591         if(!this.loaded){
17592             if(this.fireEvent("beforeload", this) === false){
17593                 return;
17594             }
17595             this.loading = true;
17596             this.ui.beforeLoad(this);
17597             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17598             if(loader){
17599                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17600                 return;
17601             }
17602         }
17603         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17604     },
17605     
17606     /**
17607      * Returns true if this node is currently loading
17608      * @return {Boolean}
17609      */
17610     isLoading : function(){
17611         return this.loading;  
17612     },
17613     
17614     loadComplete : function(deep, anim, callback){
17615         this.loading = false;
17616         this.loaded = true;
17617         this.ui.afterLoad(this);
17618         this.fireEvent("load", this);
17619         this.expand(deep, anim, callback);
17620     },
17621     
17622     /**
17623      * Returns true if this node has been loaded
17624      * @return {Boolean}
17625      */
17626     isLoaded : function(){
17627         return this.loaded;
17628     },
17629     
17630     hasChildNodes : function(){
17631         if(!this.isLeaf() && !this.loaded){
17632             return true;
17633         }else{
17634             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17635         }
17636     },
17637
17638     /**
17639      * Trigger a reload for this node
17640      * @param {Function} callback
17641      */
17642     reload : function(callback){
17643         this.collapse(false, false);
17644         while(this.firstChild){
17645             this.removeChild(this.firstChild);
17646         }
17647         this.childrenRendered = false;
17648         this.loaded = false;
17649         if(this.isHiddenRoot()){
17650             this.expanded = false;
17651         }
17652         this.expand(false, false, callback);
17653     }
17654 });/*
17655  * Based on:
17656  * Ext JS Library 1.1.1
17657  * Copyright(c) 2006-2007, Ext JS, LLC.
17658  *
17659  * Originally Released Under LGPL - original licence link has changed is not relivant.
17660  *
17661  * Fork - LGPL
17662  * <script type="text/javascript">
17663  */
17664  
17665 /**
17666  * @class Roo.tree.TreeNodeUI
17667  * @constructor
17668  * @param {Object} node The node to render
17669  * The TreeNode UI implementation is separate from the
17670  * tree implementation. Unless you are customizing the tree UI,
17671  * you should never have to use this directly.
17672  */
17673 Roo.tree.TreeNodeUI = function(node){
17674     this.node = node;
17675     this.rendered = false;
17676     this.animating = false;
17677     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17678 };
17679
17680 Roo.tree.TreeNodeUI.prototype = {
17681     removeChild : function(node){
17682         if(this.rendered){
17683             this.ctNode.removeChild(node.ui.getEl());
17684         }
17685     },
17686
17687     beforeLoad : function(){
17688          this.addClass("x-tree-node-loading");
17689     },
17690
17691     afterLoad : function(){
17692          this.removeClass("x-tree-node-loading");
17693     },
17694
17695     onTextChange : function(node, text, oldText){
17696         if(this.rendered){
17697             this.textNode.innerHTML = text;
17698         }
17699     },
17700
17701     onDisableChange : function(node, state){
17702         this.disabled = state;
17703         if(state){
17704             this.addClass("x-tree-node-disabled");
17705         }else{
17706             this.removeClass("x-tree-node-disabled");
17707         }
17708     },
17709
17710     onSelectedChange : function(state){
17711         if(state){
17712             this.focus();
17713             this.addClass("x-tree-selected");
17714         }else{
17715             //this.blur();
17716             this.removeClass("x-tree-selected");
17717         }
17718     },
17719
17720     onMove : function(tree, node, oldParent, newParent, index, refNode){
17721         this.childIndent = null;
17722         if(this.rendered){
17723             var targetNode = newParent.ui.getContainer();
17724             if(!targetNode){//target not rendered
17725                 this.holder = document.createElement("div");
17726                 this.holder.appendChild(this.wrap);
17727                 return;
17728             }
17729             var insertBefore = refNode ? refNode.ui.getEl() : null;
17730             if(insertBefore){
17731                 targetNode.insertBefore(this.wrap, insertBefore);
17732             }else{
17733                 targetNode.appendChild(this.wrap);
17734             }
17735             this.node.renderIndent(true);
17736         }
17737     },
17738
17739     addClass : function(cls){
17740         if(this.elNode){
17741             Roo.fly(this.elNode).addClass(cls);
17742         }
17743     },
17744
17745     removeClass : function(cls){
17746         if(this.elNode){
17747             Roo.fly(this.elNode).removeClass(cls);
17748         }
17749     },
17750
17751     remove : function(){
17752         if(this.rendered){
17753             this.holder = document.createElement("div");
17754             this.holder.appendChild(this.wrap);
17755         }
17756     },
17757
17758     fireEvent : function(){
17759         return this.node.fireEvent.apply(this.node, arguments);
17760     },
17761
17762     initEvents : function(){
17763         this.node.on("move", this.onMove, this);
17764         var E = Roo.EventManager;
17765         var a = this.anchor;
17766
17767         var el = Roo.fly(a, '_treeui');
17768
17769         if(Roo.isOpera){ // opera render bug ignores the CSS
17770             el.setStyle("text-decoration", "none");
17771         }
17772
17773         el.on("click", this.onClick, this);
17774         el.on("dblclick", this.onDblClick, this);
17775
17776         if(this.checkbox){
17777             Roo.EventManager.on(this.checkbox,
17778                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17779         }
17780
17781         el.on("contextmenu", this.onContextMenu, this);
17782
17783         var icon = Roo.fly(this.iconNode);
17784         icon.on("click", this.onClick, this);
17785         icon.on("dblclick", this.onDblClick, this);
17786         icon.on("contextmenu", this.onContextMenu, this);
17787         E.on(this.ecNode, "click", this.ecClick, this, true);
17788
17789         if(this.node.disabled){
17790             this.addClass("x-tree-node-disabled");
17791         }
17792         if(this.node.hidden){
17793             this.addClass("x-tree-node-disabled");
17794         }
17795         var ot = this.node.getOwnerTree();
17796         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17797         if(dd && (!this.node.isRoot || ot.rootVisible)){
17798             Roo.dd.Registry.register(this.elNode, {
17799                 node: this.node,
17800                 handles: this.getDDHandles(),
17801                 isHandle: false
17802             });
17803         }
17804     },
17805
17806     getDDHandles : function(){
17807         return [this.iconNode, this.textNode];
17808     },
17809
17810     hide : function(){
17811         if(this.rendered){
17812             this.wrap.style.display = "none";
17813         }
17814     },
17815
17816     show : function(){
17817         if(this.rendered){
17818             this.wrap.style.display = "";
17819         }
17820     },
17821
17822     onContextMenu : function(e){
17823         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17824             e.preventDefault();
17825             this.focus();
17826             this.fireEvent("contextmenu", this.node, e);
17827         }
17828     },
17829
17830     onClick : function(e){
17831         if(this.dropping){
17832             e.stopEvent();
17833             return;
17834         }
17835         if(this.fireEvent("beforeclick", this.node, e) !== false){
17836             if(!this.disabled && this.node.attributes.href){
17837                 this.fireEvent("click", this.node, e);
17838                 return;
17839             }
17840             e.preventDefault();
17841             if(this.disabled){
17842                 return;
17843             }
17844
17845             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17846                 this.node.toggle();
17847             }
17848
17849             this.fireEvent("click", this.node, e);
17850         }else{
17851             e.stopEvent();
17852         }
17853     },
17854
17855     onDblClick : function(e){
17856         e.preventDefault();
17857         if(this.disabled){
17858             return;
17859         }
17860         if(this.checkbox){
17861             this.toggleCheck();
17862         }
17863         if(!this.animating && this.node.hasChildNodes()){
17864             this.node.toggle();
17865         }
17866         this.fireEvent("dblclick", this.node, e);
17867     },
17868
17869     onCheckChange : function(){
17870         var checked = this.checkbox.checked;
17871         this.node.attributes.checked = checked;
17872         this.fireEvent('checkchange', this.node, checked);
17873     },
17874
17875     ecClick : function(e){
17876         if(!this.animating && this.node.hasChildNodes()){
17877             this.node.toggle();
17878         }
17879     },
17880
17881     startDrop : function(){
17882         this.dropping = true;
17883     },
17884
17885     // delayed drop so the click event doesn't get fired on a drop
17886     endDrop : function(){
17887        setTimeout(function(){
17888            this.dropping = false;
17889        }.createDelegate(this), 50);
17890     },
17891
17892     expand : function(){
17893         this.updateExpandIcon();
17894         this.ctNode.style.display = "";
17895     },
17896
17897     focus : function(){
17898         if(!this.node.preventHScroll){
17899             try{this.anchor.focus();
17900             }catch(e){}
17901         }else if(!Roo.isIE){
17902             try{
17903                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17904                 var l = noscroll.scrollLeft;
17905                 this.anchor.focus();
17906                 noscroll.scrollLeft = l;
17907             }catch(e){}
17908         }
17909     },
17910
17911     toggleCheck : function(value){
17912         var cb = this.checkbox;
17913         if(cb){
17914             cb.checked = (value === undefined ? !cb.checked : value);
17915         }
17916     },
17917
17918     blur : function(){
17919         try{
17920             this.anchor.blur();
17921         }catch(e){}
17922     },
17923
17924     animExpand : function(callback){
17925         var ct = Roo.get(this.ctNode);
17926         ct.stopFx();
17927         if(!this.node.hasChildNodes()){
17928             this.updateExpandIcon();
17929             this.ctNode.style.display = "";
17930             Roo.callback(callback);
17931             return;
17932         }
17933         this.animating = true;
17934         this.updateExpandIcon();
17935
17936         ct.slideIn('t', {
17937            callback : function(){
17938                this.animating = false;
17939                Roo.callback(callback);
17940             },
17941             scope: this,
17942             duration: this.node.ownerTree.duration || .25
17943         });
17944     },
17945
17946     highlight : function(){
17947         var tree = this.node.getOwnerTree();
17948         Roo.fly(this.wrap).highlight(
17949             tree.hlColor || "C3DAF9",
17950             {endColor: tree.hlBaseColor}
17951         );
17952     },
17953
17954     collapse : function(){
17955         this.updateExpandIcon();
17956         this.ctNode.style.display = "none";
17957     },
17958
17959     animCollapse : function(callback){
17960         var ct = Roo.get(this.ctNode);
17961         ct.enableDisplayMode('block');
17962         ct.stopFx();
17963
17964         this.animating = true;
17965         this.updateExpandIcon();
17966
17967         ct.slideOut('t', {
17968             callback : function(){
17969                this.animating = false;
17970                Roo.callback(callback);
17971             },
17972             scope: this,
17973             duration: this.node.ownerTree.duration || .25
17974         });
17975     },
17976
17977     getContainer : function(){
17978         return this.ctNode;
17979     },
17980
17981     getEl : function(){
17982         return this.wrap;
17983     },
17984
17985     appendDDGhost : function(ghostNode){
17986         ghostNode.appendChild(this.elNode.cloneNode(true));
17987     },
17988
17989     getDDRepairXY : function(){
17990         return Roo.lib.Dom.getXY(this.iconNode);
17991     },
17992
17993     onRender : function(){
17994         this.render();
17995     },
17996
17997     render : function(bulkRender){
17998         var n = this.node, a = n.attributes;
17999         var targetNode = n.parentNode ?
18000               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18001
18002         if(!this.rendered){
18003             this.rendered = true;
18004
18005             this.renderElements(n, a, targetNode, bulkRender);
18006
18007             if(a.qtip){
18008                if(this.textNode.setAttributeNS){
18009                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18010                    if(a.qtipTitle){
18011                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18012                    }
18013                }else{
18014                    this.textNode.setAttribute("ext:qtip", a.qtip);
18015                    if(a.qtipTitle){
18016                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18017                    }
18018                }
18019             }else if(a.qtipCfg){
18020                 a.qtipCfg.target = Roo.id(this.textNode);
18021                 Roo.QuickTips.register(a.qtipCfg);
18022             }
18023             this.initEvents();
18024             if(!this.node.expanded){
18025                 this.updateExpandIcon();
18026             }
18027         }else{
18028             if(bulkRender === true) {
18029                 targetNode.appendChild(this.wrap);
18030             }
18031         }
18032     },
18033
18034     renderElements : function(n, a, targetNode, bulkRender){
18035         // add some indent caching, this helps performance when rendering a large tree
18036         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18037         var t = n.getOwnerTree();
18038         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18039         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18040         var cb = typeof a.checked == 'boolean';
18041         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18042         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18043             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18044             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18045             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18046             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18047             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18048              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18049                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18050             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18051             "</li>"];
18052
18053         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18054             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18055                                 n.nextSibling.ui.getEl(), buf.join(""));
18056         }else{
18057             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18058         }
18059
18060         this.elNode = this.wrap.childNodes[0];
18061         this.ctNode = this.wrap.childNodes[1];
18062         var cs = this.elNode.childNodes;
18063         this.indentNode = cs[0];
18064         this.ecNode = cs[1];
18065         this.iconNode = cs[2];
18066         var index = 3;
18067         if(cb){
18068             this.checkbox = cs[3];
18069             index++;
18070         }
18071         this.anchor = cs[index];
18072         this.textNode = cs[index].firstChild;
18073     },
18074
18075     getAnchor : function(){
18076         return this.anchor;
18077     },
18078
18079     getTextEl : function(){
18080         return this.textNode;
18081     },
18082
18083     getIconEl : function(){
18084         return this.iconNode;
18085     },
18086
18087     isChecked : function(){
18088         return this.checkbox ? this.checkbox.checked : false;
18089     },
18090
18091     updateExpandIcon : function(){
18092         if(this.rendered){
18093             var n = this.node, c1, c2;
18094             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18095             var hasChild = n.hasChildNodes();
18096             if(hasChild){
18097                 if(n.expanded){
18098                     cls += "-minus";
18099                     c1 = "x-tree-node-collapsed";
18100                     c2 = "x-tree-node-expanded";
18101                 }else{
18102                     cls += "-plus";
18103                     c1 = "x-tree-node-expanded";
18104                     c2 = "x-tree-node-collapsed";
18105                 }
18106                 if(this.wasLeaf){
18107                     this.removeClass("x-tree-node-leaf");
18108                     this.wasLeaf = false;
18109                 }
18110                 if(this.c1 != c1 || this.c2 != c2){
18111                     Roo.fly(this.elNode).replaceClass(c1, c2);
18112                     this.c1 = c1; this.c2 = c2;
18113                 }
18114             }else{
18115                 if(!this.wasLeaf){
18116                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18117                     delete this.c1;
18118                     delete this.c2;
18119                     this.wasLeaf = true;
18120                 }
18121             }
18122             var ecc = "x-tree-ec-icon "+cls;
18123             if(this.ecc != ecc){
18124                 this.ecNode.className = ecc;
18125                 this.ecc = ecc;
18126             }
18127         }
18128     },
18129
18130     getChildIndent : function(){
18131         if(!this.childIndent){
18132             var buf = [];
18133             var p = this.node;
18134             while(p){
18135                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18136                     if(!p.isLast()) {
18137                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18138                     } else {
18139                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18140                     }
18141                 }
18142                 p = p.parentNode;
18143             }
18144             this.childIndent = buf.join("");
18145         }
18146         return this.childIndent;
18147     },
18148
18149     renderIndent : function(){
18150         if(this.rendered){
18151             var indent = "";
18152             var p = this.node.parentNode;
18153             if(p){
18154                 indent = p.ui.getChildIndent();
18155             }
18156             if(this.indentMarkup != indent){ // don't rerender if not required
18157                 this.indentNode.innerHTML = indent;
18158                 this.indentMarkup = indent;
18159             }
18160             this.updateExpandIcon();
18161         }
18162     }
18163 };
18164
18165 Roo.tree.RootTreeNodeUI = function(){
18166     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18167 };
18168 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18169     render : function(){
18170         if(!this.rendered){
18171             var targetNode = this.node.ownerTree.innerCt.dom;
18172             this.node.expanded = true;
18173             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18174             this.wrap = this.ctNode = targetNode.firstChild;
18175         }
18176     },
18177     collapse : function(){
18178     },
18179     expand : function(){
18180     }
18181 });/*
18182  * Based on:
18183  * Ext JS Library 1.1.1
18184  * Copyright(c) 2006-2007, Ext JS, LLC.
18185  *
18186  * Originally Released Under LGPL - original licence link has changed is not relivant.
18187  *
18188  * Fork - LGPL
18189  * <script type="text/javascript">
18190  */
18191 /**
18192  * @class Roo.tree.TreeLoader
18193  * @extends Roo.util.Observable
18194  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18195  * nodes from a specified URL. The response must be a javascript Array definition
18196  * who's elements are node definition objects. eg:
18197  * <pre><code>
18198    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18199     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18200 </code></pre>
18201  * <br><br>
18202  * A server request is sent, and child nodes are loaded only when a node is expanded.
18203  * The loading node's id is passed to the server under the parameter name "node" to
18204  * enable the server to produce the correct child nodes.
18205  * <br><br>
18206  * To pass extra parameters, an event handler may be attached to the "beforeload"
18207  * event, and the parameters specified in the TreeLoader's baseParams property:
18208  * <pre><code>
18209     myTreeLoader.on("beforeload", function(treeLoader, node) {
18210         this.baseParams.category = node.attributes.category;
18211     }, this);
18212 </code></pre><
18213  * This would pass an HTTP parameter called "category" to the server containing
18214  * the value of the Node's "category" attribute.
18215  * @constructor
18216  * Creates a new Treeloader.
18217  * @param {Object} config A config object containing config properties.
18218  */
18219 Roo.tree.TreeLoader = function(config){
18220     this.baseParams = {};
18221     this.requestMethod = "POST";
18222     Roo.apply(this, config);
18223
18224     this.addEvents({
18225     
18226         /**
18227          * @event beforeload
18228          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18229          * @param {Object} This TreeLoader object.
18230          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18231          * @param {Object} callback The callback function specified in the {@link #load} call.
18232          */
18233         beforeload : true,
18234         /**
18235          * @event load
18236          * Fires when the node has been successfuly loaded.
18237          * @param {Object} This TreeLoader object.
18238          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18239          * @param {Object} response The response object containing the data from the server.
18240          */
18241         load : true,
18242         /**
18243          * @event loadexception
18244          * Fires if the network request failed.
18245          * @param {Object} This TreeLoader object.
18246          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18247          * @param {Object} response The response object containing the data from the server.
18248          */
18249         loadexception : true,
18250         /**
18251          * @event create
18252          * Fires before a node is created, enabling you to return custom Node types 
18253          * @param {Object} This TreeLoader object.
18254          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18255          */
18256         create : true
18257     });
18258
18259     Roo.tree.TreeLoader.superclass.constructor.call(this);
18260 };
18261
18262 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18263     /**
18264     * @cfg {String} dataUrl The URL from which to request a Json string which
18265     * specifies an array of node definition object representing the child nodes
18266     * to be loaded.
18267     */
18268     /**
18269     * @cfg {Object} baseParams (optional) An object containing properties which
18270     * specify HTTP parameters to be passed to each request for child nodes.
18271     */
18272     /**
18273     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18274     * created by this loader. If the attributes sent by the server have an attribute in this object,
18275     * they take priority.
18276     */
18277     /**
18278     * @cfg {Object} uiProviders (optional) An object containing properties which
18279     * 
18280     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18281     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18282     * <i>uiProvider</i> attribute of a returned child node is a string rather
18283     * than a reference to a TreeNodeUI implementation, this that string value
18284     * is used as a property name in the uiProviders object. You can define the provider named
18285     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18286     */
18287     uiProviders : {},
18288
18289     /**
18290     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18291     * child nodes before loading.
18292     */
18293     clearOnLoad : true,
18294
18295     /**
18296     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18297     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18298     * Grid query { data : [ .....] }
18299     */
18300     
18301     root : false,
18302      /**
18303     * @cfg {String} queryParam (optional) 
18304     * Name of the query as it will be passed on the querystring (defaults to 'node')
18305     * eg. the request will be ?node=[id]
18306     */
18307     
18308     
18309     queryParam: false,
18310     
18311     /**
18312      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18313      * This is called automatically when a node is expanded, but may be used to reload
18314      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18315      * @param {Roo.tree.TreeNode} node
18316      * @param {Function} callback
18317      */
18318     load : function(node, callback){
18319         if(this.clearOnLoad){
18320             while(node.firstChild){
18321                 node.removeChild(node.firstChild);
18322             }
18323         }
18324         if(node.attributes.children){ // preloaded json children
18325             var cs = node.attributes.children;
18326             for(var i = 0, len = cs.length; i < len; i++){
18327                 node.appendChild(this.createNode(cs[i]));
18328             }
18329             if(typeof callback == "function"){
18330                 callback();
18331             }
18332         }else if(this.dataUrl){
18333             this.requestData(node, callback);
18334         }
18335     },
18336
18337     getParams: function(node){
18338         var buf = [], bp = this.baseParams;
18339         for(var key in bp){
18340             if(typeof bp[key] != "function"){
18341                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18342             }
18343         }
18344         var n = this.queryParam === false ? 'node' : this.queryParam;
18345         buf.push(n + "=", encodeURIComponent(node.id));
18346         return buf.join("");
18347     },
18348
18349     requestData : function(node, callback){
18350         if(this.fireEvent("beforeload", this, node, callback) !== false){
18351             this.transId = Roo.Ajax.request({
18352                 method:this.requestMethod,
18353                 url: this.dataUrl||this.url,
18354                 success: this.handleResponse,
18355                 failure: this.handleFailure,
18356                 scope: this,
18357                 argument: {callback: callback, node: node},
18358                 params: this.getParams(node)
18359             });
18360         }else{
18361             // if the load is cancelled, make sure we notify
18362             // the node that we are done
18363             if(typeof callback == "function"){
18364                 callback();
18365             }
18366         }
18367     },
18368
18369     isLoading : function(){
18370         return this.transId ? true : false;
18371     },
18372
18373     abort : function(){
18374         if(this.isLoading()){
18375             Roo.Ajax.abort(this.transId);
18376         }
18377     },
18378
18379     // private
18380     createNode : function(attr){
18381         // apply baseAttrs, nice idea Corey!
18382         if(this.baseAttrs){
18383             Roo.applyIf(attr, this.baseAttrs);
18384         }
18385         if(this.applyLoader !== false){
18386             attr.loader = this;
18387         }
18388         // uiProvider = depreciated..
18389         
18390         if(typeof(attr.uiProvider) == 'string'){
18391            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18392                 /**  eval:var:attr */ eval(attr.uiProvider);
18393         }
18394         if(typeof(this.uiProviders['default']) != 'undefined') {
18395             attr.uiProvider = this.uiProviders['default'];
18396         }
18397         
18398         this.fireEvent('create', this, attr);
18399         
18400         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18401         return(attr.leaf ?
18402                         new Roo.tree.TreeNode(attr) :
18403                         new Roo.tree.AsyncTreeNode(attr));
18404     },
18405
18406     processResponse : function(response, node, callback){
18407         var json = response.responseText;
18408         try {
18409             
18410             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18411             if (this.root !== false) {
18412                 o = o[this.root];
18413             }
18414             
18415             for(var i = 0, len = o.length; i < len; i++){
18416                 var n = this.createNode(o[i]);
18417                 if(n){
18418                     node.appendChild(n);
18419                 }
18420             }
18421             if(typeof callback == "function"){
18422                 callback(this, node);
18423             }
18424         }catch(e){
18425             this.handleFailure(response);
18426         }
18427     },
18428
18429     handleResponse : function(response){
18430         this.transId = false;
18431         var a = response.argument;
18432         this.processResponse(response, a.node, a.callback);
18433         this.fireEvent("load", this, a.node, response);
18434     },
18435
18436     handleFailure : function(response){
18437         this.transId = false;
18438         var a = response.argument;
18439         this.fireEvent("loadexception", this, a.node, response);
18440         if(typeof a.callback == "function"){
18441             a.callback(this, a.node);
18442         }
18443     }
18444 });/*
18445  * Based on:
18446  * Ext JS Library 1.1.1
18447  * Copyright(c) 2006-2007, Ext JS, LLC.
18448  *
18449  * Originally Released Under LGPL - original licence link has changed is not relivant.
18450  *
18451  * Fork - LGPL
18452  * <script type="text/javascript">
18453  */
18454
18455 /**
18456 * @class Roo.tree.TreeFilter
18457 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18458 * @param {TreePanel} tree
18459 * @param {Object} config (optional)
18460  */
18461 Roo.tree.TreeFilter = function(tree, config){
18462     this.tree = tree;
18463     this.filtered = {};
18464     Roo.apply(this, config);
18465 };
18466
18467 Roo.tree.TreeFilter.prototype = {
18468     clearBlank:false,
18469     reverse:false,
18470     autoClear:false,
18471     remove:false,
18472
18473      /**
18474      * Filter the data by a specific attribute.
18475      * @param {String/RegExp} value Either string that the attribute value
18476      * should start with or a RegExp to test against the attribute
18477      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18478      * @param {TreeNode} startNode (optional) The node to start the filter at.
18479      */
18480     filter : function(value, attr, startNode){
18481         attr = attr || "text";
18482         var f;
18483         if(typeof value == "string"){
18484             var vlen = value.length;
18485             // auto clear empty filter
18486             if(vlen == 0 && this.clearBlank){
18487                 this.clear();
18488                 return;
18489             }
18490             value = value.toLowerCase();
18491             f = function(n){
18492                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18493             };
18494         }else if(value.exec){ // regex?
18495             f = function(n){
18496                 return value.test(n.attributes[attr]);
18497             };
18498         }else{
18499             throw 'Illegal filter type, must be string or regex';
18500         }
18501         this.filterBy(f, null, startNode);
18502         },
18503
18504     /**
18505      * Filter by a function. The passed function will be called with each
18506      * node in the tree (or from the startNode). If the function returns true, the node is kept
18507      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18508      * @param {Function} fn The filter function
18509      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18510      */
18511     filterBy : function(fn, scope, startNode){
18512         startNode = startNode || this.tree.root;
18513         if(this.autoClear){
18514             this.clear();
18515         }
18516         var af = this.filtered, rv = this.reverse;
18517         var f = function(n){
18518             if(n == startNode){
18519                 return true;
18520             }
18521             if(af[n.id]){
18522                 return false;
18523             }
18524             var m = fn.call(scope || n, n);
18525             if(!m || rv){
18526                 af[n.id] = n;
18527                 n.ui.hide();
18528                 return false;
18529             }
18530             return true;
18531         };
18532         startNode.cascade(f);
18533         if(this.remove){
18534            for(var id in af){
18535                if(typeof id != "function"){
18536                    var n = af[id];
18537                    if(n && n.parentNode){
18538                        n.parentNode.removeChild(n);
18539                    }
18540                }
18541            }
18542         }
18543     },
18544
18545     /**
18546      * Clears the current filter. Note: with the "remove" option
18547      * set a filter cannot be cleared.
18548      */
18549     clear : function(){
18550         var t = this.tree;
18551         var af = this.filtered;
18552         for(var id in af){
18553             if(typeof id != "function"){
18554                 var n = af[id];
18555                 if(n){
18556                     n.ui.show();
18557                 }
18558             }
18559         }
18560         this.filtered = {};
18561     }
18562 };