roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     
76 };
77
78 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
79
80     /**
81      * The id of the element associated with this object.  This is what we
82      * refer to as the "linked element" because the size and position of
83      * this element is used to determine when the drag and drop objects have
84      * interacted.
85      * @property id
86      * @type String
87      */
88     id: null,
89
90     /**
91      * Configuration attributes passed into the constructor
92      * @property config
93      * @type object
94      */
95     config: null,
96
97     /**
98      * The id of the element that will be dragged.  By default this is same
99      * as the linked element , but could be changed to another element. Ex:
100      * Roo.dd.DDProxy
101      * @property dragElId
102      * @type String
103      * @private
104      */
105     dragElId: null,
106
107     /**
108      * the id of the element that initiates the drag operation.  By default
109      * this is the linked element, but could be changed to be a child of this
110      * element.  This lets us do things like only starting the drag when the
111      * header element within the linked html element is clicked.
112      * @property handleElId
113      * @type String
114      * @private
115      */
116     handleElId: null,
117
118     /**
119      * An associative array of HTML tags that will be ignored if clicked.
120      * @property invalidHandleTypes
121      * @type {string: string}
122      */
123     invalidHandleTypes: null,
124
125     /**
126      * An associative array of ids for elements that will be ignored if clicked
127      * @property invalidHandleIds
128      * @type {string: string}
129      */
130     invalidHandleIds: null,
131
132     /**
133      * An indexted array of css class names for elements that will be ignored
134      * if clicked.
135      * @property invalidHandleClasses
136      * @type string[]
137      */
138     invalidHandleClasses: null,
139
140     /**
141      * The linked element's absolute X position at the time the drag was
142      * started
143      * @property startPageX
144      * @type int
145      * @private
146      */
147     startPageX: 0,
148
149     /**
150      * The linked element's absolute X position at the time the drag was
151      * started
152      * @property startPageY
153      * @type int
154      * @private
155      */
156     startPageY: 0,
157
158     /**
159      * The group defines a logical collection of DragDrop objects that are
160      * related.  Instances only get events when interacting with other
161      * DragDrop object in the same group.  This lets us define multiple
162      * groups using a single DragDrop subclass if we want.
163      * @property groups
164      * @type {string: string}
165      */
166     groups: null,
167
168     /**
169      * Individual drag/drop instances can be locked.  This will prevent
170      * onmousedown start drag.
171      * @property locked
172      * @type boolean
173      * @private
174      */
175     locked: false,
176
177     /**
178      * Lock this instance
179      * @method lock
180      */
181     lock: function() { this.locked = true; },
182
183     /**
184      * Unlock this instace
185      * @method unlock
186      */
187     unlock: function() { this.locked = false; },
188
189     /**
190      * By default, all insances can be a drop target.  This can be disabled by
191      * setting isTarget to false.
192      * @method isTarget
193      * @type boolean
194      */
195     isTarget: true,
196
197     /**
198      * The padding configured for this drag and drop object for calculating
199      * the drop zone intersection with this object.
200      * @method padding
201      * @type int[]
202      */
203     padding: null,
204
205     /**
206      * Cached reference to the linked element
207      * @property _domRef
208      * @private
209      */
210     _domRef: null,
211
212     /**
213      * Internal typeof flag
214      * @property __ygDragDrop
215      * @private
216      */
217     __ygDragDrop: true,
218
219     /**
220      * Set to true when horizontal contraints are applied
221      * @property constrainX
222      * @type boolean
223      * @private
224      */
225     constrainX: false,
226
227     /**
228      * Set to true when vertical contraints are applied
229      * @property constrainY
230      * @type boolean
231      * @private
232      */
233     constrainY: false,
234
235     /**
236      * The left constraint
237      * @property minX
238      * @type int
239      * @private
240      */
241     minX: 0,
242
243     /**
244      * The right constraint
245      * @property maxX
246      * @type int
247      * @private
248      */
249     maxX: 0,
250
251     /**
252      * The up constraint
253      * @property minY
254      * @type int
255      * @type int
256      * @private
257      */
258     minY: 0,
259
260     /**
261      * The down constraint
262      * @property maxY
263      * @type int
264      * @private
265      */
266     maxY: 0,
267
268     /**
269      * Maintain offsets when we resetconstraints.  Set to true when you want
270      * the position of the element relative to its parent to stay the same
271      * when the page changes
272      *
273      * @property maintainOffset
274      * @type boolean
275      */
276     maintainOffset: false,
277
278     /**
279      * Array of pixel locations the element will snap to if we specified a
280      * horizontal graduation/interval.  This array is generated automatically
281      * when you define a tick interval.
282      * @property xTicks
283      * @type int[]
284      */
285     xTicks: null,
286
287     /**
288      * Array of pixel locations the element will snap to if we specified a
289      * vertical graduation/interval.  This array is generated automatically
290      * when you define a tick interval.
291      * @property yTicks
292      * @type int[]
293      */
294     yTicks: null,
295
296     /**
297      * By default the drag and drop instance will only respond to the primary
298      * button click (left button for a right-handed mouse).  Set to true to
299      * allow drag and drop to start with any mouse click that is propogated
300      * by the browser
301      * @property primaryButtonOnly
302      * @type boolean
303      */
304     primaryButtonOnly: true,
305
306     /**
307      * The availabe property is false until the linked dom element is accessible.
308      * @property available
309      * @type boolean
310      */
311     available: false,
312
313     /**
314      * By default, drags can only be initiated if the mousedown occurs in the
315      * region the linked element is.  This is done in part to work around a
316      * bug in some browsers that mis-report the mousedown if the previous
317      * mouseup happened outside of the window.  This property is set to true
318      * if outer handles are defined.
319      *
320      * @property hasOuterHandles
321      * @type boolean
322      * @default false
323      */
324     hasOuterHandles: false,
325
326     /**
327      * Code that executes immediately before the startDrag event
328      * @method b4StartDrag
329      * @private
330      */
331     b4StartDrag: function(x, y) { },
332
333     /**
334      * Abstract method called after a drag/drop object is clicked
335      * and the drag or mousedown time thresholds have beeen met.
336      * @method startDrag
337      * @param {int} X click location
338      * @param {int} Y click location
339      */
340     startDrag: function(x, y) { /* override this */ },
341
342     /**
343      * Code that executes immediately before the onDrag event
344      * @method b4Drag
345      * @private
346      */
347     b4Drag: function(e) { },
348
349     /**
350      * Abstract method called during the onMouseMove event while dragging an
351      * object.
352      * @method onDrag
353      * @param {Event} e the mousemove event
354      */
355     onDrag: function(e) { /* override this */ },
356
357     /**
358      * Abstract method called when this element fist begins hovering over
359      * another DragDrop obj
360      * @method onDragEnter
361      * @param {Event} e the mousemove event
362      * @param {String|DragDrop[]} id In POINT mode, the element
363      * id this is hovering over.  In INTERSECT mode, an array of one or more
364      * dragdrop items being hovered over.
365      */
366     onDragEnter: function(e, id) { /* override this */ },
367
368     /**
369      * Code that executes immediately before the onDragOver event
370      * @method b4DragOver
371      * @private
372      */
373     b4DragOver: function(e) { },
374
375     /**
376      * Abstract method called when this element is hovering over another
377      * DragDrop obj
378      * @method onDragOver
379      * @param {Event} e the mousemove event
380      * @param {String|DragDrop[]} id In POINT mode, the element
381      * id this is hovering over.  In INTERSECT mode, an array of dd items
382      * being hovered over.
383      */
384     onDragOver: function(e, id) { /* override this */ },
385
386     /**
387      * Code that executes immediately before the onDragOut event
388      * @method b4DragOut
389      * @private
390      */
391     b4DragOut: function(e) { },
392
393     /**
394      * Abstract method called when we are no longer hovering over an element
395      * @method onDragOut
396      * @param {Event} e the mousemove event
397      * @param {String|DragDrop[]} id In POINT mode, the element
398      * id this was hovering over.  In INTERSECT mode, an array of dd items
399      * that the mouse is no longer over.
400      */
401     onDragOut: function(e, id) { /* override this */ },
402
403     /**
404      * Code that executes immediately before the onDragDrop event
405      * @method b4DragDrop
406      * @private
407      */
408     b4DragDrop: function(e) { },
409
410     /**
411      * Abstract method called when this item is dropped on another DragDrop
412      * obj
413      * @method onDragDrop
414      * @param {Event} e the mouseup event
415      * @param {String|DragDrop[]} id In POINT mode, the element
416      * id this was dropped on.  In INTERSECT mode, an array of dd items this
417      * was dropped on.
418      */
419     onDragDrop: function(e, id) { /* override this */ },
420
421     /**
422      * Abstract method called when this item is dropped on an area with no
423      * drop target
424      * @method onInvalidDrop
425      * @param {Event} e the mouseup event
426      */
427     onInvalidDrop: function(e) { /* override this */ },
428
429     /**
430      * Code that executes immediately before the endDrag event
431      * @method b4EndDrag
432      * @private
433      */
434     b4EndDrag: function(e) { },
435
436     /**
437      * Fired when we are done dragging the object
438      * @method endDrag
439      * @param {Event} e the mouseup event
440      */
441     endDrag: function(e) { /* override this */ },
442
443     /**
444      * Code executed immediately before the onMouseDown event
445      * @method b4MouseDown
446      * @param {Event} e the mousedown event
447      * @private
448      */
449     b4MouseDown: function(e) {  },
450
451     /**
452      * Event handler that fires when a drag/drop obj gets a mousedown
453      * @method onMouseDown
454      * @param {Event} e the mousedown event
455      */
456     onMouseDown: function(e) { /* override this */ },
457
458     /**
459      * Event handler that fires when a drag/drop obj gets a mouseup
460      * @method onMouseUp
461      * @param {Event} e the mouseup event
462      */
463     onMouseUp: function(e) { /* override this */ },
464
465     /**
466      * Override the onAvailable method to do what is needed after the initial
467      * position was determined.
468      * @method onAvailable
469      */
470     onAvailable: function () {
471     },
472
473     /*
474      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
475      * @type Object
476      */
477     defaultPadding : {left:0, right:0, top:0, bottom:0},
478
479     /*
480      * Initializes the drag drop object's constraints to restrict movement to a certain element.
481  *
482  * Usage:
483  <pre><code>
484  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
485                 { dragElId: "existingProxyDiv" });
486  dd.startDrag = function(){
487      this.constrainTo("parent-id");
488  };
489  </code></pre>
490  * Or you can initalize it using the {@link Roo.Element} object:
491  <pre><code>
492  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
493      startDrag : function(){
494          this.constrainTo("parent-id");
495      }
496  });
497  </code></pre>
498      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
499      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
500      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
501      * an object containing the sides to pad. For example: {right:10, bottom:10}
502      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
503      */
504     constrainTo : function(constrainTo, pad, inContent){
505         if(typeof pad == "number"){
506             pad = {left: pad, right:pad, top:pad, bottom:pad};
507         }
508         pad = pad || this.defaultPadding;
509         var b = Roo.get(this.getEl()).getBox();
510         var ce = Roo.get(constrainTo);
511         var s = ce.getScroll();
512         var c, cd = ce.dom;
513         if(cd == document.body){
514             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
515         }else{
516             xy = ce.getXY();
517             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
518         }
519
520
521         var topSpace = b.y - c.y;
522         var leftSpace = b.x - c.x;
523
524         this.resetConstraints();
525         this.setXConstraint(leftSpace - (pad.left||0), // left
526                 c.width - leftSpace - b.width - (pad.right||0) //right
527         );
528         this.setYConstraint(topSpace - (pad.top||0), //top
529                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
530         );
531     },
532
533     /**
534      * Returns a reference to the linked element
535      * @method getEl
536      * @return {HTMLElement} the html element
537      */
538     getEl: function() {
539         if (!this._domRef) {
540             this._domRef = Roo.getDom(this.id);
541         }
542
543         return this._domRef;
544     },
545
546     /**
547      * Returns a reference to the actual element to drag.  By default this is
548      * the same as the html element, but it can be assigned to another
549      * element. An example of this can be found in Roo.dd.DDProxy
550      * @method getDragEl
551      * @return {HTMLElement} the html element
552      */
553     getDragEl: function() {
554         return Roo.getDom(this.dragElId);
555     },
556
557     /**
558      * Sets up the DragDrop object.  Must be called in the constructor of any
559      * Roo.dd.DragDrop subclass
560      * @method init
561      * @param id the id of the linked element
562      * @param {String} sGroup the group of related items
563      * @param {object} config configuration attributes
564      */
565     init: function(id, sGroup, config) {
566         this.initTarget(id, sGroup, config);
567         Event.on(this.id, "mousedown", this.handleMouseDown, this);
568         // Event.on(this.id, "selectstart", Event.preventDefault);
569     },
570
571     /**
572      * Initializes Targeting functionality only... the object does not
573      * get a mousedown handler.
574      * @method initTarget
575      * @param id the id of the linked element
576      * @param {String} sGroup the group of related items
577      * @param {object} config configuration attributes
578      */
579     initTarget: function(id, sGroup, config) {
580
581         // configuration attributes
582         this.config = config || {};
583
584         // create a local reference to the drag and drop manager
585         this.DDM = Roo.dd.DDM;
586         // initialize the groups array
587         this.groups = {};
588
589         // assume that we have an element reference instead of an id if the
590         // parameter is not a string
591         if (typeof id !== "string") {
592             id = Roo.id(id);
593         }
594
595         // set the id
596         this.id = id;
597
598         // add to an interaction group
599         this.addToGroup((sGroup) ? sGroup : "default");
600
601         // We don't want to register this as the handle with the manager
602         // so we just set the id rather than calling the setter.
603         this.handleElId = id;
604
605         // the linked element is the element that gets dragged by default
606         this.setDragElId(id);
607
608         // by default, clicked anchors will not start drag operations.
609         this.invalidHandleTypes = { A: "A" };
610         this.invalidHandleIds = {};
611         this.invalidHandleClasses = [];
612
613         this.applyConfig();
614
615         this.handleOnAvailable();
616     },
617
618     /**
619      * Applies the configuration parameters that were passed into the constructor.
620      * This is supposed to happen at each level through the inheritance chain.  So
621      * a DDProxy implentation will execute apply config on DDProxy, DD, and
622      * DragDrop in order to get all of the parameters that are available in
623      * each object.
624      * @method applyConfig
625      */
626     applyConfig: function() {
627
628         // configurable properties:
629         //    padding, isTarget, maintainOffset, primaryButtonOnly
630         this.padding           = this.config.padding || [0, 0, 0, 0];
631         this.isTarget          = (this.config.isTarget !== false);
632         this.maintainOffset    = (this.config.maintainOffset);
633         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
634
635     },
636
637     /**
638      * Executed when the linked element is available
639      * @method handleOnAvailable
640      * @private
641      */
642     handleOnAvailable: function() {
643         this.available = true;
644         this.resetConstraints();
645         this.onAvailable();
646     },
647
648      /**
649      * Configures the padding for the target zone in px.  Effectively expands
650      * (or reduces) the virtual object size for targeting calculations.
651      * Supports css-style shorthand; if only one parameter is passed, all sides
652      * will have that padding, and if only two are passed, the top and bottom
653      * will have the first param, the left and right the second.
654      * @method setPadding
655      * @param {int} iTop    Top pad
656      * @param {int} iRight  Right pad
657      * @param {int} iBot    Bot pad
658      * @param {int} iLeft   Left pad
659      */
660     setPadding: function(iTop, iRight, iBot, iLeft) {
661         // this.padding = [iLeft, iRight, iTop, iBot];
662         if (!iRight && 0 !== iRight) {
663             this.padding = [iTop, iTop, iTop, iTop];
664         } else if (!iBot && 0 !== iBot) {
665             this.padding = [iTop, iRight, iTop, iRight];
666         } else {
667             this.padding = [iTop, iRight, iBot, iLeft];
668         }
669     },
670
671     /**
672      * Stores the initial placement of the linked element.
673      * @method setInitialPosition
674      * @param {int} diffX   the X offset, default 0
675      * @param {int} diffY   the Y offset, default 0
676      */
677     setInitPosition: function(diffX, diffY) {
678         var el = this.getEl();
679
680         if (!this.DDM.verifyEl(el)) {
681             return;
682         }
683
684         var dx = diffX || 0;
685         var dy = diffY || 0;
686
687         var p = Dom.getXY( el );
688
689         this.initPageX = p[0] - dx;
690         this.initPageY = p[1] - dy;
691
692         this.lastPageX = p[0];
693         this.lastPageY = p[1];
694
695
696         this.setStartPosition(p);
697     },
698
699     /**
700      * Sets the start position of the element.  This is set when the obj
701      * is initialized, the reset when a drag is started.
702      * @method setStartPosition
703      * @param pos current position (from previous lookup)
704      * @private
705      */
706     setStartPosition: function(pos) {
707         var p = pos || Dom.getXY( this.getEl() );
708         this.deltaSetXY = null;
709
710         this.startPageX = p[0];
711         this.startPageY = p[1];
712     },
713
714     /**
715      * Add this instance to a group of related drag/drop objects.  All
716      * instances belong to at least one group, and can belong to as many
717      * groups as needed.
718      * @method addToGroup
719      * @param sGroup {string} the name of the group
720      */
721     addToGroup: function(sGroup) {
722         this.groups[sGroup] = true;
723         this.DDM.regDragDrop(this, sGroup);
724     },
725
726     /**
727      * Remove's this instance from the supplied interaction group
728      * @method removeFromGroup
729      * @param {string}  sGroup  The group to drop
730      */
731     removeFromGroup: function(sGroup) {
732         if (this.groups[sGroup]) {
733             delete this.groups[sGroup];
734         }
735
736         this.DDM.removeDDFromGroup(this, sGroup);
737     },
738
739     /**
740      * Allows you to specify that an element other than the linked element
741      * will be moved with the cursor during a drag
742      * @method setDragElId
743      * @param id {string} the id of the element that will be used to initiate the drag
744      */
745     setDragElId: function(id) {
746         this.dragElId = id;
747     },
748
749     /**
750      * Allows you to specify a child of the linked element that should be
751      * used to initiate the drag operation.  An example of this would be if
752      * you have a content div with text and links.  Clicking anywhere in the
753      * content area would normally start the drag operation.  Use this method
754      * to specify that an element inside of the content div is the element
755      * that starts the drag operation.
756      * @method setHandleElId
757      * @param id {string} the id of the element that will be used to
758      * initiate the drag.
759      */
760     setHandleElId: function(id) {
761         if (typeof id !== "string") {
762             id = Roo.id(id);
763         }
764         this.handleElId = id;
765         this.DDM.regHandle(this.id, id);
766     },
767
768     /**
769      * Allows you to set an element outside of the linked element as a drag
770      * handle
771      * @method setOuterHandleElId
772      * @param id the id of the element that will be used to initiate the drag
773      */
774     setOuterHandleElId: function(id) {
775         if (typeof id !== "string") {
776             id = Roo.id(id);
777         }
778         Event.on(id, "mousedown",
779                 this.handleMouseDown, this);
780         this.setHandleElId(id);
781
782         this.hasOuterHandles = true;
783     },
784
785     /**
786      * Remove all drag and drop hooks for this element
787      * @method unreg
788      */
789     unreg: function() {
790         Event.un(this.id, "mousedown",
791                 this.handleMouseDown);
792         this._domRef = null;
793         this.DDM._remove(this);
794     },
795
796     destroy : function(){
797         this.unreg();
798     },
799
800     /**
801      * Returns true if this instance is locked, or the drag drop mgr is locked
802      * (meaning that all drag/drop is disabled on the page.)
803      * @method isLocked
804      * @return {boolean} true if this obj or all drag/drop is locked, else
805      * false
806      */
807     isLocked: function() {
808         return (this.DDM.isLocked() || this.locked);
809     },
810
811     /**
812      * Fired when this object is clicked
813      * @method handleMouseDown
814      * @param {Event} e
815      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
816      * @private
817      */
818     handleMouseDown: function(e, oDD){
819         if (this.primaryButtonOnly && e.button != 0) {
820             return;
821         }
822
823         if (this.isLocked()) {
824             return;
825         }
826
827         this.DDM.refreshCache(this.groups);
828
829         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
830         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
831         } else {
832             if (this.clickValidator(e)) {
833
834                 // set the initial element position
835                 this.setStartPosition();
836
837
838                 this.b4MouseDown(e);
839                 this.onMouseDown(e);
840
841                 this.DDM.handleMouseDown(e, this);
842
843                 this.DDM.stopEvent(e);
844             } else {
845
846
847             }
848         }
849     },
850
851     clickValidator: function(e) {
852         var target = e.getTarget();
853         return ( this.isValidHandleChild(target) &&
854                     (this.id == this.handleElId ||
855                         this.DDM.handleWasClicked(target, this.id)) );
856     },
857
858     /**
859      * Allows you to specify a tag name that should not start a drag operation
860      * when clicked.  This is designed to facilitate embedding links within a
861      * drag handle that do something other than start the drag.
862      * @method addInvalidHandleType
863      * @param {string} tagName the type of element to exclude
864      */
865     addInvalidHandleType: function(tagName) {
866         var type = tagName.toUpperCase();
867         this.invalidHandleTypes[type] = type;
868     },
869
870     /**
871      * Lets you to specify an element id for a child of a drag handle
872      * that should not initiate a drag
873      * @method addInvalidHandleId
874      * @param {string} id the element id of the element you wish to ignore
875      */
876     addInvalidHandleId: function(id) {
877         if (typeof id !== "string") {
878             id = Roo.id(id);
879         }
880         this.invalidHandleIds[id] = id;
881     },
882
883     /**
884      * Lets you specify a css class of elements that will not initiate a drag
885      * @method addInvalidHandleClass
886      * @param {string} cssClass the class of the elements you wish to ignore
887      */
888     addInvalidHandleClass: function(cssClass) {
889         this.invalidHandleClasses.push(cssClass);
890     },
891
892     /**
893      * Unsets an excluded tag name set by addInvalidHandleType
894      * @method removeInvalidHandleType
895      * @param {string} tagName the type of element to unexclude
896      */
897     removeInvalidHandleType: function(tagName) {
898         var type = tagName.toUpperCase();
899         // this.invalidHandleTypes[type] = null;
900         delete this.invalidHandleTypes[type];
901     },
902
903     /**
904      * Unsets an invalid handle id
905      * @method removeInvalidHandleId
906      * @param {string} id the id of the element to re-enable
907      */
908     removeInvalidHandleId: function(id) {
909         if (typeof id !== "string") {
910             id = Roo.id(id);
911         }
912         delete this.invalidHandleIds[id];
913     },
914
915     /**
916      * Unsets an invalid css class
917      * @method removeInvalidHandleClass
918      * @param {string} cssClass the class of the element(s) you wish to
919      * re-enable
920      */
921     removeInvalidHandleClass: function(cssClass) {
922         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
923             if (this.invalidHandleClasses[i] == cssClass) {
924                 delete this.invalidHandleClasses[i];
925             }
926         }
927     },
928
929     /**
930      * Checks the tag exclusion list to see if this click should be ignored
931      * @method isValidHandleChild
932      * @param {HTMLElement} node the HTMLElement to evaluate
933      * @return {boolean} true if this is a valid tag type, false if not
934      */
935     isValidHandleChild: function(node) {
936
937         var valid = true;
938         // var n = (node.nodeName == "#text") ? node.parentNode : node;
939         var nodeName;
940         try {
941             nodeName = node.nodeName.toUpperCase();
942         } catch(e) {
943             nodeName = node.nodeName;
944         }
945         valid = valid && !this.invalidHandleTypes[nodeName];
946         valid = valid && !this.invalidHandleIds[node.id];
947
948         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
949             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
950         }
951
952
953         return valid;
954
955     },
956
957     /**
958      * Create the array of horizontal tick marks if an interval was specified
959      * in setXConstraint().
960      * @method setXTicks
961      * @private
962      */
963     setXTicks: function(iStartX, iTickSize) {
964         this.xTicks = [];
965         this.xTickSize = iTickSize;
966
967         var tickMap = {};
968
969         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
970             if (!tickMap[i]) {
971                 this.xTicks[this.xTicks.length] = i;
972                 tickMap[i] = true;
973             }
974         }
975
976         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
977             if (!tickMap[i]) {
978                 this.xTicks[this.xTicks.length] = i;
979                 tickMap[i] = true;
980             }
981         }
982
983         this.xTicks.sort(this.DDM.numericSort) ;
984     },
985
986     /**
987      * Create the array of vertical tick marks if an interval was specified in
988      * setYConstraint().
989      * @method setYTicks
990      * @private
991      */
992     setYTicks: function(iStartY, iTickSize) {
993         this.yTicks = [];
994         this.yTickSize = iTickSize;
995
996         var tickMap = {};
997
998         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
999             if (!tickMap[i]) {
1000                 this.yTicks[this.yTicks.length] = i;
1001                 tickMap[i] = true;
1002             }
1003         }
1004
1005         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1006             if (!tickMap[i]) {
1007                 this.yTicks[this.yTicks.length] = i;
1008                 tickMap[i] = true;
1009             }
1010         }
1011
1012         this.yTicks.sort(this.DDM.numericSort) ;
1013     },
1014
1015     /**
1016      * By default, the element can be dragged any place on the screen.  Use
1017      * this method to limit the horizontal travel of the element.  Pass in
1018      * 0,0 for the parameters if you want to lock the drag to the y axis.
1019      * @method setXConstraint
1020      * @param {int} iLeft the number of pixels the element can move to the left
1021      * @param {int} iRight the number of pixels the element can move to the
1022      * right
1023      * @param {int} iTickSize optional parameter for specifying that the
1024      * element
1025      * should move iTickSize pixels at a time.
1026      */
1027     setXConstraint: function(iLeft, iRight, iTickSize) {
1028         this.leftConstraint = iLeft;
1029         this.rightConstraint = iRight;
1030
1031         this.minX = this.initPageX - iLeft;
1032         this.maxX = this.initPageX + iRight;
1033         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1034
1035         this.constrainX = true;
1036     },
1037
1038     /**
1039      * Clears any constraints applied to this instance.  Also clears ticks
1040      * since they can't exist independent of a constraint at this time.
1041      * @method clearConstraints
1042      */
1043     clearConstraints: function() {
1044         this.constrainX = false;
1045         this.constrainY = false;
1046         this.clearTicks();
1047     },
1048
1049     /**
1050      * Clears any tick interval defined for this instance
1051      * @method clearTicks
1052      */
1053     clearTicks: function() {
1054         this.xTicks = null;
1055         this.yTicks = null;
1056         this.xTickSize = 0;
1057         this.yTickSize = 0;
1058     },
1059
1060     /**
1061      * By default, the element can be dragged any place on the screen.  Set
1062      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1063      * parameters if you want to lock the drag to the x axis.
1064      * @method setYConstraint
1065      * @param {int} iUp the number of pixels the element can move up
1066      * @param {int} iDown the number of pixels the element can move down
1067      * @param {int} iTickSize optional parameter for specifying that the
1068      * element should move iTickSize pixels at a time.
1069      */
1070     setYConstraint: function(iUp, iDown, iTickSize) {
1071         this.topConstraint = iUp;
1072         this.bottomConstraint = iDown;
1073
1074         this.minY = this.initPageY - iUp;
1075         this.maxY = this.initPageY + iDown;
1076         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1077
1078         this.constrainY = true;
1079
1080     },
1081
1082     /**
1083      * resetConstraints must be called if you manually reposition a dd element.
1084      * @method resetConstraints
1085      * @param {boolean} maintainOffset
1086      */
1087     resetConstraints: function() {
1088
1089
1090         // Maintain offsets if necessary
1091         if (this.initPageX || this.initPageX === 0) {
1092             // figure out how much this thing has moved
1093             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1094             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1095
1096             this.setInitPosition(dx, dy);
1097
1098         // This is the first time we have detected the element's position
1099         } else {
1100             this.setInitPosition();
1101         }
1102
1103         if (this.constrainX) {
1104             this.setXConstraint( this.leftConstraint,
1105                                  this.rightConstraint,
1106                                  this.xTickSize        );
1107         }
1108
1109         if (this.constrainY) {
1110             this.setYConstraint( this.topConstraint,
1111                                  this.bottomConstraint,
1112                                  this.yTickSize         );
1113         }
1114     },
1115
1116     /**
1117      * Normally the drag element is moved pixel by pixel, but we can specify
1118      * that it move a number of pixels at a time.  This method resolves the
1119      * location when we have it set up like this.
1120      * @method getTick
1121      * @param {int} val where we want to place the object
1122      * @param {int[]} tickArray sorted array of valid points
1123      * @return {int} the closest tick
1124      * @private
1125      */
1126     getTick: function(val, tickArray) {
1127
1128         if (!tickArray) {
1129             // If tick interval is not defined, it is effectively 1 pixel,
1130             // so we return the value passed to us.
1131             return val;
1132         } else if (tickArray[0] >= val) {
1133             // The value is lower than the first tick, so we return the first
1134             // tick.
1135             return tickArray[0];
1136         } else {
1137             for (var i=0, len=tickArray.length; i<len; ++i) {
1138                 var next = i + 1;
1139                 if (tickArray[next] && tickArray[next] >= val) {
1140                     var diff1 = val - tickArray[i];
1141                     var diff2 = tickArray[next] - val;
1142                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1143                 }
1144             }
1145
1146             // The value is larger than the last tick, so we return the last
1147             // tick.
1148             return tickArray[tickArray.length - 1];
1149         }
1150     },
1151
1152     /**
1153      * toString method
1154      * @method toString
1155      * @return {string} string representation of the dd obj
1156      */
1157     toString: function() {
1158         return ("DragDrop " + this.id);
1159     }
1160
1161 });
1162
1163 })();
1164 /*
1165  * Based on:
1166  * Ext JS Library 1.1.1
1167  * Copyright(c) 2006-2007, Ext JS, LLC.
1168  *
1169  * Originally Released Under LGPL - original licence link has changed is not relivant.
1170  *
1171  * Fork - LGPL
1172  * <script type="text/javascript">
1173  */
1174
1175
1176 /**
1177  * The drag and drop utility provides a framework for building drag and drop
1178  * applications.  In addition to enabling drag and drop for specific elements,
1179  * the drag and drop elements are tracked by the manager class, and the
1180  * interactions between the various elements are tracked during the drag and
1181  * the implementing code is notified about these important moments.
1182  */
1183
1184 // Only load the library once.  Rewriting the manager class would orphan
1185 // existing drag and drop instances.
1186 if (!Roo.dd.DragDropMgr) {
1187
1188 /**
1189  * @class Roo.dd.DragDropMgr
1190  * DragDropMgr is a singleton that tracks the element interaction for
1191  * all DragDrop items in the window.  Generally, you will not call
1192  * this class directly, but it does have helper methods that could
1193  * be useful in your DragDrop implementations.
1194  * @singleton
1195  */
1196 Roo.dd.DragDropMgr = function() {
1197
1198     var Event = Roo.EventManager;
1199
1200     return {
1201
1202         /**
1203          * Two dimensional Array of registered DragDrop objects.  The first
1204          * dimension is the DragDrop item group, the second the DragDrop
1205          * object.
1206          * @property ids
1207          * @type {string: string}
1208          * @private
1209          * @static
1210          */
1211         ids: {},
1212
1213         /**
1214          * Array of element ids defined as drag handles.  Used to determine
1215          * if the element that generated the mousedown event is actually the
1216          * handle and not the html element itself.
1217          * @property handleIds
1218          * @type {string: string}
1219          * @private
1220          * @static
1221          */
1222         handleIds: {},
1223
1224         /**
1225          * the DragDrop object that is currently being dragged
1226          * @property dragCurrent
1227          * @type DragDrop
1228          * @private
1229          * @static
1230          **/
1231         dragCurrent: null,
1232
1233         /**
1234          * the DragDrop object(s) that are being hovered over
1235          * @property dragOvers
1236          * @type Array
1237          * @private
1238          * @static
1239          */
1240         dragOvers: {},
1241
1242         /**
1243          * the X distance between the cursor and the object being dragged
1244          * @property deltaX
1245          * @type int
1246          * @private
1247          * @static
1248          */
1249         deltaX: 0,
1250
1251         /**
1252          * the Y distance between the cursor and the object being dragged
1253          * @property deltaY
1254          * @type int
1255          * @private
1256          * @static
1257          */
1258         deltaY: 0,
1259
1260         /**
1261          * Flag to determine if we should prevent the default behavior of the
1262          * events we define. By default this is true, but this can be set to
1263          * false if you need the default behavior (not recommended)
1264          * @property preventDefault
1265          * @type boolean
1266          * @static
1267          */
1268         preventDefault: true,
1269
1270         /**
1271          * Flag to determine if we should stop the propagation of the events
1272          * we generate. This is true by default but you may want to set it to
1273          * false if the html element contains other features that require the
1274          * mouse click.
1275          * @property stopPropagation
1276          * @type boolean
1277          * @static
1278          */
1279         stopPropagation: true,
1280
1281         /**
1282          * Internal flag that is set to true when drag and drop has been
1283          * intialized
1284          * @property initialized
1285          * @private
1286          * @static
1287          */
1288         initalized: false,
1289
1290         /**
1291          * All drag and drop can be disabled.
1292          * @property locked
1293          * @private
1294          * @static
1295          */
1296         locked: false,
1297
1298         /**
1299          * Called the first time an element is registered.
1300          * @method init
1301          * @private
1302          * @static
1303          */
1304         init: function() {
1305             this.initialized = true;
1306         },
1307
1308         /**
1309          * In point mode, drag and drop interaction is defined by the
1310          * location of the cursor during the drag/drop
1311          * @property POINT
1312          * @type int
1313          * @static
1314          */
1315         POINT: 0,
1316
1317         /**
1318          * In intersect mode, drag and drop interactio nis defined by the
1319          * overlap of two or more drag and drop objects.
1320          * @property INTERSECT
1321          * @type int
1322          * @static
1323          */
1324         INTERSECT: 1,
1325
1326         /**
1327          * The current drag and drop mode.  Default: POINT
1328          * @property mode
1329          * @type int
1330          * @static
1331          */
1332         mode: 0,
1333
1334         /**
1335          * Runs method on all drag and drop objects
1336          * @method _execOnAll
1337          * @private
1338          * @static
1339          */
1340         _execOnAll: function(sMethod, args) {
1341             for (var i in this.ids) {
1342                 for (var j in this.ids[i]) {
1343                     var oDD = this.ids[i][j];
1344                     if (! this.isTypeOfDD(oDD)) {
1345                         continue;
1346                     }
1347                     oDD[sMethod].apply(oDD, args);
1348                 }
1349             }
1350         },
1351
1352         /**
1353          * Drag and drop initialization.  Sets up the global event handlers
1354          * @method _onLoad
1355          * @private
1356          * @static
1357          */
1358         _onLoad: function() {
1359
1360             this.init();
1361
1362
1363             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1364             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1365             Event.on(window,   "unload",    this._onUnload, this, true);
1366             Event.on(window,   "resize",    this._onResize, this, true);
1367             // Event.on(window,   "mouseout",    this._test);
1368
1369         },
1370
1371         /**
1372          * Reset constraints on all drag and drop objs
1373          * @method _onResize
1374          * @private
1375          * @static
1376          */
1377         _onResize: function(e) {
1378             this._execOnAll("resetConstraints", []);
1379         },
1380
1381         /**
1382          * Lock all drag and drop functionality
1383          * @method lock
1384          * @static
1385          */
1386         lock: function() { this.locked = true; },
1387
1388         /**
1389          * Unlock all drag and drop functionality
1390          * @method unlock
1391          * @static
1392          */
1393         unlock: function() { this.locked = false; },
1394
1395         /**
1396          * Is drag and drop locked?
1397          * @method isLocked
1398          * @return {boolean} True if drag and drop is locked, false otherwise.
1399          * @static
1400          */
1401         isLocked: function() { return this.locked; },
1402
1403         /**
1404          * Location cache that is set for all drag drop objects when a drag is
1405          * initiated, cleared when the drag is finished.
1406          * @property locationCache
1407          * @private
1408          * @static
1409          */
1410         locationCache: {},
1411
1412         /**
1413          * Set useCache to false if you want to force object the lookup of each
1414          * drag and drop linked element constantly during a drag.
1415          * @property useCache
1416          * @type boolean
1417          * @static
1418          */
1419         useCache: true,
1420
1421         /**
1422          * The number of pixels that the mouse needs to move after the
1423          * mousedown before the drag is initiated.  Default=3;
1424          * @property clickPixelThresh
1425          * @type int
1426          * @static
1427          */
1428         clickPixelThresh: 3,
1429
1430         /**
1431          * The number of milliseconds after the mousedown event to initiate the
1432          * drag if we don't get a mouseup event. Default=1000
1433          * @property clickTimeThresh
1434          * @type int
1435          * @static
1436          */
1437         clickTimeThresh: 350,
1438
1439         /**
1440          * Flag that indicates that either the drag pixel threshold or the
1441          * mousdown time threshold has been met
1442          * @property dragThreshMet
1443          * @type boolean
1444          * @private
1445          * @static
1446          */
1447         dragThreshMet: false,
1448
1449         /**
1450          * Timeout used for the click time threshold
1451          * @property clickTimeout
1452          * @type Object
1453          * @private
1454          * @static
1455          */
1456         clickTimeout: null,
1457
1458         /**
1459          * The X position of the mousedown event stored for later use when a
1460          * drag threshold is met.
1461          * @property startX
1462          * @type int
1463          * @private
1464          * @static
1465          */
1466         startX: 0,
1467
1468         /**
1469          * The Y position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startY
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startY: 0,
1477
1478         /**
1479          * Each DragDrop instance must be registered with the DragDropMgr.
1480          * This is executed in DragDrop.init()
1481          * @method regDragDrop
1482          * @param {DragDrop} oDD the DragDrop object to register
1483          * @param {String} sGroup the name of the group this element belongs to
1484          * @static
1485          */
1486         regDragDrop: function(oDD, sGroup) {
1487             if (!this.initialized) { this.init(); }
1488
1489             if (!this.ids[sGroup]) {
1490                 this.ids[sGroup] = {};
1491             }
1492             this.ids[sGroup][oDD.id] = oDD;
1493         },
1494
1495         /**
1496          * Removes the supplied dd instance from the supplied group. Executed
1497          * by DragDrop.removeFromGroup, so don't call this function directly.
1498          * @method removeDDFromGroup
1499          * @private
1500          * @static
1501          */
1502         removeDDFromGroup: function(oDD, sGroup) {
1503             if (!this.ids[sGroup]) {
1504                 this.ids[sGroup] = {};
1505             }
1506
1507             var obj = this.ids[sGroup];
1508             if (obj && obj[oDD.id]) {
1509                 delete obj[oDD.id];
1510             }
1511         },
1512
1513         /**
1514          * Unregisters a drag and drop item.  This is executed in
1515          * DragDrop.unreg, use that method instead of calling this directly.
1516          * @method _remove
1517          * @private
1518          * @static
1519          */
1520         _remove: function(oDD) {
1521             for (var g in oDD.groups) {
1522                 if (g && this.ids[g][oDD.id]) {
1523                     delete this.ids[g][oDD.id];
1524                 }
1525             }
1526             delete this.handleIds[oDD.id];
1527         },
1528
1529         /**
1530          * Each DragDrop handle element must be registered.  This is done
1531          * automatically when executing DragDrop.setHandleElId()
1532          * @method regHandle
1533          * @param {String} sDDId the DragDrop id this element is a handle for
1534          * @param {String} sHandleId the id of the element that is the drag
1535          * handle
1536          * @static
1537          */
1538         regHandle: function(sDDId, sHandleId) {
1539             if (!this.handleIds[sDDId]) {
1540                 this.handleIds[sDDId] = {};
1541             }
1542             this.handleIds[sDDId][sHandleId] = sHandleId;
1543         },
1544
1545         /**
1546          * Utility function to determine if a given element has been
1547          * registered as a drag drop item.
1548          * @method isDragDrop
1549          * @param {String} id the element id to check
1550          * @return {boolean} true if this element is a DragDrop item,
1551          * false otherwise
1552          * @static
1553          */
1554         isDragDrop: function(id) {
1555             return ( this.getDDById(id) ) ? true : false;
1556         },
1557
1558         /**
1559          * Returns the drag and drop instances that are in all groups the
1560          * passed in instance belongs to.
1561          * @method getRelated
1562          * @param {DragDrop} p_oDD the obj to get related data for
1563          * @param {boolean} bTargetsOnly if true, only return targetable objs
1564          * @return {DragDrop[]} the related instances
1565          * @static
1566          */
1567         getRelated: function(p_oDD, bTargetsOnly) {
1568             var oDDs = [];
1569             for (var i in p_oDD.groups) {
1570                 for (j in this.ids[i]) {
1571                     var dd = this.ids[i][j];
1572                     if (! this.isTypeOfDD(dd)) {
1573                         continue;
1574                     }
1575                     if (!bTargetsOnly || dd.isTarget) {
1576                         oDDs[oDDs.length] = dd;
1577                     }
1578                 }
1579             }
1580
1581             return oDDs;
1582         },
1583
1584         /**
1585          * Returns true if the specified dd target is a legal target for
1586          * the specifice drag obj
1587          * @method isLegalTarget
1588          * @param {DragDrop} the drag obj
1589          * @param {DragDrop} the target
1590          * @return {boolean} true if the target is a legal target for the
1591          * dd obj
1592          * @static
1593          */
1594         isLegalTarget: function (oDD, oTargetDD) {
1595             var targets = this.getRelated(oDD, true);
1596             for (var i=0, len=targets.length;i<len;++i) {
1597                 if (targets[i].id == oTargetDD.id) {
1598                     return true;
1599                 }
1600             }
1601
1602             return false;
1603         },
1604
1605         /**
1606          * My goal is to be able to transparently determine if an object is
1607          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1608          * returns "object", oDD.constructor.toString() always returns
1609          * "DragDrop" and not the name of the subclass.  So for now it just
1610          * evaluates a well-known variable in DragDrop.
1611          * @method isTypeOfDD
1612          * @param {Object} the object to evaluate
1613          * @return {boolean} true if typeof oDD = DragDrop
1614          * @static
1615          */
1616         isTypeOfDD: function (oDD) {
1617             return (oDD && oDD.__ygDragDrop);
1618         },
1619
1620         /**
1621          * Utility function to determine if a given element has been
1622          * registered as a drag drop handle for the given Drag Drop object.
1623          * @method isHandle
1624          * @param {String} id the element id to check
1625          * @return {boolean} true if this element is a DragDrop handle, false
1626          * otherwise
1627          * @static
1628          */
1629         isHandle: function(sDDId, sHandleId) {
1630             return ( this.handleIds[sDDId] &&
1631                             this.handleIds[sDDId][sHandleId] );
1632         },
1633
1634         /**
1635          * Returns the DragDrop instance for a given id
1636          * @method getDDById
1637          * @param {String} id the id of the DragDrop object
1638          * @return {DragDrop} the drag drop object, null if it is not found
1639          * @static
1640          */
1641         getDDById: function(id) {
1642             for (var i in this.ids) {
1643                 if (this.ids[i][id]) {
1644                     return this.ids[i][id];
1645                 }
1646             }
1647             return null;
1648         },
1649
1650         /**
1651          * Fired after a registered DragDrop object gets the mousedown event.
1652          * Sets up the events required to track the object being dragged
1653          * @method handleMouseDown
1654          * @param {Event} e the event
1655          * @param oDD the DragDrop object being dragged
1656          * @private
1657          * @static
1658          */
1659         handleMouseDown: function(e, oDD) {
1660             if(Roo.QuickTips){
1661                 Roo.QuickTips.disable();
1662             }
1663             this.currentTarget = e.getTarget();
1664
1665             this.dragCurrent = oDD;
1666
1667             var el = oDD.getEl();
1668
1669             // track start position
1670             this.startX = e.getPageX();
1671             this.startY = e.getPageY();
1672
1673             this.deltaX = this.startX - el.offsetLeft;
1674             this.deltaY = this.startY - el.offsetTop;
1675
1676             this.dragThreshMet = false;
1677
1678             this.clickTimeout = setTimeout(
1679                     function() {
1680                         var DDM = Roo.dd.DDM;
1681                         DDM.startDrag(DDM.startX, DDM.startY);
1682                     },
1683                     this.clickTimeThresh );
1684         },
1685
1686         /**
1687          * Fired when either the drag pixel threshol or the mousedown hold
1688          * time threshold has been met.
1689          * @method startDrag
1690          * @param x {int} the X position of the original mousedown
1691          * @param y {int} the Y position of the original mousedown
1692          * @static
1693          */
1694         startDrag: function(x, y) {
1695             clearTimeout(this.clickTimeout);
1696             if (this.dragCurrent) {
1697                 this.dragCurrent.b4StartDrag(x, y);
1698                 this.dragCurrent.startDrag(x, y);
1699             }
1700             this.dragThreshMet = true;
1701         },
1702
1703         /**
1704          * Internal function to handle the mouseup event.  Will be invoked
1705          * from the context of the document.
1706          * @method handleMouseUp
1707          * @param {Event} e the event
1708          * @private
1709          * @static
1710          */
1711         handleMouseUp: function(e) {
1712
1713             if(Roo.QuickTips){
1714                 Roo.QuickTips.enable();
1715             }
1716             if (! this.dragCurrent) {
1717                 return;
1718             }
1719
1720             clearTimeout(this.clickTimeout);
1721
1722             if (this.dragThreshMet) {
1723                 this.fireEvents(e, true);
1724             } else {
1725             }
1726
1727             this.stopDrag(e);
1728
1729             this.stopEvent(e);
1730         },
1731
1732         /**
1733          * Utility to stop event propagation and event default, if these
1734          * features are turned on.
1735          * @method stopEvent
1736          * @param {Event} e the event as returned by this.getEvent()
1737          * @static
1738          */
1739         stopEvent: function(e){
1740             if(this.stopPropagation) {
1741                 e.stopPropagation();
1742             }
1743
1744             if (this.preventDefault) {
1745                 e.preventDefault();
1746             }
1747         },
1748
1749         /**
1750          * Internal function to clean up event handlers after the drag
1751          * operation is complete
1752          * @method stopDrag
1753          * @param {Event} e the event
1754          * @private
1755          * @static
1756          */
1757         stopDrag: function(e) {
1758             // Fire the drag end event for the item that was dragged
1759             if (this.dragCurrent) {
1760                 if (this.dragThreshMet) {
1761                     this.dragCurrent.b4EndDrag(e);
1762                     this.dragCurrent.endDrag(e);
1763                 }
1764
1765                 this.dragCurrent.onMouseUp(e);
1766             }
1767
1768             this.dragCurrent = null;
1769             this.dragOvers = {};
1770         },
1771
1772         /**
1773          * Internal function to handle the mousemove event.  Will be invoked
1774          * from the context of the html element.
1775          *
1776          * @TODO figure out what we can do about mouse events lost when the
1777          * user drags objects beyond the window boundary.  Currently we can
1778          * detect this in internet explorer by verifying that the mouse is
1779          * down during the mousemove event.  Firefox doesn't give us the
1780          * button state on the mousemove event.
1781          * @method handleMouseMove
1782          * @param {Event} e the event
1783          * @private
1784          * @static
1785          */
1786         handleMouseMove: function(e) {
1787             if (! this.dragCurrent) {
1788                 return true;
1789             }
1790
1791             // var button = e.which || e.button;
1792
1793             // check for IE mouseup outside of page boundary
1794             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1795                 this.stopEvent(e);
1796                 return this.handleMouseUp(e);
1797             }
1798
1799             if (!this.dragThreshMet) {
1800                 var diffX = Math.abs(this.startX - e.getPageX());
1801                 var diffY = Math.abs(this.startY - e.getPageY());
1802                 if (diffX > this.clickPixelThresh ||
1803                             diffY > this.clickPixelThresh) {
1804                     this.startDrag(this.startX, this.startY);
1805                 }
1806             }
1807
1808             if (this.dragThreshMet) {
1809                 this.dragCurrent.b4Drag(e);
1810                 this.dragCurrent.onDrag(e);
1811                 if(!this.dragCurrent.moveOnly){
1812                     this.fireEvents(e, false);
1813                 }
1814             }
1815
1816             this.stopEvent(e);
1817
1818             return true;
1819         },
1820
1821         /**
1822          * Iterates over all of the DragDrop elements to find ones we are
1823          * hovering over or dropping on
1824          * @method fireEvents
1825          * @param {Event} e the event
1826          * @param {boolean} isDrop is this a drop op or a mouseover op?
1827          * @private
1828          * @static
1829          */
1830         fireEvents: function(e, isDrop) {
1831             var dc = this.dragCurrent;
1832
1833             // If the user did the mouse up outside of the window, we could
1834             // get here even though we have ended the drag.
1835             if (!dc || dc.isLocked()) {
1836                 return;
1837             }
1838
1839             var pt = e.getPoint();
1840
1841             // cache the previous dragOver array
1842             var oldOvers = [];
1843
1844             var outEvts   = [];
1845             var overEvts  = [];
1846             var dropEvts  = [];
1847             var enterEvts = [];
1848
1849             // Check to see if the object(s) we were hovering over is no longer
1850             // being hovered over so we can fire the onDragOut event
1851             for (var i in this.dragOvers) {
1852
1853                 var ddo = this.dragOvers[i];
1854
1855                 if (! this.isTypeOfDD(ddo)) {
1856                     continue;
1857                 }
1858
1859                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1860                     outEvts.push( ddo );
1861                 }
1862
1863                 oldOvers[i] = true;
1864                 delete this.dragOvers[i];
1865             }
1866
1867             for (var sGroup in dc.groups) {
1868
1869                 if ("string" != typeof sGroup) {
1870                     continue;
1871                 }
1872
1873                 for (i in this.ids[sGroup]) {
1874                     var oDD = this.ids[sGroup][i];
1875                     if (! this.isTypeOfDD(oDD)) {
1876                         continue;
1877                     }
1878
1879                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1880                         if (this.isOverTarget(pt, oDD, this.mode)) {
1881                             // look for drop interactions
1882                             if (isDrop) {
1883                                 dropEvts.push( oDD );
1884                             // look for drag enter and drag over interactions
1885                             } else {
1886
1887                                 // initial drag over: dragEnter fires
1888                                 if (!oldOvers[oDD.id]) {
1889                                     enterEvts.push( oDD );
1890                                 // subsequent drag overs: dragOver fires
1891                                 } else {
1892                                     overEvts.push( oDD );
1893                                 }
1894
1895                                 this.dragOvers[oDD.id] = oDD;
1896                             }
1897                         }
1898                     }
1899                 }
1900             }
1901
1902             if (this.mode) {
1903                 if (outEvts.length) {
1904                     dc.b4DragOut(e, outEvts);
1905                     dc.onDragOut(e, outEvts);
1906                 }
1907
1908                 if (enterEvts.length) {
1909                     dc.onDragEnter(e, enterEvts);
1910                 }
1911
1912                 if (overEvts.length) {
1913                     dc.b4DragOver(e, overEvts);
1914                     dc.onDragOver(e, overEvts);
1915                 }
1916
1917                 if (dropEvts.length) {
1918                     dc.b4DragDrop(e, dropEvts);
1919                     dc.onDragDrop(e, dropEvts);
1920                 }
1921
1922             } else {
1923                 // fire dragout events
1924                 var len = 0;
1925                 for (i=0, len=outEvts.length; i<len; ++i) {
1926                     dc.b4DragOut(e, outEvts[i].id);
1927                     dc.onDragOut(e, outEvts[i].id);
1928                 }
1929
1930                 // fire enter events
1931                 for (i=0,len=enterEvts.length; i<len; ++i) {
1932                     // dc.b4DragEnter(e, oDD.id);
1933                     dc.onDragEnter(e, enterEvts[i].id);
1934                 }
1935
1936                 // fire over events
1937                 for (i=0,len=overEvts.length; i<len; ++i) {
1938                     dc.b4DragOver(e, overEvts[i].id);
1939                     dc.onDragOver(e, overEvts[i].id);
1940                 }
1941
1942                 // fire drop events
1943                 for (i=0, len=dropEvts.length; i<len; ++i) {
1944                     dc.b4DragDrop(e, dropEvts[i].id);
1945                     dc.onDragDrop(e, dropEvts[i].id);
1946                 }
1947
1948             }
1949
1950             // notify about a drop that did not find a target
1951             if (isDrop && !dropEvts.length) {
1952                 dc.onInvalidDrop(e);
1953             }
1954
1955         },
1956
1957         /**
1958          * Helper function for getting the best match from the list of drag
1959          * and drop objects returned by the drag and drop events when we are
1960          * in INTERSECT mode.  It returns either the first object that the
1961          * cursor is over, or the object that has the greatest overlap with
1962          * the dragged element.
1963          * @method getBestMatch
1964          * @param  {DragDrop[]} dds The array of drag and drop objects
1965          * targeted
1966          * @return {DragDrop}       The best single match
1967          * @static
1968          */
1969         getBestMatch: function(dds) {
1970             var winner = null;
1971             // Return null if the input is not what we expect
1972             //if (!dds || !dds.length || dds.length == 0) {
1973                // winner = null;
1974             // If there is only one item, it wins
1975             //} else if (dds.length == 1) {
1976
1977             var len = dds.length;
1978
1979             if (len == 1) {
1980                 winner = dds[0];
1981             } else {
1982                 // Loop through the targeted items
1983                 for (var i=0; i<len; ++i) {
1984                     var dd = dds[i];
1985                     // If the cursor is over the object, it wins.  If the
1986                     // cursor is over multiple matches, the first one we come
1987                     // to wins.
1988                     if (dd.cursorIsOver) {
1989                         winner = dd;
1990                         break;
1991                     // Otherwise the object with the most overlap wins
1992                     } else {
1993                         if (!winner ||
1994                             winner.overlap.getArea() < dd.overlap.getArea()) {
1995                             winner = dd;
1996                         }
1997                     }
1998                 }
1999             }
2000
2001             return winner;
2002         },
2003
2004         /**
2005          * Refreshes the cache of the top-left and bottom-right points of the
2006          * drag and drop objects in the specified group(s).  This is in the
2007          * format that is stored in the drag and drop instance, so typical
2008          * usage is:
2009          * <code>
2010          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2011          * </code>
2012          * Alternatively:
2013          * <code>
2014          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2015          * </code>
2016          * @TODO this really should be an indexed array.  Alternatively this
2017          * method could accept both.
2018          * @method refreshCache
2019          * @param {Object} groups an associative array of groups to refresh
2020          * @static
2021          */
2022         refreshCache: function(groups) {
2023             for (var sGroup in groups) {
2024                 if ("string" != typeof sGroup) {
2025                     continue;
2026                 }
2027                 for (var i in this.ids[sGroup]) {
2028                     var oDD = this.ids[sGroup][i];
2029
2030                     if (this.isTypeOfDD(oDD)) {
2031                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2032                         var loc = this.getLocation(oDD);
2033                         if (loc) {
2034                             this.locationCache[oDD.id] = loc;
2035                         } else {
2036                             delete this.locationCache[oDD.id];
2037                             // this will unregister the drag and drop object if
2038                             // the element is not in a usable state
2039                             // oDD.unreg();
2040                         }
2041                     }
2042                 }
2043             }
2044         },
2045
2046         /**
2047          * This checks to make sure an element exists and is in the DOM.  The
2048          * main purpose is to handle cases where innerHTML is used to remove
2049          * drag and drop objects from the DOM.  IE provides an 'unspecified
2050          * error' when trying to access the offsetParent of such an element
2051          * @method verifyEl
2052          * @param {HTMLElement} el the element to check
2053          * @return {boolean} true if the element looks usable
2054          * @static
2055          */
2056         verifyEl: function(el) {
2057             if (el) {
2058                 var parent;
2059                 if(Roo.isIE){
2060                     try{
2061                         parent = el.offsetParent;
2062                     }catch(e){}
2063                 }else{
2064                     parent = el.offsetParent;
2065                 }
2066                 if (parent) {
2067                     return true;
2068                 }
2069             }
2070
2071             return false;
2072         },
2073
2074         /**
2075          * Returns a Region object containing the drag and drop element's position
2076          * and size, including the padding configured for it
2077          * @method getLocation
2078          * @param {DragDrop} oDD the drag and drop object to get the
2079          *                       location for
2080          * @return {Roo.lib.Region} a Region object representing the total area
2081          *                             the element occupies, including any padding
2082          *                             the instance is configured for.
2083          * @static
2084          */
2085         getLocation: function(oDD) {
2086             if (! this.isTypeOfDD(oDD)) {
2087                 return null;
2088             }
2089
2090             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2091
2092             try {
2093                 pos= Roo.lib.Dom.getXY(el);
2094             } catch (e) { }
2095
2096             if (!pos) {
2097                 return null;
2098             }
2099
2100             x1 = pos[0];
2101             x2 = x1 + el.offsetWidth;
2102             y1 = pos[1];
2103             y2 = y1 + el.offsetHeight;
2104
2105             t = y1 - oDD.padding[0];
2106             r = x2 + oDD.padding[1];
2107             b = y2 + oDD.padding[2];
2108             l = x1 - oDD.padding[3];
2109
2110             return new Roo.lib.Region( t, r, b, l );
2111         },
2112
2113         /**
2114          * Checks the cursor location to see if it over the target
2115          * @method isOverTarget
2116          * @param {Roo.lib.Point} pt The point to evaluate
2117          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2118          * @return {boolean} true if the mouse is over the target
2119          * @private
2120          * @static
2121          */
2122         isOverTarget: function(pt, oTarget, intersect) {
2123             // use cache if available
2124             var loc = this.locationCache[oTarget.id];
2125             if (!loc || !this.useCache) {
2126                 loc = this.getLocation(oTarget);
2127                 this.locationCache[oTarget.id] = loc;
2128
2129             }
2130
2131             if (!loc) {
2132                 return false;
2133             }
2134
2135             oTarget.cursorIsOver = loc.contains( pt );
2136
2137             // DragDrop is using this as a sanity check for the initial mousedown
2138             // in this case we are done.  In POINT mode, if the drag obj has no
2139             // contraints, we are also done. Otherwise we need to evaluate the
2140             // location of the target as related to the actual location of the
2141             // dragged element.
2142             var dc = this.dragCurrent;
2143             if (!dc || !dc.getTargetCoord ||
2144                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2145                 return oTarget.cursorIsOver;
2146             }
2147
2148             oTarget.overlap = null;
2149
2150             // Get the current location of the drag element, this is the
2151             // location of the mouse event less the delta that represents
2152             // where the original mousedown happened on the element.  We
2153             // need to consider constraints and ticks as well.
2154             var pos = dc.getTargetCoord(pt.x, pt.y);
2155
2156             var el = dc.getDragEl();
2157             var curRegion = new Roo.lib.Region( pos.y,
2158                                                    pos.x + el.offsetWidth,
2159                                                    pos.y + el.offsetHeight,
2160                                                    pos.x );
2161
2162             var overlap = curRegion.intersect(loc);
2163
2164             if (overlap) {
2165                 oTarget.overlap = overlap;
2166                 return (intersect) ? true : oTarget.cursorIsOver;
2167             } else {
2168                 return false;
2169             }
2170         },
2171
2172         /**
2173          * unload event handler
2174          * @method _onUnload
2175          * @private
2176          * @static
2177          */
2178         _onUnload: function(e, me) {
2179             Roo.dd.DragDropMgr.unregAll();
2180         },
2181
2182         /**
2183          * Cleans up the drag and drop events and objects.
2184          * @method unregAll
2185          * @private
2186          * @static
2187          */
2188         unregAll: function() {
2189
2190             if (this.dragCurrent) {
2191                 this.stopDrag();
2192                 this.dragCurrent = null;
2193             }
2194
2195             this._execOnAll("unreg", []);
2196
2197             for (i in this.elementCache) {
2198                 delete this.elementCache[i];
2199             }
2200
2201             this.elementCache = {};
2202             this.ids = {};
2203         },
2204
2205         /**
2206          * A cache of DOM elements
2207          * @property elementCache
2208          * @private
2209          * @static
2210          */
2211         elementCache: {},
2212
2213         /**
2214          * Get the wrapper for the DOM element specified
2215          * @method getElWrapper
2216          * @param {String} id the id of the element to get
2217          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2218          * @private
2219          * @deprecated This wrapper isn't that useful
2220          * @static
2221          */
2222         getElWrapper: function(id) {
2223             var oWrapper = this.elementCache[id];
2224             if (!oWrapper || !oWrapper.el) {
2225                 oWrapper = this.elementCache[id] =
2226                     new this.ElementWrapper(Roo.getDom(id));
2227             }
2228             return oWrapper;
2229         },
2230
2231         /**
2232          * Returns the actual DOM element
2233          * @method getElement
2234          * @param {String} id the id of the elment to get
2235          * @return {Object} The element
2236          * @deprecated use Roo.getDom instead
2237          * @static
2238          */
2239         getElement: function(id) {
2240             return Roo.getDom(id);
2241         },
2242
2243         /**
2244          * Returns the style property for the DOM element (i.e.,
2245          * document.getElById(id).style)
2246          * @method getCss
2247          * @param {String} id the id of the elment to get
2248          * @return {Object} The style property of the element
2249          * @deprecated use Roo.getDom instead
2250          * @static
2251          */
2252         getCss: function(id) {
2253             var el = Roo.getDom(id);
2254             return (el) ? el.style : null;
2255         },
2256
2257         /**
2258          * Inner class for cached elements
2259          * @class DragDropMgr.ElementWrapper
2260          * @for DragDropMgr
2261          * @private
2262          * @deprecated
2263          */
2264         ElementWrapper: function(el) {
2265                 /**
2266                  * The element
2267                  * @property el
2268                  */
2269                 this.el = el || null;
2270                 /**
2271                  * The element id
2272                  * @property id
2273                  */
2274                 this.id = this.el && el.id;
2275                 /**
2276                  * A reference to the style property
2277                  * @property css
2278                  */
2279                 this.css = this.el && el.style;
2280             },
2281
2282         /**
2283          * Returns the X position of an html element
2284          * @method getPosX
2285          * @param el the element for which to get the position
2286          * @return {int} the X coordinate
2287          * @for DragDropMgr
2288          * @deprecated use Roo.lib.Dom.getX instead
2289          * @static
2290          */
2291         getPosX: function(el) {
2292             return Roo.lib.Dom.getX(el);
2293         },
2294
2295         /**
2296          * Returns the Y position of an html element
2297          * @method getPosY
2298          * @param el the element for which to get the position
2299          * @return {int} the Y coordinate
2300          * @deprecated use Roo.lib.Dom.getY instead
2301          * @static
2302          */
2303         getPosY: function(el) {
2304             return Roo.lib.Dom.getY(el);
2305         },
2306
2307         /**
2308          * Swap two nodes.  In IE, we use the native method, for others we
2309          * emulate the IE behavior
2310          * @method swapNode
2311          * @param n1 the first node to swap
2312          * @param n2 the other node to swap
2313          * @static
2314          */
2315         swapNode: function(n1, n2) {
2316             if (n1.swapNode) {
2317                 n1.swapNode(n2);
2318             } else {
2319                 var p = n2.parentNode;
2320                 var s = n2.nextSibling;
2321
2322                 if (s == n1) {
2323                     p.insertBefore(n1, n2);
2324                 } else if (n2 == n1.nextSibling) {
2325                     p.insertBefore(n2, n1);
2326                 } else {
2327                     n1.parentNode.replaceChild(n2, n1);
2328                     p.insertBefore(n1, s);
2329                 }
2330             }
2331         },
2332
2333         /**
2334          * Returns the current scroll position
2335          * @method getScroll
2336          * @private
2337          * @static
2338          */
2339         getScroll: function () {
2340             var t, l, dde=document.documentElement, db=document.body;
2341             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2342                 t = dde.scrollTop;
2343                 l = dde.scrollLeft;
2344             } else if (db) {
2345                 t = db.scrollTop;
2346                 l = db.scrollLeft;
2347             } else {
2348
2349             }
2350             return { top: t, left: l };
2351         },
2352
2353         /**
2354          * Returns the specified element style property
2355          * @method getStyle
2356          * @param {HTMLElement} el          the element
2357          * @param {string}      styleProp   the style property
2358          * @return {string} The value of the style property
2359          * @deprecated use Roo.lib.Dom.getStyle
2360          * @static
2361          */
2362         getStyle: function(el, styleProp) {
2363             return Roo.fly(el).getStyle(styleProp);
2364         },
2365
2366         /**
2367          * Gets the scrollTop
2368          * @method getScrollTop
2369          * @return {int} the document's scrollTop
2370          * @static
2371          */
2372         getScrollTop: function () { return this.getScroll().top; },
2373
2374         /**
2375          * Gets the scrollLeft
2376          * @method getScrollLeft
2377          * @return {int} the document's scrollTop
2378          * @static
2379          */
2380         getScrollLeft: function () { return this.getScroll().left; },
2381
2382         /**
2383          * Sets the x/y position of an element to the location of the
2384          * target element.
2385          * @method moveToEl
2386          * @param {HTMLElement} moveEl      The element to move
2387          * @param {HTMLElement} targetEl    The position reference element
2388          * @static
2389          */
2390         moveToEl: function (moveEl, targetEl) {
2391             var aCoord = Roo.lib.Dom.getXY(targetEl);
2392             Roo.lib.Dom.setXY(moveEl, aCoord);
2393         },
2394
2395         /**
2396          * Numeric array sort function
2397          * @method numericSort
2398          * @static
2399          */
2400         numericSort: function(a, b) { return (a - b); },
2401
2402         /**
2403          * Internal counter
2404          * @property _timeoutCount
2405          * @private
2406          * @static
2407          */
2408         _timeoutCount: 0,
2409
2410         /**
2411          * Trying to make the load order less important.  Without this we get
2412          * an error if this file is loaded before the Event Utility.
2413          * @method _addListeners
2414          * @private
2415          * @static
2416          */
2417         _addListeners: function() {
2418             var DDM = Roo.dd.DDM;
2419             if ( Roo.lib.Event && document ) {
2420                 DDM._onLoad();
2421             } else {
2422                 if (DDM._timeoutCount > 2000) {
2423                 } else {
2424                     setTimeout(DDM._addListeners, 10);
2425                     if (document && document.body) {
2426                         DDM._timeoutCount += 1;
2427                     }
2428                 }
2429             }
2430         },
2431
2432         /**
2433          * Recursively searches the immediate parent and all child nodes for
2434          * the handle element in order to determine wheter or not it was
2435          * clicked.
2436          * @method handleWasClicked
2437          * @param node the html element to inspect
2438          * @static
2439          */
2440         handleWasClicked: function(node, id) {
2441             if (this.isHandle(id, node.id)) {
2442                 return true;
2443             } else {
2444                 // check to see if this is a text node child of the one we want
2445                 var p = node.parentNode;
2446
2447                 while (p) {
2448                     if (this.isHandle(id, p.id)) {
2449                         return true;
2450                     } else {
2451                         p = p.parentNode;
2452                     }
2453                 }
2454             }
2455
2456             return false;
2457         }
2458
2459     };
2460
2461 }();
2462
2463 // shorter alias, save a few bytes
2464 Roo.dd.DDM = Roo.dd.DragDropMgr;
2465 Roo.dd.DDM._addListeners();
2466
2467 }/*
2468  * Based on:
2469  * Ext JS Library 1.1.1
2470  * Copyright(c) 2006-2007, Ext JS, LLC.
2471  *
2472  * Originally Released Under LGPL - original licence link has changed is not relivant.
2473  *
2474  * Fork - LGPL
2475  * <script type="text/javascript">
2476  */
2477
2478 /**
2479  * @class Roo.dd.DD
2480  * A DragDrop implementation where the linked element follows the
2481  * mouse cursor during a drag.
2482  * @extends Roo.dd.DragDrop
2483  * @constructor
2484  * @param {String} id the id of the linked element
2485  * @param {String} sGroup the group of related DragDrop items
2486  * @param {object} config an object containing configurable attributes
2487  *                Valid properties for DD:
2488  *                    scroll
2489  */
2490 Roo.dd.DD = function(id, sGroup, config) {
2491     if (id) {
2492         this.init(id, sGroup, config);
2493     }
2494 };
2495
2496 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2497
2498     /**
2499      * When set to true, the utility automatically tries to scroll the browser
2500      * window wehn a drag and drop element is dragged near the viewport boundary.
2501      * Defaults to true.
2502      * @property scroll
2503      * @type boolean
2504      */
2505     scroll: true,
2506
2507     /**
2508      * Sets the pointer offset to the distance between the linked element's top
2509      * left corner and the location the element was clicked
2510      * @method autoOffset
2511      * @param {int} iPageX the X coordinate of the click
2512      * @param {int} iPageY the Y coordinate of the click
2513      */
2514     autoOffset: function(iPageX, iPageY) {
2515         var x = iPageX - this.startPageX;
2516         var y = iPageY - this.startPageY;
2517         this.setDelta(x, y);
2518     },
2519
2520     /**
2521      * Sets the pointer offset.  You can call this directly to force the
2522      * offset to be in a particular location (e.g., pass in 0,0 to set it
2523      * to the center of the object)
2524      * @method setDelta
2525      * @param {int} iDeltaX the distance from the left
2526      * @param {int} iDeltaY the distance from the top
2527      */
2528     setDelta: function(iDeltaX, iDeltaY) {
2529         this.deltaX = iDeltaX;
2530         this.deltaY = iDeltaY;
2531     },
2532
2533     /**
2534      * Sets the drag element to the location of the mousedown or click event,
2535      * maintaining the cursor location relative to the location on the element
2536      * that was clicked.  Override this if you want to place the element in a
2537      * location other than where the cursor is.
2538      * @method setDragElPos
2539      * @param {int} iPageX the X coordinate of the mousedown or drag event
2540      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2541      */
2542     setDragElPos: function(iPageX, iPageY) {
2543         // the first time we do this, we are going to check to make sure
2544         // the element has css positioning
2545
2546         var el = this.getDragEl();
2547         this.alignElWithMouse(el, iPageX, iPageY);
2548     },
2549
2550     /**
2551      * Sets the element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method alignElWithMouse
2556      * @param {HTMLElement} el the element to move
2557      * @param {int} iPageX the X coordinate of the mousedown or drag event
2558      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2559      */
2560     alignElWithMouse: function(el, iPageX, iPageY) {
2561         var oCoord = this.getTargetCoord(iPageX, iPageY);
2562         var fly = el.dom ? el : Roo.fly(el);
2563         if (!this.deltaSetXY) {
2564             var aCoord = [oCoord.x, oCoord.y];
2565             fly.setXY(aCoord);
2566             var newLeft = fly.getLeft(true);
2567             var newTop  = fly.getTop(true);
2568             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2569         } else {
2570             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2571         }
2572
2573         this.cachePosition(oCoord.x, oCoord.y);
2574         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2575         return oCoord;
2576     },
2577
2578     /**
2579      * Saves the most recent position so that we can reset the constraints and
2580      * tick marks on-demand.  We need to know this so that we can calculate the
2581      * number of pixels the element is offset from its original position.
2582      * @method cachePosition
2583      * @param iPageX the current x position (optional, this just makes it so we
2584      * don't have to look it up again)
2585      * @param iPageY the current y position (optional, this just makes it so we
2586      * don't have to look it up again)
2587      */
2588     cachePosition: function(iPageX, iPageY) {
2589         if (iPageX) {
2590             this.lastPageX = iPageX;
2591             this.lastPageY = iPageY;
2592         } else {
2593             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2594             this.lastPageX = aCoord[0];
2595             this.lastPageY = aCoord[1];
2596         }
2597     },
2598
2599     /**
2600      * Auto-scroll the window if the dragged object has been moved beyond the
2601      * visible window boundary.
2602      * @method autoScroll
2603      * @param {int} x the drag element's x position
2604      * @param {int} y the drag element's y position
2605      * @param {int} h the height of the drag element
2606      * @param {int} w the width of the drag element
2607      * @private
2608      */
2609     autoScroll: function(x, y, h, w) {
2610
2611         if (this.scroll) {
2612             // The client height
2613             var clientH = Roo.lib.Dom.getViewWidth();
2614
2615             // The client width
2616             var clientW = Roo.lib.Dom.getViewHeight();
2617
2618             // The amt scrolled down
2619             var st = this.DDM.getScrollTop();
2620
2621             // The amt scrolled right
2622             var sl = this.DDM.getScrollLeft();
2623
2624             // Location of the bottom of the element
2625             var bot = h + y;
2626
2627             // Location of the right of the element
2628             var right = w + x;
2629
2630             // The distance from the cursor to the bottom of the visible area,
2631             // adjusted so that we don't scroll if the cursor is beyond the
2632             // element drag constraints
2633             var toBot = (clientH + st - y - this.deltaY);
2634
2635             // The distance from the cursor to the right of the visible area
2636             var toRight = (clientW + sl - x - this.deltaX);
2637
2638
2639             // How close to the edge the cursor must be before we scroll
2640             // var thresh = (document.all) ? 100 : 40;
2641             var thresh = 40;
2642
2643             // How many pixels to scroll per autoscroll op.  This helps to reduce
2644             // clunky scrolling. IE is more sensitive about this ... it needs this
2645             // value to be higher.
2646             var scrAmt = (document.all) ? 80 : 30;
2647
2648             // Scroll down if we are near the bottom of the visible page and the
2649             // obj extends below the crease
2650             if ( bot > clientH && toBot < thresh ) {
2651                 window.scrollTo(sl, st + scrAmt);
2652             }
2653
2654             // Scroll up if the window is scrolled down and the top of the object
2655             // goes above the top border
2656             if ( y < st && st > 0 && y - st < thresh ) {
2657                 window.scrollTo(sl, st - scrAmt);
2658             }
2659
2660             // Scroll right if the obj is beyond the right border and the cursor is
2661             // near the border.
2662             if ( right > clientW && toRight < thresh ) {
2663                 window.scrollTo(sl + scrAmt, st);
2664             }
2665
2666             // Scroll left if the window has been scrolled to the right and the obj
2667             // extends past the left border
2668             if ( x < sl && sl > 0 && x - sl < thresh ) {
2669                 window.scrollTo(sl - scrAmt, st);
2670             }
2671         }
2672     },
2673
2674     /**
2675      * Finds the location the element should be placed if we want to move
2676      * it to where the mouse location less the click offset would place us.
2677      * @method getTargetCoord
2678      * @param {int} iPageX the X coordinate of the click
2679      * @param {int} iPageY the Y coordinate of the click
2680      * @return an object that contains the coordinates (Object.x and Object.y)
2681      * @private
2682      */
2683     getTargetCoord: function(iPageX, iPageY) {
2684
2685
2686         var x = iPageX - this.deltaX;
2687         var y = iPageY - this.deltaY;
2688
2689         if (this.constrainX) {
2690             if (x < this.minX) { x = this.minX; }
2691             if (x > this.maxX) { x = this.maxX; }
2692         }
2693
2694         if (this.constrainY) {
2695             if (y < this.minY) { y = this.minY; }
2696             if (y > this.maxY) { y = this.maxY; }
2697         }
2698
2699         x = this.getTick(x, this.xTicks);
2700         y = this.getTick(y, this.yTicks);
2701
2702
2703         return {x:x, y:y};
2704     },
2705
2706     /*
2707      * Sets up config options specific to this class. Overrides
2708      * Roo.dd.DragDrop, but all versions of this method through the
2709      * inheritance chain are called
2710      */
2711     applyConfig: function() {
2712         Roo.dd.DD.superclass.applyConfig.call(this);
2713         this.scroll = (this.config.scroll !== false);
2714     },
2715
2716     /*
2717      * Event that fires prior to the onMouseDown event.  Overrides
2718      * Roo.dd.DragDrop.
2719      */
2720     b4MouseDown: function(e) {
2721         // this.resetConstraints();
2722         this.autoOffset(e.getPageX(),
2723                             e.getPageY());
2724     },
2725
2726     /*
2727      * Event that fires prior to the onDrag event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4Drag: function(e) {
2731         this.setDragElPos(e.getPageX(),
2732                             e.getPageY());
2733     },
2734
2735     toString: function() {
2736         return ("DD " + this.id);
2737     }
2738
2739     //////////////////////////////////////////////////////////////////////////
2740     // Debugging ygDragDrop events that can be overridden
2741     //////////////////////////////////////////////////////////////////////////
2742     /*
2743     startDrag: function(x, y) {
2744     },
2745
2746     onDrag: function(e) {
2747     },
2748
2749     onDragEnter: function(e, id) {
2750     },
2751
2752     onDragOver: function(e, id) {
2753     },
2754
2755     onDragOut: function(e, id) {
2756     },
2757
2758     onDragDrop: function(e, id) {
2759     },
2760
2761     endDrag: function(e) {
2762     }
2763
2764     */
2765
2766 });/*
2767  * Based on:
2768  * Ext JS Library 1.1.1
2769  * Copyright(c) 2006-2007, Ext JS, LLC.
2770  *
2771  * Originally Released Under LGPL - original licence link has changed is not relivant.
2772  *
2773  * Fork - LGPL
2774  * <script type="text/javascript">
2775  */
2776
2777 /**
2778  * @class Roo.dd.DDProxy
2779  * A DragDrop implementation that inserts an empty, bordered div into
2780  * the document that follows the cursor during drag operations.  At the time of
2781  * the click, the frame div is resized to the dimensions of the linked html
2782  * element, and moved to the exact location of the linked element.
2783  *
2784  * References to the "frame" element refer to the single proxy element that
2785  * was created to be dragged in place of all DDProxy elements on the
2786  * page.
2787  *
2788  * @extends Roo.dd.DD
2789  * @constructor
2790  * @param {String} id the id of the linked html element
2791  * @param {String} sGroup the group of related DragDrop objects
2792  * @param {object} config an object containing configurable attributes
2793  *                Valid properties for DDProxy in addition to those in DragDrop:
2794  *                   resizeFrame, centerFrame, dragElId
2795  */
2796 Roo.dd.DDProxy = function(id, sGroup, config) {
2797     if (id) {
2798         this.init(id, sGroup, config);
2799         this.initFrame();
2800     }
2801 };
2802
2803 /**
2804  * The default drag frame div id
2805  * @property Roo.dd.DDProxy.dragElId
2806  * @type String
2807  * @static
2808  */
2809 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2810
2811 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2812
2813     /**
2814      * By default we resize the drag frame to be the same size as the element
2815      * we want to drag (this is to get the frame effect).  We can turn it off
2816      * if we want a different behavior.
2817      * @property resizeFrame
2818      * @type boolean
2819      */
2820     resizeFrame: true,
2821
2822     /**
2823      * By default the frame is positioned exactly where the drag element is, so
2824      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2825      * you do not have constraints on the obj is to have the drag frame centered
2826      * around the cursor.  Set centerFrame to true for this effect.
2827      * @property centerFrame
2828      * @type boolean
2829      */
2830     centerFrame: false,
2831
2832     /**
2833      * Creates the proxy element if it does not yet exist
2834      * @method createFrame
2835      */
2836     createFrame: function() {
2837         var self = this;
2838         var body = document.body;
2839
2840         if (!body || !body.firstChild) {
2841             setTimeout( function() { self.createFrame(); }, 50 );
2842             return;
2843         }
2844
2845         var div = this.getDragEl();
2846
2847         if (!div) {
2848             div    = document.createElement("div");
2849             div.id = this.dragElId;
2850             var s  = div.style;
2851
2852             s.position   = "absolute";
2853             s.visibility = "hidden";
2854             s.cursor     = "move";
2855             s.border     = "2px solid #aaa";
2856             s.zIndex     = 999;
2857
2858             // appendChild can blow up IE if invoked prior to the window load event
2859             // while rendering a table.  It is possible there are other scenarios
2860             // that would cause this to happen as well.
2861             body.insertBefore(div, body.firstChild);
2862         }
2863     },
2864
2865     /**
2866      * Initialization for the drag frame element.  Must be called in the
2867      * constructor of all subclasses
2868      * @method initFrame
2869      */
2870     initFrame: function() {
2871         this.createFrame();
2872     },
2873
2874     applyConfig: function() {
2875         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2876
2877         this.resizeFrame = (this.config.resizeFrame !== false);
2878         this.centerFrame = (this.config.centerFrame);
2879         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2880     },
2881
2882     /**
2883      * Resizes the drag frame to the dimensions of the clicked object, positions
2884      * it over the object, and finally displays it
2885      * @method showFrame
2886      * @param {int} iPageX X click position
2887      * @param {int} iPageY Y click position
2888      * @private
2889      */
2890     showFrame: function(iPageX, iPageY) {
2891         var el = this.getEl();
2892         var dragEl = this.getDragEl();
2893         var s = dragEl.style;
2894
2895         this._resizeProxy();
2896
2897         if (this.centerFrame) {
2898             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2899                            Math.round(parseInt(s.height, 10)/2) );
2900         }
2901
2902         this.setDragElPos(iPageX, iPageY);
2903
2904         Roo.fly(dragEl).show();
2905     },
2906
2907     /**
2908      * The proxy is automatically resized to the dimensions of the linked
2909      * element when a drag is initiated, unless resizeFrame is set to false
2910      * @method _resizeProxy
2911      * @private
2912      */
2913     _resizeProxy: function() {
2914         if (this.resizeFrame) {
2915             var el = this.getEl();
2916             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2917         }
2918     },
2919
2920     // overrides Roo.dd.DragDrop
2921     b4MouseDown: function(e) {
2922         var x = e.getPageX();
2923         var y = e.getPageY();
2924         this.autoOffset(x, y);
2925         this.setDragElPos(x, y);
2926     },
2927
2928     // overrides Roo.dd.DragDrop
2929     b4StartDrag: function(x, y) {
2930         // show the drag frame
2931         this.showFrame(x, y);
2932     },
2933
2934     // overrides Roo.dd.DragDrop
2935     b4EndDrag: function(e) {
2936         Roo.fly(this.getDragEl()).hide();
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     // By default we try to move the element to the last location of the frame.
2941     // This is so that the default behavior mirrors that of Roo.dd.DD.
2942     endDrag: function(e) {
2943
2944         var lel = this.getEl();
2945         var del = this.getDragEl();
2946
2947         // Show the drag frame briefly so we can get its position
2948         del.style.visibility = "";
2949
2950         this.beforeMove();
2951         // Hide the linked element before the move to get around a Safari
2952         // rendering bug.
2953         lel.style.visibility = "hidden";
2954         Roo.dd.DDM.moveToEl(lel, del);
2955         del.style.visibility = "hidden";
2956         lel.style.visibility = "";
2957
2958         this.afterDrag();
2959     },
2960
2961     beforeMove : function(){
2962
2963     },
2964
2965     afterDrag : function(){
2966
2967     },
2968
2969     toString: function() {
2970         return ("DDProxy " + this.id);
2971     }
2972
2973 });
2974 /*
2975  * Based on:
2976  * Ext JS Library 1.1.1
2977  * Copyright(c) 2006-2007, Ext JS, LLC.
2978  *
2979  * Originally Released Under LGPL - original licence link has changed is not relivant.
2980  *
2981  * Fork - LGPL
2982  * <script type="text/javascript">
2983  */
2984
2985  /**
2986  * @class Roo.dd.DDTarget
2987  * A DragDrop implementation that does not move, but can be a drop
2988  * target.  You would get the same result by simply omitting implementation
2989  * for the event callbacks, but this way we reduce the processing cost of the
2990  * event listener and the callbacks.
2991  * @extends Roo.dd.DragDrop
2992  * @constructor
2993  * @param {String} id the id of the element that is a drop target
2994  * @param {String} sGroup the group of related DragDrop objects
2995  * @param {object} config an object containing configurable attributes
2996  *                 Valid properties for DDTarget in addition to those in
2997  *                 DragDrop:
2998  *                    none
2999  */
3000 Roo.dd.DDTarget = function(id, sGroup, config) {
3001     if (id) {
3002         this.initTarget(id, sGroup, config);
3003     }
3004     if (config.listeners || config.events) { 
3005        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3006             listeners : config.listeners || {}, 
3007             events : config.events || {} 
3008         });    
3009     }
3010 };
3011
3012 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3013 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3014     toString: function() {
3015         return ("DDTarget " + this.id);
3016     }
3017 });
3018 /*
3019  * Based on:
3020  * Ext JS Library 1.1.1
3021  * Copyright(c) 2006-2007, Ext JS, LLC.
3022  *
3023  * Originally Released Under LGPL - original licence link has changed is not relivant.
3024  *
3025  * Fork - LGPL
3026  * <script type="text/javascript">
3027  */
3028  
3029
3030 /**
3031  * @class Roo.dd.ScrollManager
3032  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3033  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3034  * @singleton
3035  */
3036 Roo.dd.ScrollManager = function(){
3037     var ddm = Roo.dd.DragDropMgr;
3038     var els = {};
3039     var dragEl = null;
3040     var proc = {};
3041     
3042     var onStop = function(e){
3043         dragEl = null;
3044         clearProc();
3045     };
3046     
3047     var triggerRefresh = function(){
3048         if(ddm.dragCurrent){
3049              ddm.refreshCache(ddm.dragCurrent.groups);
3050         }
3051     };
3052     
3053     var doScroll = function(){
3054         if(ddm.dragCurrent){
3055             var dds = Roo.dd.ScrollManager;
3056             if(!dds.animate){
3057                 if(proc.el.scroll(proc.dir, dds.increment)){
3058                     triggerRefresh();
3059                 }
3060             }else{
3061                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3062             }
3063         }
3064     };
3065     
3066     var clearProc = function(){
3067         if(proc.id){
3068             clearInterval(proc.id);
3069         }
3070         proc.id = 0;
3071         proc.el = null;
3072         proc.dir = "";
3073     };
3074     
3075     var startProc = function(el, dir){
3076         clearProc();
3077         proc.el = el;
3078         proc.dir = dir;
3079         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3080     };
3081     
3082     var onFire = function(e, isDrop){
3083         if(isDrop || !ddm.dragCurrent){ return; }
3084         var dds = Roo.dd.ScrollManager;
3085         if(!dragEl || dragEl != ddm.dragCurrent){
3086             dragEl = ddm.dragCurrent;
3087             // refresh regions on drag start
3088             dds.refreshCache();
3089         }
3090         
3091         var xy = Roo.lib.Event.getXY(e);
3092         var pt = new Roo.lib.Point(xy[0], xy[1]);
3093         for(var id in els){
3094             var el = els[id], r = el._region;
3095             if(r && r.contains(pt) && el.isScrollable()){
3096                 if(r.bottom - pt.y <= dds.thresh){
3097                     if(proc.el != el){
3098                         startProc(el, "down");
3099                     }
3100                     return;
3101                 }else if(r.right - pt.x <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "left");
3104                     }
3105                     return;
3106                 }else if(pt.y - r.top <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "up");
3109                     }
3110                     return;
3111                 }else if(pt.x - r.left <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "right");
3114                     }
3115                     return;
3116                 }
3117             }
3118         }
3119         clearProc();
3120     };
3121     
3122     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3123     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3124     
3125     return {
3126         /**
3127          * Registers new overflow element(s) to auto scroll
3128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3129          */
3130         register : function(el){
3131             if(el instanceof Array){
3132                 for(var i = 0, len = el.length; i < len; i++) {
3133                         this.register(el[i]);
3134                 }
3135             }else{
3136                 el = Roo.get(el);
3137                 els[el.id] = el;
3138             }
3139         },
3140         
3141         /**
3142          * Unregisters overflow element(s) so they are no longer scrolled
3143          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3144          */
3145         unregister : function(el){
3146             if(el instanceof Array){
3147                 for(var i = 0, len = el.length; i < len; i++) {
3148                         this.unregister(el[i]);
3149                 }
3150             }else{
3151                 el = Roo.get(el);
3152                 delete els[el.id];
3153             }
3154         },
3155         
3156         /**
3157          * The number of pixels from the edge of a container the pointer needs to be to 
3158          * trigger scrolling (defaults to 25)
3159          * @type Number
3160          */
3161         thresh : 25,
3162         
3163         /**
3164          * The number of pixels to scroll in each scroll increment (defaults to 50)
3165          * @type Number
3166          */
3167         increment : 100,
3168         
3169         /**
3170          * The frequency of scrolls in milliseconds (defaults to 500)
3171          * @type Number
3172          */
3173         frequency : 500,
3174         
3175         /**
3176          * True to animate the scroll (defaults to true)
3177          * @type Boolean
3178          */
3179         animate: true,
3180         
3181         /**
3182          * The animation duration in seconds - 
3183          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3184          * @type Number
3185          */
3186         animDuration: .4,
3187         
3188         /**
3189          * Manually trigger a cache refresh.
3190          */
3191         refreshCache : function(){
3192             for(var id in els){
3193                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3194                     els[id]._region = els[id].getRegion();
3195                 }
3196             }
3197         }
3198     };
3199 }();/*
3200  * Based on:
3201  * Ext JS Library 1.1.1
3202  * Copyright(c) 2006-2007, Ext JS, LLC.
3203  *
3204  * Originally Released Under LGPL - original licence link has changed is not relivant.
3205  *
3206  * Fork - LGPL
3207  * <script type="text/javascript">
3208  */
3209  
3210
3211 /**
3212  * @class Roo.dd.Registry
3213  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3214  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3215  * @singleton
3216  */
3217 Roo.dd.Registry = function(){
3218     var elements = {}; 
3219     var handles = {}; 
3220     var autoIdSeed = 0;
3221
3222     var getId = function(el, autogen){
3223         if(typeof el == "string"){
3224             return el;
3225         }
3226         var id = el.id;
3227         if(!id && autogen !== false){
3228             id = "roodd-" + (++autoIdSeed);
3229             el.id = id;
3230         }
3231         return id;
3232     };
3233     
3234     return {
3235     /**
3236      * Register a drag drop element
3237      * @param {String|HTMLElement} element The id or DOM node to register
3238      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3239      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3240      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3241      * populated in the data object (if applicable):
3242      * <pre>
3243 Value      Description<br />
3244 ---------  ------------------------------------------<br />
3245 handles    Array of DOM nodes that trigger dragging<br />
3246            for the element being registered<br />
3247 isHandle   True if the element passed in triggers<br />
3248            dragging itself, else false
3249 </pre>
3250      */
3251         register : function(el, data){
3252             data = data || {};
3253             if(typeof el == "string"){
3254                 el = document.getElementById(el);
3255             }
3256             data.ddel = el;
3257             elements[getId(el)] = data;
3258             if(data.isHandle !== false){
3259                 handles[data.ddel.id] = data;
3260             }
3261             if(data.handles){
3262                 var hs = data.handles;
3263                 for(var i = 0, len = hs.length; i < len; i++){
3264                         handles[getId(hs[i])] = data;
3265                 }
3266             }
3267         },
3268
3269     /**
3270      * Unregister a drag drop element
3271      * @param {String|HTMLElement}  element The id or DOM node to unregister
3272      */
3273         unregister : function(el){
3274             var id = getId(el, false);
3275             var data = elements[id];
3276             if(data){
3277                 delete elements[id];
3278                 if(data.handles){
3279                     var hs = data.handles;
3280                     for(var i = 0, len = hs.length; i < len; i++){
3281                         delete handles[getId(hs[i], false)];
3282                     }
3283                 }
3284             }
3285         },
3286
3287     /**
3288      * Returns the handle registered for a DOM Node by id
3289      * @param {String|HTMLElement} id The DOM node or id to look up
3290      * @return {Object} handle The custom handle data
3291      */
3292         getHandle : function(id){
3293             if(typeof id != "string"){ // must be element?
3294                 id = id.id;
3295             }
3296             return handles[id];
3297         },
3298
3299     /**
3300      * Returns the handle that is registered for the DOM node that is the target of the event
3301      * @param {Event} e The event
3302      * @return {Object} handle The custom handle data
3303      */
3304         getHandleFromEvent : function(e){
3305             var t = Roo.lib.Event.getTarget(e);
3306             return t ? handles[t.id] : null;
3307         },
3308
3309     /**
3310      * Returns a custom data object that is registered for a DOM node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} data The custom data
3313      */
3314         getTarget : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return elements[id];
3319         },
3320
3321     /**
3322      * Returns a custom data object that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} data The custom data
3325      */
3326         getTargetFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? elements[t.id] || handles[t.id] : null;
3329         }
3330     };
3331 }();/*
3332  * Based on:
3333  * Ext JS Library 1.1.1
3334  * Copyright(c) 2006-2007, Ext JS, LLC.
3335  *
3336  * Originally Released Under LGPL - original licence link has changed is not relivant.
3337  *
3338  * Fork - LGPL
3339  * <script type="text/javascript">
3340  */
3341  
3342
3343 /**
3344  * @class Roo.dd.StatusProxy
3345  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3346  * default drag proxy used by all Roo.dd components.
3347  * @constructor
3348  * @param {Object} config
3349  */
3350 Roo.dd.StatusProxy = function(config){
3351     Roo.apply(this, config);
3352     this.id = this.id || Roo.id();
3353     this.el = new Roo.Layer({
3354         dh: {
3355             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3356                 {tag: "div", cls: "x-dd-drop-icon"},
3357                 {tag: "div", cls: "x-dd-drag-ghost"}
3358             ]
3359         }, 
3360         shadow: !config || config.shadow !== false
3361     });
3362     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3363     this.dropStatus = this.dropNotAllowed;
3364 };
3365
3366 Roo.dd.StatusProxy.prototype = {
3367     /**
3368      * @cfg {String} dropAllowed
3369      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3370      */
3371     dropAllowed : "x-dd-drop-ok",
3372     /**
3373      * @cfg {String} dropNotAllowed
3374      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3375      */
3376     dropNotAllowed : "x-dd-drop-nodrop",
3377
3378     /**
3379      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3380      * over the current target element.
3381      * @param {String} cssClass The css class for the new drop status indicator image
3382      */
3383     setStatus : function(cssClass){
3384         cssClass = cssClass || this.dropNotAllowed;
3385         if(this.dropStatus != cssClass){
3386             this.el.replaceClass(this.dropStatus, cssClass);
3387             this.dropStatus = cssClass;
3388         }
3389     },
3390
3391     /**
3392      * Resets the status indicator to the default dropNotAllowed value
3393      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3394      */
3395     reset : function(clearGhost){
3396         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3397         this.dropStatus = this.dropNotAllowed;
3398         if(clearGhost){
3399             this.ghost.update("");
3400         }
3401     },
3402
3403     /**
3404      * Updates the contents of the ghost element
3405      * @param {String} html The html that will replace the current innerHTML of the ghost element
3406      */
3407     update : function(html){
3408         if(typeof html == "string"){
3409             this.ghost.update(html);
3410         }else{
3411             this.ghost.update("");
3412             html.style.margin = "0";
3413             this.ghost.dom.appendChild(html);
3414         }
3415         // ensure float = none set?? cant remember why though.
3416         var el = this.ghost.dom.firstChild;
3417                 if(el){
3418                         Roo.fly(el).setStyle('float', 'none');
3419                 }
3420     },
3421     
3422     /**
3423      * Returns the underlying proxy {@link Roo.Layer}
3424      * @return {Roo.Layer} el
3425     */
3426     getEl : function(){
3427         return this.el;
3428     },
3429
3430     /**
3431      * Returns the ghost element
3432      * @return {Roo.Element} el
3433      */
3434     getGhost : function(){
3435         return this.ghost;
3436     },
3437
3438     /**
3439      * Hides the proxy
3440      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3441      */
3442     hide : function(clear){
3443         this.el.hide();
3444         if(clear){
3445             this.reset(true);
3446         }
3447     },
3448
3449     /**
3450      * Stops the repair animation if it's currently running
3451      */
3452     stop : function(){
3453         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3454             this.anim.stop();
3455         }
3456     },
3457
3458     /**
3459      * Displays this proxy
3460      */
3461     show : function(){
3462         this.el.show();
3463     },
3464
3465     /**
3466      * Force the Layer to sync its shadow and shim positions to the element
3467      */
3468     sync : function(){
3469         this.el.sync();
3470     },
3471
3472     /**
3473      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3474      * invalid drop operation by the item being dragged.
3475      * @param {Array} xy The XY position of the element ([x, y])
3476      * @param {Function} callback The function to call after the repair is complete
3477      * @param {Object} scope The scope in which to execute the callback
3478      */
3479     repair : function(xy, callback, scope){
3480         this.callback = callback;
3481         this.scope = scope;
3482         if(xy && this.animRepair !== false){
3483             this.el.addClass("x-dd-drag-repair");
3484             this.el.hideUnders(true);
3485             this.anim = this.el.shift({
3486                 duration: this.repairDuration || .5,
3487                 easing: 'easeOut',
3488                 xy: xy,
3489                 stopFx: true,
3490                 callback: this.afterRepair,
3491                 scope: this
3492             });
3493         }else{
3494             this.afterRepair();
3495         }
3496     },
3497
3498     // private
3499     afterRepair : function(){
3500         this.hide(true);
3501         if(typeof this.callback == "function"){
3502             this.callback.call(this.scope || this);
3503         }
3504         this.callback = null;
3505         this.scope = null;
3506     }
3507 };/*
3508  * Based on:
3509  * Ext JS Library 1.1.1
3510  * Copyright(c) 2006-2007, Ext JS, LLC.
3511  *
3512  * Originally Released Under LGPL - original licence link has changed is not relivant.
3513  *
3514  * Fork - LGPL
3515  * <script type="text/javascript">
3516  */
3517
3518 /**
3519  * @class Roo.dd.DragSource
3520  * @extends Roo.dd.DDProxy
3521  * A simple class that provides the basic implementation needed to make any element draggable.
3522  * @constructor
3523  * @param {String/HTMLElement/Element} el The container element
3524  * @param {Object} config
3525  */
3526 Roo.dd.DragSource = function(el, config){
3527     this.el = Roo.get(el);
3528     this.dragData = {};
3529     
3530     Roo.apply(this, config);
3531     
3532     if(!this.proxy){
3533         this.proxy = new Roo.dd.StatusProxy();
3534     }
3535
3536     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3537           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3538     
3539     this.dragging = false;
3540 };
3541
3542 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3543     /**
3544      * @cfg {String} dropAllowed
3545      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3546      */
3547     dropAllowed : "x-dd-drop-ok",
3548     /**
3549      * @cfg {String} dropNotAllowed
3550      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3551      */
3552     dropNotAllowed : "x-dd-drop-nodrop",
3553
3554     /**
3555      * Returns the data object associated with this drag source
3556      * @return {Object} data An object containing arbitrary data
3557      */
3558     getDragData : function(e){
3559         return this.dragData;
3560     },
3561
3562     // private
3563     onDragEnter : function(e, id){
3564         var target = Roo.dd.DragDropMgr.getDDById(id);
3565         this.cachedTarget = target;
3566         if(this.beforeDragEnter(target, e, id) !== false){
3567             if(target.isNotifyTarget){
3568                 var status = target.notifyEnter(this, e, this.dragData);
3569                 this.proxy.setStatus(status);
3570             }else{
3571                 this.proxy.setStatus(this.dropAllowed);
3572             }
3573             
3574             if(this.afterDragEnter){
3575                 /**
3576                  * An empty function by default, but provided so that you can perform a custom action
3577                  * when the dragged item enters the drop target by providing an implementation.
3578                  * @param {Roo.dd.DragDrop} target The drop target
3579                  * @param {Event} e The event object
3580                  * @param {String} id The id of the dragged element
3581                  * @method afterDragEnter
3582                  */
3583                 this.afterDragEnter(target, e, id);
3584             }
3585         }
3586     },
3587
3588     /**
3589      * An empty function by default, but provided so that you can perform a custom action
3590      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3591      * @param {Roo.dd.DragDrop} target The drop target
3592      * @param {Event} e The event object
3593      * @param {String} id The id of the dragged element
3594      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3595      */
3596     beforeDragEnter : function(target, e, id){
3597         return true;
3598     },
3599
3600     // private
3601     alignElWithMouse: function() {
3602         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3603         this.proxy.sync();
3604     },
3605
3606     // private
3607     onDragOver : function(e, id){
3608         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3609         if(this.beforeDragOver(target, e, id) !== false){
3610             if(target.isNotifyTarget){
3611                 var status = target.notifyOver(this, e, this.dragData);
3612                 this.proxy.setStatus(status);
3613             }
3614
3615             if(this.afterDragOver){
3616                 /**
3617                  * An empty function by default, but provided so that you can perform a custom action
3618                  * while the dragged item is over the drop target by providing an implementation.
3619                  * @param {Roo.dd.DragDrop} target The drop target
3620                  * @param {Event} e The event object
3621                  * @param {String} id The id of the dragged element
3622                  * @method afterDragOver
3623                  */
3624                 this.afterDragOver(target, e, id);
3625             }
3626         }
3627     },
3628
3629     /**
3630      * An empty function by default, but provided so that you can perform a custom action
3631      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3632      * @param {Roo.dd.DragDrop} target The drop target
3633      * @param {Event} e The event object
3634      * @param {String} id The id of the dragged element
3635      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3636      */
3637     beforeDragOver : function(target, e, id){
3638         return true;
3639     },
3640
3641     // private
3642     onDragOut : function(e, id){
3643         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3644         if(this.beforeDragOut(target, e, id) !== false){
3645             if(target.isNotifyTarget){
3646                 target.notifyOut(this, e, this.dragData);
3647             }
3648             this.proxy.reset();
3649             if(this.afterDragOut){
3650                 /**
3651                  * An empty function by default, but provided so that you can perform a custom action
3652                  * after the dragged item is dragged out of the target without dropping.
3653                  * @param {Roo.dd.DragDrop} target The drop target
3654                  * @param {Event} e The event object
3655                  * @param {String} id The id of the dragged element
3656                  * @method afterDragOut
3657                  */
3658                 this.afterDragOut(target, e, id);
3659             }
3660         }
3661         this.cachedTarget = null;
3662     },
3663
3664     /**
3665      * An empty function by default, but provided so that you can perform a custom action before the dragged
3666      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3667      * @param {Roo.dd.DragDrop} target The drop target
3668      * @param {Event} e The event object
3669      * @param {String} id The id of the dragged element
3670      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3671      */
3672     beforeDragOut : function(target, e, id){
3673         return true;
3674     },
3675     
3676     // private
3677     onDragDrop : function(e, id){
3678         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3679         if(this.beforeDragDrop(target, e, id) !== false){
3680             if(target.isNotifyTarget){
3681                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3682                     this.onValidDrop(target, e, id);
3683                 }else{
3684                     this.onInvalidDrop(target, e, id);
3685                 }
3686             }else{
3687                 this.onValidDrop(target, e, id);
3688             }
3689             
3690             if(this.afterDragDrop){
3691                 /**
3692                  * An empty function by default, but provided so that you can perform a custom action
3693                  * after a valid drag drop has occurred by providing an implementation.
3694                  * @param {Roo.dd.DragDrop} target The drop target
3695                  * @param {Event} e The event object
3696                  * @param {String} id The id of the dropped element
3697                  * @method afterDragDrop
3698                  */
3699                 this.afterDragDrop(target, e, id);
3700             }
3701         }
3702         delete this.cachedTarget;
3703     },
3704
3705     /**
3706      * An empty function by default, but provided so that you can perform a custom action before the dragged
3707      * item is dropped onto the target and optionally cancel the onDragDrop.
3708      * @param {Roo.dd.DragDrop} target The drop target
3709      * @param {Event} e The event object
3710      * @param {String} id The id of the dragged element
3711      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3712      */
3713     beforeDragDrop : function(target, e, id){
3714         return true;
3715     },
3716
3717     // private
3718     onValidDrop : function(target, e, id){
3719         this.hideProxy();
3720         if(this.afterValidDrop){
3721             /**
3722              * An empty function by default, but provided so that you can perform a custom action
3723              * after a valid drop has occurred by providing an implementation.
3724              * @param {Object} target The target DD 
3725              * @param {Event} e The event object
3726              * @param {String} id The id of the dropped element
3727              * @method afterInvalidDrop
3728              */
3729             this.afterValidDrop(target, e, id);
3730         }
3731     },
3732
3733     // private
3734     getRepairXY : function(e, data){
3735         return this.el.getXY();  
3736     },
3737
3738     // private
3739     onInvalidDrop : function(target, e, id){
3740         this.beforeInvalidDrop(target, e, id);
3741         if(this.cachedTarget){
3742             if(this.cachedTarget.isNotifyTarget){
3743                 this.cachedTarget.notifyOut(this, e, this.dragData);
3744             }
3745             this.cacheTarget = null;
3746         }
3747         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3748
3749         if(this.afterInvalidDrop){
3750             /**
3751              * An empty function by default, but provided so that you can perform a custom action
3752              * after an invalid drop has occurred by providing an implementation.
3753              * @param {Event} e The event object
3754              * @param {String} id The id of the dropped element
3755              * @method afterInvalidDrop
3756              */
3757             this.afterInvalidDrop(e, id);
3758         }
3759     },
3760
3761     // private
3762     afterRepair : function(){
3763         if(Roo.enableFx){
3764             this.el.highlight(this.hlColor || "c3daf9");
3765         }
3766         this.dragging = false;
3767     },
3768
3769     /**
3770      * An empty function by default, but provided so that you can perform a custom action after an invalid
3771      * drop has occurred.
3772      * @param {Roo.dd.DragDrop} target The drop target
3773      * @param {Event} e The event object
3774      * @param {String} id The id of the dragged element
3775      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3776      */
3777     beforeInvalidDrop : function(target, e, id){
3778         return true;
3779     },
3780
3781     // private
3782     handleMouseDown : function(e){
3783         if(this.dragging) {
3784             return;
3785         }
3786         var data = this.getDragData(e);
3787         if(data && this.onBeforeDrag(data, e) !== false){
3788             this.dragData = data;
3789             this.proxy.stop();
3790             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3791         } 
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action before the initial
3796      * drag event begins and optionally cancel it.
3797      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3798      * @param {Event} e The event object
3799      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3800      */
3801     onBeforeDrag : function(data, e){
3802         return true;
3803     },
3804
3805     /**
3806      * An empty function by default, but provided so that you can perform a custom action once the initial
3807      * drag event has begun.  The drag cannot be canceled from this function.
3808      * @param {Number} x The x position of the click on the dragged object
3809      * @param {Number} y The y position of the click on the dragged object
3810      */
3811     onStartDrag : Roo.emptyFn,
3812
3813     // private - YUI override
3814     startDrag : function(x, y){
3815         this.proxy.reset();
3816         this.dragging = true;
3817         this.proxy.update("");
3818         this.onInitDrag(x, y);
3819         this.proxy.show();
3820     },
3821
3822     // private
3823     onInitDrag : function(x, y){
3824         var clone = this.el.dom.cloneNode(true);
3825         clone.id = Roo.id(); // prevent duplicate ids
3826         this.proxy.update(clone);
3827         this.onStartDrag(x, y);
3828         return true;
3829     },
3830
3831     /**
3832      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3833      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3834      */
3835     getProxy : function(){
3836         return this.proxy;  
3837     },
3838
3839     /**
3840      * Hides the drag source's {@link Roo.dd.StatusProxy}
3841      */
3842     hideProxy : function(){
3843         this.proxy.hide();  
3844         this.proxy.reset(true);
3845         this.dragging = false;
3846     },
3847
3848     // private
3849     triggerCacheRefresh : function(){
3850         Roo.dd.DDM.refreshCache(this.groups);
3851     },
3852
3853     // private - override to prevent hiding
3854     b4EndDrag: function(e) {
3855     },
3856
3857     // private - override to prevent moving
3858     endDrag : function(e){
3859         this.onEndDrag(this.dragData, e);
3860     },
3861
3862     // private
3863     onEndDrag : function(data, e){
3864     },
3865     
3866     // private - pin to cursor
3867     autoOffset : function(x, y) {
3868         this.setDelta(-12, -20);
3869     }    
3870 });/*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880
3881
3882 /**
3883  * @class Roo.dd.DropTarget
3884  * @extends Roo.dd.DDTarget
3885  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3886  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3887  * @constructor
3888  * @param {String/HTMLElement/Element} el The container element
3889  * @param {Object} config
3890  */
3891 Roo.dd.DropTarget = function(el, config){
3892     this.el = Roo.get(el);
3893     
3894     var listeners = false; ;
3895     if (config & config.listeners) {
3896         listeners= config.listeners;
3897         delete config.listeners;
3898     }
3899     Roo.apply(this, config);
3900     
3901     if(this.containerScroll){
3902         Roo.dd.ScrollManager.register(this.el);
3903     }
3904     this.addEvents( {
3905          /**
3906          * @scope Roo.dd.DropTarget
3907          */
3908          
3909          /**
3910          * @event enter
3911          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3912          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3913          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3914          * 
3915          * IMPORTANT : it should set this.overClass and this.dropAllowed
3916          * 
3917          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3918          * @param {Event} e The event
3919          * @param {Object} data An object containing arbitrary data supplied by the drag source
3920          */
3921         "enter" : true,
3922         
3923          /**
3924          * @event over
3925          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3926          * This method will be called on every mouse movement while the drag source is over the drop target.
3927          * This default implementation simply returns the dropAllowed config value.
3928          * 
3929          * IMPORTANT : it should set this.dropAllowed
3930          * 
3931          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3932          * @param {Event} e The event
3933          * @param {Object} data An object containing arbitrary data supplied by the drag source
3934          
3935          */
3936         "over" : true,
3937         /**
3938          * @event out
3939          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3940          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3941          * overClass (if any) from the drop element.
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946          "out" : true,
3947          
3948         /**
3949          * @event drop
3950          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3951          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3952          * implementation that does something to process the drop event and returns true so that the drag source's
3953          * repair action does not run.
3954          * 
3955          * IMPORTANT : it should set this.success
3956          * 
3957          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3958          * @param {Event} e The event
3959          * @param {Object} data An object containing arbitrary data supplied by the drag source
3960         */
3961          "drop" : true
3962     });
3963             
3964      
3965     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3966         this.el.dom, 
3967         this.ddGroup || this.group,
3968         {
3969             isTarget: true,
3970             listeners : listeners || {} 
3971            
3972         
3973         }
3974     );
3975
3976 };
3977
3978 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3979     /**
3980      * @cfg {String} overClass
3981      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3982      */
3983      /**
3984      * @cfg {String} ddGroup
3985      * The drag drop group to handle drop events for
3986      */
3987      
3988     /**
3989      * @cfg {String} dropAllowed
3990      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3991      */
3992     dropAllowed : "x-dd-drop-ok",
3993     /**
3994      * @cfg {String} dropNotAllowed
3995      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3996      */
3997     dropNotAllowed : "x-dd-drop-nodrop",
3998     /**
3999      * @cfg {boolean} success
4000      * set this after drop listener.. 
4001      */
4002     success : false,
4003     /**
4004      * @cfg {boolean} valid
4005      * if the drop point is valid for over/enter..
4006      */
4007     valid : false,
4008     // private
4009     isTarget : true,
4010
4011     // private
4012     isNotifyTarget : true,
4013     
4014     /**
4015      * @hide
4016      */
4017     notifyEnter : function(dd, e, data){
4018         this.valid = true;
4019         this.fireEvent('enter', this, dd, e, data);
4020         if(this.overClass){
4021             this.el.addClass(this.overClass);
4022         }
4023         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4024     },
4025
4026     /**
4027      * @hide
4028      */
4029     notifyOver : function(dd, e, data){
4030         this.valid = true;
4031         this.fireEvent('over', this, dd, e, data);
4032         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4033     },
4034
4035     /**
4036      * @hide
4037      */
4038     notifyOut : function(dd, e, data){
4039         this.fireEvent('out', this, dd, e, data);
4040         if(this.overClass){
4041             this.el.removeClass(this.overClass);
4042         }
4043     },
4044
4045     /**
4046      * @hide
4047      */
4048     notifyDrop : function(dd, e, data){
4049         this.success = false;
4050         this.fireEvent('drop', this, dd, e, data);
4051         return this.success;
4052     }
4053 });/*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 /**
4066  * @class Roo.dd.DragZone
4067  * @extends Roo.dd.DragSource
4068  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4069  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4070  * @constructor
4071  * @param {String/HTMLElement/Element} el The container element
4072  * @param {Object} config
4073  */
4074 Roo.dd.DragZone = function(el, config){
4075     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4076     if(this.containerScroll){
4077         Roo.dd.ScrollManager.register(this.el);
4078     }
4079 };
4080
4081 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4082     /**
4083      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4084      * for auto scrolling during drag operations.
4085      */
4086     /**
4087      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4088      * method after a failed drop (defaults to "c3daf9" - light blue)
4089      */
4090
4091     /**
4092      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4093      * for a valid target to drag based on the mouse down. Override this method
4094      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4095      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4096      * @param {EventObject} e The mouse down event
4097      * @return {Object} The dragData
4098      */
4099     getDragData : function(e){
4100         return Roo.dd.Registry.getHandleFromEvent(e);
4101     },
4102     
4103     /**
4104      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4105      * this.dragData.ddel
4106      * @param {Number} x The x position of the click on the dragged object
4107      * @param {Number} y The y position of the click on the dragged object
4108      * @return {Boolean} true to continue the drag, false to cancel
4109      */
4110     onInitDrag : function(x, y){
4111         this.proxy.update(this.dragData.ddel.cloneNode(true));
4112         this.onStartDrag(x, y);
4113         return true;
4114     },
4115     
4116     /**
4117      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4118      */
4119     afterRepair : function(){
4120         if(Roo.enableFx){
4121             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4122         }
4123         this.dragging = false;
4124     },
4125
4126     /**
4127      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4128      * the XY of this.dragData.ddel
4129      * @param {EventObject} e The mouse up event
4130      * @return {Array} The xy location (e.g. [100, 200])
4131      */
4132     getRepairXY : function(e){
4133         return Roo.Element.fly(this.dragData.ddel).getXY();  
4134     }
4135 });/*
4136  * Based on:
4137  * Ext JS Library 1.1.1
4138  * Copyright(c) 2006-2007, Ext JS, LLC.
4139  *
4140  * Originally Released Under LGPL - original licence link has changed is not relivant.
4141  *
4142  * Fork - LGPL
4143  * <script type="text/javascript">
4144  */
4145 /**
4146  * @class Roo.dd.DropZone
4147  * @extends Roo.dd.DropTarget
4148  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4149  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4150  * @constructor
4151  * @param {String/HTMLElement/Element} el The container element
4152  * @param {Object} config
4153  */
4154 Roo.dd.DropZone = function(el, config){
4155     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4156 };
4157
4158 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4159     /**
4160      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4161      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4162      * provide your own custom lookup.
4163      * @param {Event} e The event
4164      * @return {Object} data The custom data
4165      */
4166     getTargetFromEvent : function(e){
4167         return Roo.dd.Registry.getTargetFromEvent(e);
4168     },
4169
4170     /**
4171      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4172      * that it has registered.  This method has no default implementation and should be overridden to provide
4173      * node-specific processing if necessary.
4174      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4175      * {@link #getTargetFromEvent} for this node)
4176      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4177      * @param {Event} e The event
4178      * @param {Object} data An object containing arbitrary data supplied by the drag source
4179      */
4180     onNodeEnter : function(n, dd, e, data){
4181         
4182     },
4183
4184     /**
4185      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4186      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4187      * overridden to provide the proper feedback.
4188      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4189      * {@link #getTargetFromEvent} for this node)
4190      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4191      * @param {Event} e The event
4192      * @param {Object} data An object containing arbitrary data supplied by the drag source
4193      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4194      * underlying {@link Roo.dd.StatusProxy} can be updated
4195      */
4196     onNodeOver : function(n, dd, e, data){
4197         return this.dropAllowed;
4198     },
4199
4200     /**
4201      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4202      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4203      * node-specific processing if necessary.
4204      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4205      * {@link #getTargetFromEvent} for this node)
4206      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4207      * @param {Event} e The event
4208      * @param {Object} data An object containing arbitrary data supplied by the drag source
4209      */
4210     onNodeOut : function(n, dd, e, data){
4211         
4212     },
4213
4214     /**
4215      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4216      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4217      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4218      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219      * {@link #getTargetFromEvent} for this node)
4220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221      * @param {Event} e The event
4222      * @param {Object} data An object containing arbitrary data supplied by the drag source
4223      * @return {Boolean} True if the drop was valid, else false
4224      */
4225     onNodeDrop : function(n, dd, e, data){
4226         return false;
4227     },
4228
4229     /**
4230      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4231      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4232      * it should be overridden to provide the proper feedback if necessary.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag source
4236      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4237      * underlying {@link Roo.dd.StatusProxy} can be updated
4238      */
4239     onContainerOver : function(dd, e, data){
4240         return this.dropNotAllowed;
4241     },
4242
4243     /**
4244      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4245      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4246      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4247      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {Boolean} True if the drop was valid, else false
4252      */
4253     onContainerDrop : function(dd, e, data){
4254         return false;
4255     },
4256
4257     /**
4258      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4259      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4260      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4261      * you should override this method and provide a custom implementation.
4262      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4263      * @param {Event} e The event
4264      * @param {Object} data An object containing arbitrary data supplied by the drag source
4265      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4266      * underlying {@link Roo.dd.StatusProxy} can be updated
4267      */
4268     notifyEnter : function(dd, e, data){
4269         return this.dropNotAllowed;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4274      * This method will be called on every mouse movement while the drag source is over the drop zone.
4275      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4276      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4277      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4278      * registered node, it will call {@link #onContainerOver}.
4279      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4280      * @param {Event} e The event
4281      * @param {Object} data An object containing arbitrary data supplied by the drag source
4282      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4283      * underlying {@link Roo.dd.StatusProxy} can be updated
4284      */
4285     notifyOver : function(dd, e, data){
4286         var n = this.getTargetFromEvent(e);
4287         if(!n){ // not over valid drop target
4288             if(this.lastOverNode){
4289                 this.onNodeOut(this.lastOverNode, dd, e, data);
4290                 this.lastOverNode = null;
4291             }
4292             return this.onContainerOver(dd, e, data);
4293         }
4294         if(this.lastOverNode != n){
4295             if(this.lastOverNode){
4296                 this.onNodeOut(this.lastOverNode, dd, e, data);
4297             }
4298             this.onNodeEnter(n, dd, e, data);
4299             this.lastOverNode = n;
4300         }
4301         return this.onNodeOver(n, dd, e, data);
4302     },
4303
4304     /**
4305      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4306      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4307      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4308      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4309      * @param {Event} e The event
4310      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4311      */
4312     notifyOut : function(dd, e, data){
4313         if(this.lastOverNode){
4314             this.onNodeOut(this.lastOverNode, dd, e, data);
4315             this.lastOverNode = null;
4316         }
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4321      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4322      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4323      * otherwise it will call {@link #onContainerDrop}.
4324      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4325      * @param {Event} e The event
4326      * @param {Object} data An object containing arbitrary data supplied by the drag source
4327      * @return {Boolean} True if the drop was valid, else false
4328      */
4329     notifyDrop : function(dd, e, data){
4330         if(this.lastOverNode){
4331             this.onNodeOut(this.lastOverNode, dd, e, data);
4332             this.lastOverNode = null;
4333         }
4334         var n = this.getTargetFromEvent(e);
4335         return n ?
4336             this.onNodeDrop(n, dd, e, data) :
4337             this.onContainerDrop(dd, e, data);
4338     },
4339
4340     // private
4341     triggerCacheRefresh : function(){
4342         Roo.dd.DDM.refreshCache(this.groups);
4343     }  
4344 });/*
4345  * Based on:
4346  * Ext JS Library 1.1.1
4347  * Copyright(c) 2006-2007, Ext JS, LLC.
4348  *
4349  * Originally Released Under LGPL - original licence link has changed is not relivant.
4350  *
4351  * Fork - LGPL
4352  * <script type="text/javascript">
4353  */
4354
4355
4356 /**
4357  * @class Roo.data.SortTypes
4358  * @singleton
4359  * Defines the default sorting (casting?) comparison functions used when sorting data.
4360  */
4361 Roo.data.SortTypes = {
4362     /**
4363      * Default sort that does nothing
4364      * @param {Mixed} s The value being converted
4365      * @return {Mixed} The comparison value
4366      */
4367     none : function(s){
4368         return s;
4369     },
4370     
4371     /**
4372      * The regular expression used to strip tags
4373      * @type {RegExp}
4374      * @property
4375      */
4376     stripTagsRE : /<\/?[^>]+>/gi,
4377     
4378     /**
4379      * Strips all HTML tags to sort on text only
4380      * @param {Mixed} s The value being converted
4381      * @return {String} The comparison value
4382      */
4383     asText : function(s){
4384         return String(s).replace(this.stripTagsRE, "");
4385     },
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only - Case insensitive
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asUCText : function(s){
4393         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Case insensitive string
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCString : function(s) {
4402         return String(s).toUpperCase();
4403     },
4404     
4405     /**
4406      * Date sorting
4407      * @param {Mixed} s The value being converted
4408      * @return {Number} The comparison value
4409      */
4410     asDate : function(s) {
4411         if(!s){
4412             return 0;
4413         }
4414         if(s instanceof Date){
4415             return s.getTime();
4416         }
4417         return Date.parse(String(s));
4418     },
4419     
4420     /**
4421      * Float sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Float} The comparison value
4424      */
4425     asFloat : function(s) {
4426         var val = parseFloat(String(s).replace(/,/g, ""));
4427         if(isNaN(val)) val = 0;
4428         return val;
4429     },
4430     
4431     /**
4432      * Integer sorting
4433      * @param {Mixed} s The value being converted
4434      * @return {Number} The comparison value
4435      */
4436     asInt : function(s) {
4437         var val = parseInt(String(s).replace(/,/g, ""));
4438         if(isNaN(val)) val = 0;
4439         return val;
4440     }
4441 };/*
4442  * Based on:
4443  * Ext JS Library 1.1.1
4444  * Copyright(c) 2006-2007, Ext JS, LLC.
4445  *
4446  * Originally Released Under LGPL - original licence link has changed is not relivant.
4447  *
4448  * Fork - LGPL
4449  * <script type="text/javascript">
4450  */
4451
4452 /**
4453 * @class Roo.data.Record
4454  * Instances of this class encapsulate both record <em>definition</em> information, and record
4455  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4456  * to access Records cached in an {@link Roo.data.Store} object.<br>
4457  * <p>
4458  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4459  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4460  * objects.<br>
4461  * <p>
4462  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4463  * @constructor
4464  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4465  * {@link #create}. The parameters are the same.
4466  * @param {Array} data An associative Array of data values keyed by the field name.
4467  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4468  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4469  * not specified an integer id is generated.
4470  */
4471 Roo.data.Record = function(data, id){
4472     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4473     this.data = data;
4474 };
4475
4476 /**
4477  * Generate a constructor for a specific record layout.
4478  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4479  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4480  * Each field definition object may contain the following properties: <ul>
4481  * <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,
4482  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4483  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4484  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4485  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4486  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4487  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4488  * this may be omitted.</p></li>
4489  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4490  * <ul><li>auto (Default, implies no conversion)</li>
4491  * <li>string</li>
4492  * <li>int</li>
4493  * <li>float</li>
4494  * <li>boolean</li>
4495  * <li>date</li></ul></p></li>
4496  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4497  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4498  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4499  * by the Reader into an object that will be stored in the Record. It is passed the
4500  * following parameters:<ul>
4501  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4502  * </ul></p></li>
4503  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4504  * </ul>
4505  * <br>usage:<br><pre><code>
4506 var TopicRecord = Roo.data.Record.create(
4507     {name: 'title', mapping: 'topic_title'},
4508     {name: 'author', mapping: 'username'},
4509     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4510     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4511     {name: 'lastPoster', mapping: 'user2'},
4512     {name: 'excerpt', mapping: 'post_text'}
4513 );
4514
4515 var myNewRecord = new TopicRecord({
4516     title: 'Do my job please',
4517     author: 'noobie',
4518     totalPosts: 1,
4519     lastPost: new Date(),
4520     lastPoster: 'Animal',
4521     excerpt: 'No way dude!'
4522 });
4523 myStore.add(myNewRecord);
4524 </code></pre>
4525  * @method create
4526  * @static
4527  */
4528 Roo.data.Record.create = function(o){
4529     var f = function(){
4530         f.superclass.constructor.apply(this, arguments);
4531     };
4532     Roo.extend(f, Roo.data.Record);
4533     var p = f.prototype;
4534     p.fields = new Roo.util.MixedCollection(false, function(field){
4535         return field.name;
4536     });
4537     for(var i = 0, len = o.length; i < len; i++){
4538         p.fields.add(new Roo.data.Field(o[i]));
4539     }
4540     f.getField = function(name){
4541         return p.fields.get(name);  
4542     };
4543     return f;
4544 };
4545
4546 Roo.data.Record.AUTO_ID = 1000;
4547 Roo.data.Record.EDIT = 'edit';
4548 Roo.data.Record.REJECT = 'reject';
4549 Roo.data.Record.COMMIT = 'commit';
4550
4551 Roo.data.Record.prototype = {
4552     /**
4553      * Readonly flag - true if this record has been modified.
4554      * @type Boolean
4555      */
4556     dirty : false,
4557     editing : false,
4558     error: null,
4559     modified: null,
4560
4561     // private
4562     join : function(store){
4563         this.store = store;
4564     },
4565
4566     /**
4567      * Set the named field to the specified value.
4568      * @param {String} name The name of the field to set.
4569      * @param {Object} value The value to set the field to.
4570      */
4571     set : function(name, value){
4572         if(this.data[name] == value){
4573             return;
4574         }
4575         this.dirty = true;
4576         if(!this.modified){
4577             this.modified = {};
4578         }
4579         if(typeof this.modified[name] == 'undefined'){
4580             this.modified[name] = this.data[name];
4581         }
4582         this.data[name] = value;
4583         if(!this.editing){
4584             this.store.afterEdit(this);
4585         }       
4586     },
4587
4588     /**
4589      * Get the value of the named field.
4590      * @param {String} name The name of the field to get the value of.
4591      * @return {Object} The value of the field.
4592      */
4593     get : function(name){
4594         return this.data[name]; 
4595     },
4596
4597     // private
4598     beginEdit : function(){
4599         this.editing = true;
4600         this.modified = {}; 
4601     },
4602
4603     // private
4604     cancelEdit : function(){
4605         this.editing = false;
4606         delete this.modified;
4607     },
4608
4609     // private
4610     endEdit : function(){
4611         this.editing = false;
4612         if(this.dirty && this.store){
4613             this.store.afterEdit(this);
4614         }
4615     },
4616
4617     /**
4618      * Usually called by the {@link Roo.data.Store} which owns the Record.
4619      * Rejects all changes made to the Record since either creation, or the last commit operation.
4620      * Modified fields are reverted to their original values.
4621      * <p>
4622      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4623      * of reject operations.
4624      */
4625     reject : function(){
4626         var m = this.modified;
4627         for(var n in m){
4628             if(typeof m[n] != "function"){
4629                 this.data[n] = m[n];
4630             }
4631         }
4632         this.dirty = false;
4633         delete this.modified;
4634         this.editing = false;
4635         if(this.store){
4636             this.store.afterReject(this);
4637         }
4638     },
4639
4640     /**
4641      * Usually called by the {@link Roo.data.Store} which owns the Record.
4642      * Commits all changes made to the Record since either creation, or the last commit operation.
4643      * <p>
4644      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4645      * of commit operations.
4646      */
4647     commit : function(){
4648         this.dirty = false;
4649         delete this.modified;
4650         this.editing = false;
4651         if(this.store){
4652             this.store.afterCommit(this);
4653         }
4654     },
4655
4656     // private
4657     hasError : function(){
4658         return this.error != null;
4659     },
4660
4661     // private
4662     clearError : function(){
4663         this.error = null;
4664     },
4665
4666     /**
4667      * Creates a copy of this record.
4668      * @param {String} id (optional) A new record id if you don't want to use this record's id
4669      * @return {Record}
4670      */
4671     copy : function(newId) {
4672         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4673     }
4674 };/*
4675  * Based on:
4676  * Ext JS Library 1.1.1
4677  * Copyright(c) 2006-2007, Ext JS, LLC.
4678  *
4679  * Originally Released Under LGPL - original licence link has changed is not relivant.
4680  *
4681  * Fork - LGPL
4682  * <script type="text/javascript">
4683  */
4684
4685
4686
4687 /**
4688  * @class Roo.data.Store
4689  * @extends Roo.util.Observable
4690  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4691  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4692  * <p>
4693  * 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
4694  * has no knowledge of the format of the data returned by the Proxy.<br>
4695  * <p>
4696  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4697  * instances from the data object. These records are cached and made available through accessor functions.
4698  * @constructor
4699  * Creates a new Store.
4700  * @param {Object} config A config object containing the objects needed for the Store to access data,
4701  * and read the data into Records.
4702  */
4703 Roo.data.Store = function(config){
4704     this.data = new Roo.util.MixedCollection(false);
4705     this.data.getKey = function(o){
4706         return o.id;
4707     };
4708     this.baseParams = {};
4709     // private
4710     this.paramNames = {
4711         "start" : "start",
4712         "limit" : "limit",
4713         "sort" : "sort",
4714         "dir" : "dir"
4715     };
4716
4717     if(config && config.data){
4718         this.inlineData = config.data;
4719         delete config.data;
4720     }
4721
4722     Roo.apply(this, config);
4723     
4724     if(this.reader){ // reader passed
4725         this.reader = Roo.factory(this.reader, Roo.data);
4726         this.reader.xmodule = this.xmodule || false;
4727         if(!this.recordType){
4728             this.recordType = this.reader.recordType;
4729         }
4730         if(this.reader.onMetaChange){
4731             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4732         }
4733     }
4734
4735     if(this.recordType){
4736         this.fields = this.recordType.prototype.fields;
4737     }
4738     this.modified = [];
4739
4740     this.addEvents({
4741         /**
4742          * @event datachanged
4743          * Fires when the data cache has changed, and a widget which is using this Store
4744          * as a Record cache should refresh its view.
4745          * @param {Store} this
4746          */
4747         datachanged : true,
4748         /**
4749          * @event metachange
4750          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4751          * @param {Store} this
4752          * @param {Object} meta The JSON metadata
4753          */
4754         metachange : true,
4755         /**
4756          * @event add
4757          * Fires when Records have been added to the Store
4758          * @param {Store} this
4759          * @param {Roo.data.Record[]} records The array of Records added
4760          * @param {Number} index The index at which the record(s) were added
4761          */
4762         add : true,
4763         /**
4764          * @event remove
4765          * Fires when a Record has been removed from the Store
4766          * @param {Store} this
4767          * @param {Roo.data.Record} record The Record that was removed
4768          * @param {Number} index The index at which the record was removed
4769          */
4770         remove : true,
4771         /**
4772          * @event update
4773          * Fires when a Record has been updated
4774          * @param {Store} this
4775          * @param {Roo.data.Record} record The Record that was updated
4776          * @param {String} operation The update operation being performed.  Value may be one of:
4777          * <pre><code>
4778  Roo.data.Record.EDIT
4779  Roo.data.Record.REJECT
4780  Roo.data.Record.COMMIT
4781          * </code></pre>
4782          */
4783         update : true,
4784         /**
4785          * @event clear
4786          * Fires when the data cache has been cleared.
4787          * @param {Store} this
4788          */
4789         clear : true,
4790         /**
4791          * @event beforeload
4792          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4793          * the load action will be canceled.
4794          * @param {Store} this
4795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4796          */
4797         beforeload : true,
4798         /**
4799          * @event load
4800          * Fires after a new set of Records has been loaded.
4801          * @param {Store} this
4802          * @param {Roo.data.Record[]} records The Records that were loaded
4803          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4804          */
4805         load : true,
4806         /**
4807          * @event loadexception
4808          * Fires if an exception occurs in the Proxy during loading.
4809          * Called with the signature of the Proxy's "loadexception" event.
4810          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4811          * 
4812          * @param {Proxy} 
4813          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4814          * @param {Object} load options 
4815          * @param {Object} jsonData from your request (normally this contains the Exception)
4816          */
4817         loadexception : true
4818     });
4819     
4820     if(this.proxy){
4821         this.proxy = Roo.factory(this.proxy, Roo.data);
4822         this.proxy.xmodule = this.xmodule || false;
4823         this.relayEvents(this.proxy,  ["loadexception"]);
4824     }
4825     this.sortToggle = {};
4826
4827     Roo.data.Store.superclass.constructor.call(this);
4828
4829     if(this.inlineData){
4830         this.loadData(this.inlineData);
4831         delete this.inlineData;
4832     }
4833 };
4834 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4835      /**
4836     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4837     * without a remote query - used by combo/forms at present.
4838     */
4839     
4840     /**
4841     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4842     */
4843     /**
4844     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4845     */
4846     /**
4847     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4848     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4849     */
4850     /**
4851     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4852     * on any HTTP request
4853     */
4854     /**
4855     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4856     */
4857     /**
4858     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4859     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4860     */
4861     remoteSort : false,
4862
4863     /**
4864     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4865      * loaded or when a record is removed. (defaults to false).
4866     */
4867     pruneModifiedRecords : false,
4868
4869     // private
4870     lastOptions : null,
4871
4872     /**
4873      * Add Records to the Store and fires the add event.
4874      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4875      */
4876     add : function(records){
4877         records = [].concat(records);
4878         for(var i = 0, len = records.length; i < len; i++){
4879             records[i].join(this);
4880         }
4881         var index = this.data.length;
4882         this.data.addAll(records);
4883         this.fireEvent("add", this, records, index);
4884     },
4885
4886     /**
4887      * Remove a Record from the Store and fires the remove event.
4888      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4889      */
4890     remove : function(record){
4891         var index = this.data.indexOf(record);
4892         this.data.removeAt(index);
4893         if(this.pruneModifiedRecords){
4894             this.modified.remove(record);
4895         }
4896         this.fireEvent("remove", this, record, index);
4897     },
4898
4899     /**
4900      * Remove all Records from the Store and fires the clear event.
4901      */
4902     removeAll : function(){
4903         this.data.clear();
4904         if(this.pruneModifiedRecords){
4905             this.modified = [];
4906         }
4907         this.fireEvent("clear", this);
4908     },
4909
4910     /**
4911      * Inserts Records to the Store at the given index and fires the add event.
4912      * @param {Number} index The start index at which to insert the passed Records.
4913      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4914      */
4915     insert : function(index, records){
4916         records = [].concat(records);
4917         for(var i = 0, len = records.length; i < len; i++){
4918             this.data.insert(index, records[i]);
4919             records[i].join(this);
4920         }
4921         this.fireEvent("add", this, records, index);
4922     },
4923
4924     /**
4925      * Get the index within the cache of the passed Record.
4926      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4927      * @return {Number} The index of the passed Record. Returns -1 if not found.
4928      */
4929     indexOf : function(record){
4930         return this.data.indexOf(record);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the Record with the passed id.
4935      * @param {String} id The id of the Record to find.
4936      * @return {Number} The index of the Record. Returns -1 if not found.
4937      */
4938     indexOfId : function(id){
4939         return this.data.indexOfKey(id);
4940     },
4941
4942     /**
4943      * Get the Record with the specified id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4946      */
4947     getById : function(id){
4948         return this.data.key(id);
4949     },
4950
4951     /**
4952      * Get the Record at the specified index.
4953      * @param {Number} index The index of the Record to find.
4954      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4955      */
4956     getAt : function(index){
4957         return this.data.itemAt(index);
4958     },
4959
4960     /**
4961      * Returns a range of Records between specified indices.
4962      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4963      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4964      * @return {Roo.data.Record[]} An array of Records
4965      */
4966     getRange : function(start, end){
4967         return this.data.getRange(start, end);
4968     },
4969
4970     // private
4971     storeOptions : function(o){
4972         o = Roo.apply({}, o);
4973         delete o.callback;
4974         delete o.scope;
4975         this.lastOptions = o;
4976     },
4977
4978     /**
4979      * Loads the Record cache from the configured Proxy using the configured Reader.
4980      * <p>
4981      * If using remote paging, then the first load call must specify the <em>start</em>
4982      * and <em>limit</em> properties in the options.params property to establish the initial
4983      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4984      * <p>
4985      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4986      * and this call will return before the new data has been loaded. Perform any post-processing
4987      * in a callback function, or in a "load" event handler.</strong>
4988      * <p>
4989      * @param {Object} options An object containing properties which control loading options:<ul>
4990      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4991      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4992      * passed the following arguments:<ul>
4993      * <li>r : Roo.data.Record[]</li>
4994      * <li>options: Options object from the load call</li>
4995      * <li>success: Boolean success indicator</li></ul></li>
4996      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4997      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4998      * </ul>
4999      */
5000     load : function(options){
5001         options = options || {};
5002         if(this.fireEvent("beforeload", this, options) !== false){
5003             this.storeOptions(options);
5004             var p = Roo.apply(options.params || {}, this.baseParams);
5005             // if meta was not loaded from remote source.. try requesting it.
5006             if (!this.reader.metaFromRemote) {
5007                 p._requestMeta = 1;
5008             }
5009             if(this.sortInfo && this.remoteSort){
5010                 var pn = this.paramNames;
5011                 p[pn["sort"]] = this.sortInfo.field;
5012                 p[pn["dir"]] = this.sortInfo.direction;
5013             }
5014             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5015         }
5016     },
5017
5018     /**
5019      * Reloads the Record cache from the configured Proxy using the configured Reader and
5020      * the options from the last load operation performed.
5021      * @param {Object} options (optional) An object containing properties which may override the options
5022      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5023      * the most recently used options are reused).
5024      */
5025     reload : function(options){
5026         this.load(Roo.applyIf(options||{}, this.lastOptions));
5027     },
5028
5029     // private
5030     // Called as a callback by the Reader during a load operation.
5031     loadRecords : function(o, options, success){
5032         if(!o || success === false){
5033             if(success !== false){
5034                 this.fireEvent("load", this, [], options);
5035             }
5036             if(options.callback){
5037                 options.callback.call(options.scope || this, [], options, false);
5038             }
5039             return;
5040         }
5041         // if data returned failure - throw an exception.
5042         if (o.success === false) {
5043             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5044             return;
5045         }
5046         var r = o.records, t = o.totalRecords || r.length;
5047         if(!options || options.add !== true){
5048             if(this.pruneModifiedRecords){
5049                 this.modified = [];
5050             }
5051             for(var i = 0, len = r.length; i < len; i++){
5052                 r[i].join(this);
5053             }
5054             if(this.snapshot){
5055                 this.data = this.snapshot;
5056                 delete this.snapshot;
5057             }
5058             this.data.clear();
5059             this.data.addAll(r);
5060             this.totalLength = t;
5061             this.applySort();
5062             this.fireEvent("datachanged", this);
5063         }else{
5064             this.totalLength = Math.max(t, this.data.length+r.length);
5065             this.add(r);
5066         }
5067         this.fireEvent("load", this, r, options);
5068         if(options.callback){
5069             options.callback.call(options.scope || this, r, options, true);
5070         }
5071     },
5072
5073     /**
5074      * Loads data from a passed data block. A Reader which understands the format of the data
5075      * must have been configured in the constructor.
5076      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5077      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5078      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5079      */
5080     loadData : function(o, append){
5081         var r = this.reader.readRecords(o);
5082         this.loadRecords(r, {add: append}, true);
5083     },
5084
5085     /**
5086      * Gets the number of cached records.
5087      * <p>
5088      * <em>If using paging, this may not be the total size of the dataset. If the data object
5089      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5090      * the data set size</em>
5091      */
5092     getCount : function(){
5093         return this.data.length || 0;
5094     },
5095
5096     /**
5097      * Gets the total number of records in the dataset as returned by the server.
5098      * <p>
5099      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5100      * the dataset size</em>
5101      */
5102     getTotalCount : function(){
5103         return this.totalLength || 0;
5104     },
5105
5106     /**
5107      * Returns the sort state of the Store as an object with two properties:
5108      * <pre><code>
5109  field {String} The name of the field by which the Records are sorted
5110  direction {String} The sort order, "ASC" or "DESC"
5111      * </code></pre>
5112      */
5113     getSortState : function(){
5114         return this.sortInfo;
5115     },
5116
5117     // private
5118     applySort : function(){
5119         if(this.sortInfo && !this.remoteSort){
5120             var s = this.sortInfo, f = s.field;
5121             var st = this.fields.get(f).sortType;
5122             var fn = function(r1, r2){
5123                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5124                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5125             };
5126             this.data.sort(s.direction, fn);
5127             if(this.snapshot && this.snapshot != this.data){
5128                 this.snapshot.sort(s.direction, fn);
5129             }
5130         }
5131     },
5132
5133     /**
5134      * Sets the default sort column and order to be used by the next load operation.
5135      * @param {String} fieldName The name of the field to sort by.
5136      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5137      */
5138     setDefaultSort : function(field, dir){
5139         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5140     },
5141
5142     /**
5143      * Sort the Records.
5144      * If remote sorting is used, the sort is performed on the server, and the cache is
5145      * reloaded. If local sorting is used, the cache is sorted internally.
5146      * @param {String} fieldName The name of the field to sort by.
5147      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5148      */
5149     sort : function(fieldName, dir){
5150         var f = this.fields.get(fieldName);
5151         if(!dir){
5152             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5153                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5154             }else{
5155                 dir = f.sortDir;
5156             }
5157         }
5158         this.sortToggle[f.name] = dir;
5159         this.sortInfo = {field: f.name, direction: dir};
5160         if(!this.remoteSort){
5161             this.applySort();
5162             this.fireEvent("datachanged", this);
5163         }else{
5164             this.load(this.lastOptions);
5165         }
5166     },
5167
5168     /**
5169      * Calls the specified function for each of the Records in the cache.
5170      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5171      * Returning <em>false</em> aborts and exits the iteration.
5172      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5173      */
5174     each : function(fn, scope){
5175         this.data.each(fn, scope);
5176     },
5177
5178     /**
5179      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5180      * (e.g., during paging).
5181      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5182      */
5183     getModifiedRecords : function(){
5184         return this.modified;
5185     },
5186
5187     // private
5188     createFilterFn : function(property, value, anyMatch){
5189         if(!value.exec){ // not a regex
5190             value = String(value);
5191             if(value.length == 0){
5192                 return false;
5193             }
5194             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5195         }
5196         return function(r){
5197             return value.test(r.data[property]);
5198         };
5199     },
5200
5201     /**
5202      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5203      * @param {String} property A field on your records
5204      * @param {Number} start The record index to start at (defaults to 0)
5205      * @param {Number} end The last record index to include (defaults to length - 1)
5206      * @return {Number} The sum
5207      */
5208     sum : function(property, start, end){
5209         var rs = this.data.items, v = 0;
5210         start = start || 0;
5211         end = (end || end === 0) ? end : rs.length-1;
5212
5213         for(var i = start; i <= end; i++){
5214             v += (rs[i].data[property] || 0);
5215         }
5216         return v;
5217     },
5218
5219     /**
5220      * Filter the records by a specified property.
5221      * @param {String} field A field on your records
5222      * @param {String/RegExp} value Either a string that the field
5223      * should start with or a RegExp to test against the field
5224      * @param {Boolean} anyMatch True to match any part not just the beginning
5225      */
5226     filter : function(property, value, anyMatch){
5227         var fn = this.createFilterFn(property, value, anyMatch);
5228         return fn ? this.filterBy(fn) : this.clearFilter();
5229     },
5230
5231     /**
5232      * Filter by a function. The specified function will be called with each
5233      * record in this data source. If the function returns true the record is included,
5234      * otherwise it is filtered.
5235      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5236      * @param {Object} scope (optional) The scope of the function (defaults to this)
5237      */
5238     filterBy : function(fn, scope){
5239         this.snapshot = this.snapshot || this.data;
5240         this.data = this.queryBy(fn, scope||this);
5241         this.fireEvent("datachanged", this);
5242     },
5243
5244     /**
5245      * Query the records by a specified property.
5246      * @param {String} field A field on your records
5247      * @param {String/RegExp} value Either a string that the field
5248      * should start with or a RegExp to test against the field
5249      * @param {Boolean} anyMatch True to match any part not just the beginning
5250      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5251      */
5252     query : function(property, value, anyMatch){
5253         var fn = this.createFilterFn(property, value, anyMatch);
5254         return fn ? this.queryBy(fn) : this.data.clone();
5255     },
5256
5257     /**
5258      * Query by a function. The specified function will be called with each
5259      * record in this data source. If the function returns true the record is included
5260      * in the results.
5261      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5262      * @param {Object} scope (optional) The scope of the function (defaults to this)
5263       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5264      **/
5265     queryBy : function(fn, scope){
5266         var data = this.snapshot || this.data;
5267         return data.filterBy(fn, scope||this);
5268     },
5269
5270     /**
5271      * Collects unique values for a particular dataIndex from this store.
5272      * @param {String} dataIndex The property to collect
5273      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5274      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5275      * @return {Array} An array of the unique values
5276      **/
5277     collect : function(dataIndex, allowNull, bypassFilter){
5278         var d = (bypassFilter === true && this.snapshot) ?
5279                 this.snapshot.items : this.data.items;
5280         var v, sv, r = [], l = {};
5281         for(var i = 0, len = d.length; i < len; i++){
5282             v = d[i].data[dataIndex];
5283             sv = String(v);
5284             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5285                 l[sv] = true;
5286                 r[r.length] = v;
5287             }
5288         }
5289         return r;
5290     },
5291
5292     /**
5293      * Revert to a view of the Record cache with no filtering applied.
5294      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5295      */
5296     clearFilter : function(suppressEvent){
5297         if(this.snapshot && this.snapshot != this.data){
5298             this.data = this.snapshot;
5299             delete this.snapshot;
5300             if(suppressEvent !== true){
5301                 this.fireEvent("datachanged", this);
5302             }
5303         }
5304     },
5305
5306     // private
5307     afterEdit : function(record){
5308         if(this.modified.indexOf(record) == -1){
5309             this.modified.push(record);
5310         }
5311         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5312     },
5313
5314     // private
5315     afterReject : function(record){
5316         this.modified.remove(record);
5317         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5318     },
5319
5320     // private
5321     afterCommit : function(record){
5322         this.modified.remove(record);
5323         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5324     },
5325
5326     /**
5327      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5328      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5329      */
5330     commitChanges : function(){
5331         var m = this.modified.slice(0);
5332         this.modified = [];
5333         for(var i = 0, len = m.length; i < len; i++){
5334             m[i].commit();
5335         }
5336     },
5337
5338     /**
5339      * Cancel outstanding changes on all changed records.
5340      */
5341     rejectChanges : function(){
5342         var m = this.modified.slice(0);
5343         this.modified = [];
5344         for(var i = 0, len = m.length; i < len; i++){
5345             m[i].reject();
5346         }
5347     },
5348
5349     onMetaChange : function(meta, rtype, o){
5350         this.recordType = rtype;
5351         this.fields = rtype.prototype.fields;
5352         delete this.snapshot;
5353         this.sortInfo = meta.sortInfo || this.sortInfo;
5354         this.modified = [];
5355         this.fireEvent('metachange', this, this.reader.meta);
5356     }
5357 });/*
5358  * Based on:
5359  * Ext JS Library 1.1.1
5360  * Copyright(c) 2006-2007, Ext JS, LLC.
5361  *
5362  * Originally Released Under LGPL - original licence link has changed is not relivant.
5363  *
5364  * Fork - LGPL
5365  * <script type="text/javascript">
5366  */
5367
5368 /**
5369  * @class Roo.data.SimpleStore
5370  * @extends Roo.data.Store
5371  * Small helper class to make creating Stores from Array data easier.
5372  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5373  * @cfg {Array} fields An array of field definition objects, or field name strings.
5374  * @cfg {Array} data The multi-dimensional array of data
5375  * @constructor
5376  * @param {Object} config
5377  */
5378 Roo.data.SimpleStore = function(config){
5379     Roo.data.SimpleStore.superclass.constructor.call(this, {
5380         isLocal : true,
5381         reader: new Roo.data.ArrayReader({
5382                 id: config.id
5383             },
5384             Roo.data.Record.create(config.fields)
5385         ),
5386         proxy : new Roo.data.MemoryProxy(config.data)
5387     });
5388     this.load();
5389 };
5390 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5391  * Based on:
5392  * Ext JS Library 1.1.1
5393  * Copyright(c) 2006-2007, Ext JS, LLC.
5394  *
5395  * Originally Released Under LGPL - original licence link has changed is not relivant.
5396  *
5397  * Fork - LGPL
5398  * <script type="text/javascript">
5399  */
5400
5401 /**
5402 /**
5403  * @extends Roo.data.Store
5404  * @class Roo.data.JsonStore
5405  * Small helper class to make creating Stores for JSON data easier. <br/>
5406 <pre><code>
5407 var store = new Roo.data.JsonStore({
5408     url: 'get-images.php',
5409     root: 'images',
5410     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5411 });
5412 </code></pre>
5413  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5414  * JsonReader and HttpProxy (unless inline data is provided).</b>
5415  * @cfg {Array} fields An array of field definition objects, or field name strings.
5416  * @constructor
5417  * @param {Object} config
5418  */
5419 Roo.data.JsonStore = function(c){
5420     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5421         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5422         reader: new Roo.data.JsonReader(c, c.fields)
5423     }));
5424 };
5425 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5426  * Based on:
5427  * Ext JS Library 1.1.1
5428  * Copyright(c) 2006-2007, Ext JS, LLC.
5429  *
5430  * Originally Released Under LGPL - original licence link has changed is not relivant.
5431  *
5432  * Fork - LGPL
5433  * <script type="text/javascript">
5434  */
5435
5436  
5437 Roo.data.Field = function(config){
5438     if(typeof config == "string"){
5439         config = {name: config};
5440     }
5441     Roo.apply(this, config);
5442     
5443     if(!this.type){
5444         this.type = "auto";
5445     }
5446     
5447     var st = Roo.data.SortTypes;
5448     // named sortTypes are supported, here we look them up
5449     if(typeof this.sortType == "string"){
5450         this.sortType = st[this.sortType];
5451     }
5452     
5453     // set default sortType for strings and dates
5454     if(!this.sortType){
5455         switch(this.type){
5456             case "string":
5457                 this.sortType = st.asUCString;
5458                 break;
5459             case "date":
5460                 this.sortType = st.asDate;
5461                 break;
5462             default:
5463                 this.sortType = st.none;
5464         }
5465     }
5466
5467     // define once
5468     var stripRe = /[\$,%]/g;
5469
5470     // prebuilt conversion function for this field, instead of
5471     // switching every time we're reading a value
5472     if(!this.convert){
5473         var cv, dateFormat = this.dateFormat;
5474         switch(this.type){
5475             case "":
5476             case "auto":
5477             case undefined:
5478                 cv = function(v){ return v; };
5479                 break;
5480             case "string":
5481                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5482                 break;
5483             case "int":
5484                 cv = function(v){
5485                     return v !== undefined && v !== null && v !== '' ?
5486                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5487                     };
5488                 break;
5489             case "float":
5490                 cv = function(v){
5491                     return v !== undefined && v !== null && v !== '' ?
5492                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5493                     };
5494                 break;
5495             case "bool":
5496             case "boolean":
5497                 cv = function(v){ return v === true || v === "true" || v == 1; };
5498                 break;
5499             case "date":
5500                 cv = function(v){
5501                     if(!v){
5502                         return '';
5503                     }
5504                     if(v instanceof Date){
5505                         return v;
5506                     }
5507                     if(dateFormat){
5508                         if(dateFormat == "timestamp"){
5509                             return new Date(v*1000);
5510                         }
5511                         return Date.parseDate(v, dateFormat);
5512                     }
5513                     var parsed = Date.parse(v);
5514                     return parsed ? new Date(parsed) : null;
5515                 };
5516              break;
5517             
5518         }
5519         this.convert = cv;
5520     }
5521 };
5522
5523 Roo.data.Field.prototype = {
5524     dateFormat: null,
5525     defaultValue: "",
5526     mapping: null,
5527     sortType : null,
5528     sortDir : "ASC"
5529 };/*
5530  * Based on:
5531  * Ext JS Library 1.1.1
5532  * Copyright(c) 2006-2007, Ext JS, LLC.
5533  *
5534  * Originally Released Under LGPL - original licence link has changed is not relivant.
5535  *
5536  * Fork - LGPL
5537  * <script type="text/javascript">
5538  */
5539  
5540 // Base class for reading structured data from a data source.  This class is intended to be
5541 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5542
5543 /**
5544  * @class Roo.data.DataReader
5545  * Base class for reading structured data from a data source.  This class is intended to be
5546  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5547  */
5548
5549 Roo.data.DataReader = function(meta, recordType){
5550     
5551     this.meta = meta;
5552     
5553     this.recordType = recordType instanceof Array ? 
5554         Roo.data.Record.create(recordType) : recordType;
5555 };
5556
5557 Roo.data.DataReader.prototype = {
5558      /**
5559      * Create an empty record
5560      * @param {Object} data (optional) - overlay some values
5561      * @return {Roo.data.Record} record created.
5562      */
5563     newRow :  function(d) {
5564         var da =  {};
5565         this.recordType.prototype.fields.each(function(c) {
5566             switch( c.type) {
5567                 case 'int' : da[c.name] = 0; break;
5568                 case 'date' : da[c.name] = new Date(); break;
5569                 case 'float' : da[c.name] = 0.0; break;
5570                 case 'boolean' : da[c.name] = false; break;
5571                 default : da[c.name] = ""; break;
5572             }
5573             
5574         });
5575         return new this.recordType(Roo.apply(da, d));
5576     }
5577     
5578 };/*
5579  * Based on:
5580  * Ext JS Library 1.1.1
5581  * Copyright(c) 2006-2007, Ext JS, LLC.
5582  *
5583  * Originally Released Under LGPL - original licence link has changed is not relivant.
5584  *
5585  * Fork - LGPL
5586  * <script type="text/javascript">
5587  */
5588
5589 /**
5590  * @class Roo.data.DataProxy
5591  * @extends Roo.data.Observable
5592  * This class is an abstract base class for implementations which provide retrieval of
5593  * unformatted data objects.<br>
5594  * <p>
5595  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5596  * (of the appropriate type which knows how to parse the data object) to provide a block of
5597  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5598  * <p>
5599  * Custom implementations must implement the load method as described in
5600  * {@link Roo.data.HttpProxy#load}.
5601  */
5602 Roo.data.DataProxy = function(){
5603     this.addEvents({
5604         /**
5605          * @event beforeload
5606          * Fires before a network request is made to retrieve a data object.
5607          * @param {Object} This DataProxy object.
5608          * @param {Object} params The params parameter to the load function.
5609          */
5610         beforeload : true,
5611         /**
5612          * @event load
5613          * Fires before the load method's callback is called.
5614          * @param {Object} This DataProxy object.
5615          * @param {Object} o The data object.
5616          * @param {Object} arg The callback argument object passed to the load function.
5617          */
5618         load : true,
5619         /**
5620          * @event loadexception
5621          * Fires if an Exception occurs during data retrieval.
5622          * @param {Object} This DataProxy object.
5623          * @param {Object} o The data object.
5624          * @param {Object} arg The callback argument object passed to the load function.
5625          * @param {Object} e The Exception.
5626          */
5627         loadexception : true
5628     });
5629     Roo.data.DataProxy.superclass.constructor.call(this);
5630 };
5631
5632 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5633
5634     /**
5635      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5636      */
5637 /*
5638  * Based on:
5639  * Ext JS Library 1.1.1
5640  * Copyright(c) 2006-2007, Ext JS, LLC.
5641  *
5642  * Originally Released Under LGPL - original licence link has changed is not relivant.
5643  *
5644  * Fork - LGPL
5645  * <script type="text/javascript">
5646  */
5647 /**
5648  * @class Roo.data.MemoryProxy
5649  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5650  * to the Reader when its load method is called.
5651  * @constructor
5652  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5653  */
5654 Roo.data.MemoryProxy = function(data){
5655     if (data.data) {
5656         data = data.data;
5657     }
5658     Roo.data.MemoryProxy.superclass.constructor.call(this);
5659     this.data = data;
5660 };
5661
5662 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5663     /**
5664      * Load data from the requested source (in this case an in-memory
5665      * data object passed to the constructor), read the data object into
5666      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5667      * process that block using the passed callback.
5668      * @param {Object} params This parameter is not used by the MemoryProxy class.
5669      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5670      * object into a block of Roo.data.Records.
5671      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5672      * The function must be passed <ul>
5673      * <li>The Record block object</li>
5674      * <li>The "arg" argument from the load function</li>
5675      * <li>A boolean success indicator</li>
5676      * </ul>
5677      * @param {Object} scope The scope in which to call the callback
5678      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5679      */
5680     load : function(params, reader, callback, scope, arg){
5681         params = params || {};
5682         var result;
5683         try {
5684             result = reader.readRecords(this.data);
5685         }catch(e){
5686             this.fireEvent("loadexception", this, arg, null, e);
5687             callback.call(scope, null, arg, false);
5688             return;
5689         }
5690         callback.call(scope, result, arg, true);
5691     },
5692     
5693     // private
5694     update : function(params, records){
5695         
5696     }
5697 });/*
5698  * Based on:
5699  * Ext JS Library 1.1.1
5700  * Copyright(c) 2006-2007, Ext JS, LLC.
5701  *
5702  * Originally Released Under LGPL - original licence link has changed is not relivant.
5703  *
5704  * Fork - LGPL
5705  * <script type="text/javascript">
5706  */
5707 /**
5708  * @class Roo.data.HttpProxy
5709  * @extends Roo.data.DataProxy
5710  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5711  * configured to reference a certain URL.<br><br>
5712  * <p>
5713  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5714  * from which the running page was served.<br><br>
5715  * <p>
5716  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5717  * <p>
5718  * Be aware that to enable the browser to parse an XML document, the server must set
5719  * the Content-Type header in the HTTP response to "text/xml".
5720  * @constructor
5721  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5722  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5723  * will be used to make the request.
5724  */
5725 Roo.data.HttpProxy = function(conn){
5726     Roo.data.HttpProxy.superclass.constructor.call(this);
5727     // is conn a conn config or a real conn?
5728     this.conn = conn;
5729     this.useAjax = !conn || !conn.events;
5730   
5731 };
5732
5733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5734     // thse are take from connection...
5735     
5736     /**
5737      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5738      */
5739     /**
5740      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5741      * extra parameters to each request made by this object. (defaults to undefined)
5742      */
5743     /**
5744      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5745      *  to each request made by this object. (defaults to undefined)
5746      */
5747     /**
5748      * @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)
5749      */
5750     /**
5751      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5752      */
5753      /**
5754      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5755      * @type Boolean
5756      */
5757   
5758
5759     /**
5760      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5761      * @type Boolean
5762      */
5763     /**
5764      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5765      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5766      * a finer-grained basis than the DataProxy events.
5767      */
5768     getConnection : function(){
5769         return this.useAjax ? Roo.Ajax : this.conn;
5770     },
5771
5772     /**
5773      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5774      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5775      * process that block using the passed callback.
5776      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5777      * for the request to the remote server.
5778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5779      * object into a block of Roo.data.Records.
5780      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5781      * The function must be passed <ul>
5782      * <li>The Record block object</li>
5783      * <li>The "arg" argument from the load function</li>
5784      * <li>A boolean success indicator</li>
5785      * </ul>
5786      * @param {Object} scope The scope in which to call the callback
5787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5788      */
5789     load : function(params, reader, callback, scope, arg){
5790         if(this.fireEvent("beforeload", this, params) !== false){
5791             var  o = {
5792                 params : params || {},
5793                 request: {
5794                     callback : callback,
5795                     scope : scope,
5796                     arg : arg
5797                 },
5798                 reader: reader,
5799                 callback : this.loadResponse,
5800                 scope: this
5801             };
5802             if(this.useAjax){
5803                 Roo.applyIf(o, this.conn);
5804                 if(this.activeRequest){
5805                     Roo.Ajax.abort(this.activeRequest);
5806                 }
5807                 this.activeRequest = Roo.Ajax.request(o);
5808             }else{
5809                 this.conn.request(o);
5810             }
5811         }else{
5812             callback.call(scope||this, null, arg, false);
5813         }
5814     },
5815
5816     // private
5817     loadResponse : function(o, success, response){
5818         delete this.activeRequest;
5819         if(!success){
5820             this.fireEvent("loadexception", this, o, response);
5821             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5822             return;
5823         }
5824         var result;
5825         try {
5826             result = o.reader.read(response);
5827         }catch(e){
5828             this.fireEvent("loadexception", this, o, response, e);
5829             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5830             return;
5831         }
5832         
5833         this.fireEvent("load", this, o, o.request.arg);
5834         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5835     },
5836
5837     // private
5838     update : function(dataSet){
5839
5840     },
5841
5842     // private
5843     updateResponse : function(dataSet){
5844
5845     }
5846 });/*
5847  * Based on:
5848  * Ext JS Library 1.1.1
5849  * Copyright(c) 2006-2007, Ext JS, LLC.
5850  *
5851  * Originally Released Under LGPL - original licence link has changed is not relivant.
5852  *
5853  * Fork - LGPL
5854  * <script type="text/javascript">
5855  */
5856
5857 /**
5858  * @class Roo.data.ScriptTagProxy
5859  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5860  * other than the originating domain of the running page.<br><br>
5861  * <p>
5862  * <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
5863  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5864  * <p>
5865  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5866  * source code that is used as the source inside a &lt;script> tag.<br><br>
5867  * <p>
5868  * In order for the browser to process the returned data, the server must wrap the data object
5869  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5870  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5871  * depending on whether the callback name was passed:
5872  * <p>
5873  * <pre><code>
5874 boolean scriptTag = false;
5875 String cb = request.getParameter("callback");
5876 if (cb != null) {
5877     scriptTag = true;
5878     response.setContentType("text/javascript");
5879 } else {
5880     response.setContentType("application/x-json");
5881 }
5882 Writer out = response.getWriter();
5883 if (scriptTag) {
5884     out.write(cb + "(");
5885 }
5886 out.print(dataBlock.toJsonString());
5887 if (scriptTag) {
5888     out.write(");");
5889 }
5890 </pre></code>
5891  *
5892  * @constructor
5893  * @param {Object} config A configuration object.
5894  */
5895 Roo.data.ScriptTagProxy = function(config){
5896     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5897     Roo.apply(this, config);
5898     this.head = document.getElementsByTagName("head")[0];
5899 };
5900
5901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5902
5903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5904     /**
5905      * @cfg {String} url The URL from which to request the data object.
5906      */
5907     /**
5908      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5909      */
5910     timeout : 30000,
5911     /**
5912      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5913      * the server the name of the callback function set up by the load call to process the returned data object.
5914      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5915      * javascript output which calls this named function passing the data object as its only parameter.
5916      */
5917     callbackParam : "callback",
5918     /**
5919      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5920      * name to the request.
5921      */
5922     nocache : true,
5923
5924     /**
5925      * Load data from the configured URL, read the data object into
5926      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5927      * process that block using the passed callback.
5928      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5929      * for the request to the remote server.
5930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5931      * object into a block of Roo.data.Records.
5932      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5933      * The function must be passed <ul>
5934      * <li>The Record block object</li>
5935      * <li>The "arg" argument from the load function</li>
5936      * <li>A boolean success indicator</li>
5937      * </ul>
5938      * @param {Object} scope The scope in which to call the callback
5939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5940      */
5941     load : function(params, reader, callback, scope, arg){
5942         if(this.fireEvent("beforeload", this, params) !== false){
5943
5944             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5945
5946             var url = this.url;
5947             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5948             if(this.nocache){
5949                 url += "&_dc=" + (new Date().getTime());
5950             }
5951             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5952             var trans = {
5953                 id : transId,
5954                 cb : "stcCallback"+transId,
5955                 scriptId : "stcScript"+transId,
5956                 params : params,
5957                 arg : arg,
5958                 url : url,
5959                 callback : callback,
5960                 scope : scope,
5961                 reader : reader
5962             };
5963             var conn = this;
5964
5965             window[trans.cb] = function(o){
5966                 conn.handleResponse(o, trans);
5967             };
5968
5969             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5970
5971             if(this.autoAbort !== false){
5972                 this.abort();
5973             }
5974
5975             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5976
5977             var script = document.createElement("script");
5978             script.setAttribute("src", url);
5979             script.setAttribute("type", "text/javascript");
5980             script.setAttribute("id", trans.scriptId);
5981             this.head.appendChild(script);
5982
5983             this.trans = trans;
5984         }else{
5985             callback.call(scope||this, null, arg, false);
5986         }
5987     },
5988
5989     // private
5990     isLoading : function(){
5991         return this.trans ? true : false;
5992     },
5993
5994     /**
5995      * Abort the current server request.
5996      */
5997     abort : function(){
5998         if(this.isLoading()){
5999             this.destroyTrans(this.trans);
6000         }
6001     },
6002
6003     // private
6004     destroyTrans : function(trans, isLoaded){
6005         this.head.removeChild(document.getElementById(trans.scriptId));
6006         clearTimeout(trans.timeoutId);
6007         if(isLoaded){
6008             window[trans.cb] = undefined;
6009             try{
6010                 delete window[trans.cb];
6011             }catch(e){}
6012         }else{
6013             // if hasn't been loaded, wait for load to remove it to prevent script error
6014             window[trans.cb] = function(){
6015                 window[trans.cb] = undefined;
6016                 try{
6017                     delete window[trans.cb];
6018                 }catch(e){}
6019             };
6020         }
6021     },
6022
6023     // private
6024     handleResponse : function(o, trans){
6025         this.trans = false;
6026         this.destroyTrans(trans, true);
6027         var result;
6028         try {
6029             result = trans.reader.readRecords(o);
6030         }catch(e){
6031             this.fireEvent("loadexception", this, o, trans.arg, e);
6032             trans.callback.call(trans.scope||window, null, trans.arg, false);
6033             return;
6034         }
6035         this.fireEvent("load", this, o, trans.arg);
6036         trans.callback.call(trans.scope||window, result, trans.arg, true);
6037     },
6038
6039     // private
6040     handleFailure : function(trans){
6041         this.trans = false;
6042         this.destroyTrans(trans, false);
6043         this.fireEvent("loadexception", this, null, trans.arg);
6044         trans.callback.call(trans.scope||window, null, trans.arg, false);
6045     }
6046 });/*
6047  * Based on:
6048  * Ext JS Library 1.1.1
6049  * Copyright(c) 2006-2007, Ext JS, LLC.
6050  *
6051  * Originally Released Under LGPL - original licence link has changed is not relivant.
6052  *
6053  * Fork - LGPL
6054  * <script type="text/javascript">
6055  */
6056
6057 /**
6058  * @class Roo.data.JsonReader
6059  * @extends Roo.data.DataReader
6060  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6061  * based on mappings in a provided Roo.data.Record constructor.
6062  * 
6063  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6064  * in the reply previously. 
6065  * 
6066  * <p>
6067  * Example code:
6068  * <pre><code>
6069 var RecordDef = Roo.data.Record.create([
6070     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6071     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6072 ]);
6073 var myReader = new Roo.data.JsonReader({
6074     totalProperty: "results",    // The property which contains the total dataset size (optional)
6075     root: "rows",                // The property which contains an Array of row objects
6076     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6077 }, RecordDef);
6078 </code></pre>
6079  * <p>
6080  * This would consume a JSON file like this:
6081  * <pre><code>
6082 { 'results': 2, 'rows': [
6083     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6084     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6085 }
6086 </code></pre>
6087  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6088  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6089  * paged from the remote server.
6090  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6091  * @cfg {String} root name of the property which contains the Array of row objects.
6092  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6093  * @constructor
6094  * Create a new JsonReader
6095  * @param {Object} meta Metadata configuration options
6096  * @param {Object} recordType Either an Array of field definition objects,
6097  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6098  */
6099 Roo.data.JsonReader = function(meta, recordType){
6100     
6101     meta = meta || {};
6102     // set some defaults:
6103     Roo.applyIf(meta, {
6104         totalProperty: 'total',
6105         successProperty : 'success',
6106         root : 'data',
6107         id : 'id'
6108     });
6109     
6110     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6111 };
6112 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6113     
6114     /**
6115      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6116      * Used by Store query builder to append _requestMeta to params.
6117      * 
6118      */
6119     metaFromRemote : false,
6120     /**
6121      * This method is only used by a DataProxy which has retrieved data from a remote server.
6122      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6123      * @return {Object} data A data block which is used by an Roo.data.Store object as
6124      * a cache of Roo.data.Records.
6125      */
6126     read : function(response){
6127         var json = response.responseText;
6128        
6129         var o = /* eval:var:o */ eval("("+json+")");
6130         if(!o) {
6131             throw {message: "JsonReader.read: Json object not found"};
6132         }
6133         
6134         if(o.metaData){
6135             
6136             delete this.ef;
6137             this.metaFromRemote = true;
6138             this.meta = o.metaData;
6139             this.recordType = Roo.data.Record.create(o.metaData.fields);
6140             this.onMetaChange(this.meta, this.recordType, o);
6141         }
6142         return this.readRecords(o);
6143     },
6144
6145     // private function a store will implement
6146     onMetaChange : function(meta, recordType, o){
6147
6148     },
6149
6150     /**
6151          * @ignore
6152          */
6153     simpleAccess: function(obj, subsc) {
6154         return obj[subsc];
6155     },
6156
6157         /**
6158          * @ignore
6159          */
6160     getJsonAccessor: function(){
6161         var re = /[\[\.]/;
6162         return function(expr) {
6163             try {
6164                 return(re.test(expr))
6165                     ? new Function("obj", "return obj." + expr)
6166                     : function(obj){
6167                         return obj[expr];
6168                     };
6169             } catch(e){}
6170             return Roo.emptyFn;
6171         };
6172     }(),
6173
6174     /**
6175      * Create a data block containing Roo.data.Records from an XML document.
6176      * @param {Object} o An object which contains an Array of row objects in the property specified
6177      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6178      * which contains the total size of the dataset.
6179      * @return {Object} data A data block which is used by an Roo.data.Store object as
6180      * a cache of Roo.data.Records.
6181      */
6182     readRecords : function(o){
6183         /**
6184          * After any data loads, the raw JSON data is available for further custom processing.
6185          * @type Object
6186          */
6187         this.jsonData = o;
6188         var s = this.meta, Record = this.recordType,
6189             f = Record.prototype.fields, fi = f.items, fl = f.length;
6190
6191 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6192         if (!this.ef) {
6193             if(s.totalProperty) {
6194                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6195                 }
6196                 if(s.successProperty) {
6197                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6198                 }
6199                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6200                 if (s.id) {
6201                         var g = this.getJsonAccessor(s.id);
6202                         this.getId = function(rec) {
6203                                 var r = g(rec);
6204                                 return (r === undefined || r === "") ? null : r;
6205                         };
6206                 } else {
6207                         this.getId = function(){return null;};
6208                 }
6209             this.ef = [];
6210             for(var jj = 0; jj < fl; jj++){
6211                 f = fi[jj];
6212                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6213                 this.ef[jj] = this.getJsonAccessor(map);
6214             }
6215         }
6216
6217         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6218         if(s.totalProperty){
6219             var vt = parseInt(this.getTotal(o), 10);
6220             if(!isNaN(vt)){
6221                 totalRecords = vt;
6222             }
6223         }
6224         if(s.successProperty){
6225             var vs = this.getSuccess(o);
6226             if(vs === false || vs === 'false'){
6227                 success = false;
6228             }
6229         }
6230         var records = [];
6231             for(var i = 0; i < c; i++){
6232                     var n = root[i];
6233                 var values = {};
6234                 var id = this.getId(n);
6235                 for(var j = 0; j < fl; j++){
6236                     f = fi[j];
6237                 var v = this.ef[j](n);
6238                 if (!f.convert) {
6239                     Roo.log('missing convert for ' + f.name);
6240                     Roo.log(f);
6241                     continue;
6242                 }
6243                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6244                 }
6245                 var record = new Record(values, id);
6246                 record.json = n;
6247                 records[i] = record;
6248             }
6249             return {
6250                 success : success,
6251                 records : records,
6252                 totalRecords : totalRecords
6253             };
6254     }
6255 });/*
6256  * Based on:
6257  * Ext JS Library 1.1.1
6258  * Copyright(c) 2006-2007, Ext JS, LLC.
6259  *
6260  * Originally Released Under LGPL - original licence link has changed is not relivant.
6261  *
6262  * Fork - LGPL
6263  * <script type="text/javascript">
6264  */
6265
6266 /**
6267  * @class Roo.data.XmlReader
6268  * @extends Roo.data.DataReader
6269  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6270  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6271  * <p>
6272  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6273  * header in the HTTP response must be set to "text/xml".</em>
6274  * <p>
6275  * Example code:
6276  * <pre><code>
6277 var RecordDef = Roo.data.Record.create([
6278    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6279    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6280 ]);
6281 var myReader = new Roo.data.XmlReader({
6282    totalRecords: "results", // The element which contains the total dataset size (optional)
6283    record: "row",           // The repeated element which contains row information
6284    id: "id"                 // The element within the row that provides an ID for the record (optional)
6285 }, RecordDef);
6286 </code></pre>
6287  * <p>
6288  * This would consume an XML file like this:
6289  * <pre><code>
6290 &lt;?xml?>
6291 &lt;dataset>
6292  &lt;results>2&lt;/results>
6293  &lt;row>
6294    &lt;id>1&lt;/id>
6295    &lt;name>Bill&lt;/name>
6296    &lt;occupation>Gardener&lt;/occupation>
6297  &lt;/row>
6298  &lt;row>
6299    &lt;id>2&lt;/id>
6300    &lt;name>Ben&lt;/name>
6301    &lt;occupation>Horticulturalist&lt;/occupation>
6302  &lt;/row>
6303 &lt;/dataset>
6304 </code></pre>
6305  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6306  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6307  * paged from the remote server.
6308  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6309  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6310  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6311  * a record identifier value.
6312  * @constructor
6313  * Create a new XmlReader
6314  * @param {Object} meta Metadata configuration options
6315  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6316  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6317  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6318  */
6319 Roo.data.XmlReader = function(meta, recordType){
6320     meta = meta || {};
6321     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6322 };
6323 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6324     /**
6325      * This method is only used by a DataProxy which has retrieved data from a remote server.
6326          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6327          * to contain a method called 'responseXML' that returns an XML document object.
6328      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6329      * a cache of Roo.data.Records.
6330      */
6331     read : function(response){
6332         var doc = response.responseXML;
6333         if(!doc) {
6334             throw {message: "XmlReader.read: XML Document not available"};
6335         }
6336         return this.readRecords(doc);
6337     },
6338
6339     /**
6340      * Create a data block containing Roo.data.Records from an XML document.
6341          * @param {Object} doc A parsed XML document.
6342      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6343      * a cache of Roo.data.Records.
6344      */
6345     readRecords : function(doc){
6346         /**
6347          * After any data loads/reads, the raw XML Document is available for further custom processing.
6348          * @type XMLDocument
6349          */
6350         this.xmlData = doc;
6351         var root = doc.documentElement || doc;
6352         var q = Roo.DomQuery;
6353         var recordType = this.recordType, fields = recordType.prototype.fields;
6354         var sid = this.meta.id;
6355         var totalRecords = 0, success = true;
6356         if(this.meta.totalRecords){
6357             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6358         }
6359         
6360         if(this.meta.success){
6361             var sv = q.selectValue(this.meta.success, root, true);
6362             success = sv !== false && sv !== 'false';
6363         }
6364         var records = [];
6365         var ns = q.select(this.meta.record, root);
6366         for(var i = 0, len = ns.length; i < len; i++) {
6367                 var n = ns[i];
6368                 var values = {};
6369                 var id = sid ? q.selectValue(sid, n) : undefined;
6370                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6371                     var f = fields.items[j];
6372                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6373                     v = f.convert(v);
6374                     values[f.name] = v;
6375                 }
6376                 var record = new recordType(values, id);
6377                 record.node = n;
6378                 records[records.length] = record;
6379             }
6380
6381             return {
6382                 success : success,
6383                 records : records,
6384                 totalRecords : totalRecords || records.length
6385             };
6386     }
6387 });/*
6388  * Based on:
6389  * Ext JS Library 1.1.1
6390  * Copyright(c) 2006-2007, Ext JS, LLC.
6391  *
6392  * Originally Released Under LGPL - original licence link has changed is not relivant.
6393  *
6394  * Fork - LGPL
6395  * <script type="text/javascript">
6396  */
6397
6398 /**
6399  * @class Roo.data.ArrayReader
6400  * @extends Roo.data.DataReader
6401  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6402  * Each element of that Array represents a row of data fields. The
6403  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6404  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6405  * <p>
6406  * Example code:.
6407  * <pre><code>
6408 var RecordDef = Roo.data.Record.create([
6409     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6410     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6411 ]);
6412 var myReader = new Roo.data.ArrayReader({
6413     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6414 }, RecordDef);
6415 </code></pre>
6416  * <p>
6417  * This would consume an Array like this:
6418  * <pre><code>
6419 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6420   </code></pre>
6421  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6422  * @constructor
6423  * Create a new JsonReader
6424  * @param {Object} meta Metadata configuration options.
6425  * @param {Object} recordType Either an Array of field definition objects
6426  * as specified to {@link Roo.data.Record#create},
6427  * or an {@link Roo.data.Record} object
6428  * created using {@link Roo.data.Record#create}.
6429  */
6430 Roo.data.ArrayReader = function(meta, recordType){
6431     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6432 };
6433
6434 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6435     /**
6436      * Create a data block containing Roo.data.Records from an XML document.
6437      * @param {Object} o An Array of row objects which represents the dataset.
6438      * @return {Object} data A data block which is used by an Roo.data.Store object as
6439      * a cache of Roo.data.Records.
6440      */
6441     readRecords : function(o){
6442         var sid = this.meta ? this.meta.id : null;
6443         var recordType = this.recordType, fields = recordType.prototype.fields;
6444         var records = [];
6445         var root = o;
6446             for(var i = 0; i < root.length; i++){
6447                     var n = root[i];
6448                 var values = {};
6449                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6450                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6451                 var f = fields.items[j];
6452                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6453                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6454                 v = f.convert(v);
6455                 values[f.name] = v;
6456             }
6457                 var record = new recordType(values, id);
6458                 record.json = n;
6459                 records[records.length] = record;
6460             }
6461             return {
6462                 records : records,
6463                 totalRecords : records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477
6478 /**
6479  * @class Roo.data.Tree
6480  * @extends Roo.util.Observable
6481  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6482  * in the tree have most standard DOM functionality.
6483  * @constructor
6484  * @param {Node} root (optional) The root node
6485  */
6486 Roo.data.Tree = function(root){
6487    this.nodeHash = {};
6488    /**
6489     * The root node for this tree
6490     * @type Node
6491     */
6492    this.root = null;
6493    if(root){
6494        this.setRootNode(root);
6495    }
6496    this.addEvents({
6497        /**
6498         * @event append
6499         * Fires when a new child node is appended to a node in this tree.
6500         * @param {Tree} tree The owner tree
6501         * @param {Node} parent The parent node
6502         * @param {Node} node The newly appended node
6503         * @param {Number} index The index of the newly appended node
6504         */
6505        "append" : true,
6506        /**
6507         * @event remove
6508         * Fires when a child node is removed from a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The child node removed
6512         */
6513        "remove" : true,
6514        /**
6515         * @event move
6516         * Fires when a node is moved to a new location in the tree
6517         * @param {Tree} tree The owner tree
6518         * @param {Node} node The node moved
6519         * @param {Node} oldParent The old parent of this node
6520         * @param {Node} newParent The new parent of this node
6521         * @param {Number} index The index it was moved to
6522         */
6523        "move" : true,
6524        /**
6525         * @event insert
6526         * Fires when a new child node is inserted in a node in this tree.
6527         * @param {Tree} tree The owner tree
6528         * @param {Node} parent The parent node
6529         * @param {Node} node The child node inserted
6530         * @param {Node} refNode The child node the node was inserted before
6531         */
6532        "insert" : true,
6533        /**
6534         * @event beforeappend
6535         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node to be appended
6539         */
6540        "beforeappend" : true,
6541        /**
6542         * @event beforeremove
6543         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6544         * @param {Tree} tree The owner tree
6545         * @param {Node} parent The parent node
6546         * @param {Node} node The child node to be removed
6547         */
6548        "beforeremove" : true,
6549        /**
6550         * @event beforemove
6551         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6552         * @param {Tree} tree The owner tree
6553         * @param {Node} node The node being moved
6554         * @param {Node} oldParent The parent of the node
6555         * @param {Node} newParent The new parent the node is moving to
6556         * @param {Number} index The index it is being moved to
6557         */
6558        "beforemove" : true,
6559        /**
6560         * @event beforeinsert
6561         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6562         * @param {Tree} tree The owner tree
6563         * @param {Node} parent The parent node
6564         * @param {Node} node The child node to be inserted
6565         * @param {Node} refNode The child node the node is being inserted before
6566         */
6567        "beforeinsert" : true
6568    });
6569
6570     Roo.data.Tree.superclass.constructor.call(this);
6571 };
6572
6573 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6574     pathSeparator: "/",
6575
6576     proxyNodeEvent : function(){
6577         return this.fireEvent.apply(this, arguments);
6578     },
6579
6580     /**
6581      * Returns the root node for this tree.
6582      * @return {Node}
6583      */
6584     getRootNode : function(){
6585         return this.root;
6586     },
6587
6588     /**
6589      * Sets the root node for this tree.
6590      * @param {Node} node
6591      * @return {Node}
6592      */
6593     setRootNode : function(node){
6594         this.root = node;
6595         node.ownerTree = this;
6596         node.isRoot = true;
6597         this.registerNode(node);
6598         return node;
6599     },
6600
6601     /**
6602      * Gets a node in this tree by its id.
6603      * @param {String} id
6604      * @return {Node}
6605      */
6606     getNodeById : function(id){
6607         return this.nodeHash[id];
6608     },
6609
6610     registerNode : function(node){
6611         this.nodeHash[node.id] = node;
6612     },
6613
6614     unregisterNode : function(node){
6615         delete this.nodeHash[node.id];
6616     },
6617
6618     toString : function(){
6619         return "[Tree"+(this.id?" "+this.id:"")+"]";
6620     }
6621 });
6622
6623 /**
6624  * @class Roo.data.Node
6625  * @extends Roo.util.Observable
6626  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6627  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6628  * @constructor
6629  * @param {Object} attributes The attributes/config for the node
6630  */
6631 Roo.data.Node = function(attributes){
6632     /**
6633      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6634      * @type {Object}
6635      */
6636     this.attributes = attributes || {};
6637     this.leaf = this.attributes.leaf;
6638     /**
6639      * The node id. @type String
6640      */
6641     this.id = this.attributes.id;
6642     if(!this.id){
6643         this.id = Roo.id(null, "ynode-");
6644         this.attributes.id = this.id;
6645     }
6646     /**
6647      * All child nodes of this node. @type Array
6648      */
6649     this.childNodes = [];
6650     if(!this.childNodes.indexOf){ // indexOf is a must
6651         this.childNodes.indexOf = function(o){
6652             for(var i = 0, len = this.length; i < len; i++){
6653                 if(this[i] == o) {
6654                     return i;
6655                 }
6656             }
6657             return -1;
6658         };
6659     }
6660     /**
6661      * The parent node for this node. @type Node
6662      */
6663     this.parentNode = null;
6664     /**
6665      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6666      */
6667     this.firstChild = null;
6668     /**
6669      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6670      */
6671     this.lastChild = null;
6672     /**
6673      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6674      */
6675     this.previousSibling = null;
6676     /**
6677      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6678      */
6679     this.nextSibling = null;
6680
6681     this.addEvents({
6682        /**
6683         * @event append
6684         * Fires when a new child node is appended
6685         * @param {Tree} tree The owner tree
6686         * @param {Node} this This node
6687         * @param {Node} node The newly appended node
6688         * @param {Number} index The index of the newly appended node
6689         */
6690        "append" : true,
6691        /**
6692         * @event remove
6693         * Fires when a child node is removed
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The removed node
6697         */
6698        "remove" : true,
6699        /**
6700         * @event move
6701         * Fires when this node is moved to a new location in the tree
6702         * @param {Tree} tree The owner tree
6703         * @param {Node} this This node
6704         * @param {Node} oldParent The old parent of this node
6705         * @param {Node} newParent The new parent of this node
6706         * @param {Number} index The index it was moved to
6707         */
6708        "move" : true,
6709        /**
6710         * @event insert
6711         * Fires when a new child node is inserted.
6712         * @param {Tree} tree The owner tree
6713         * @param {Node} this This node
6714         * @param {Node} node The child node inserted
6715         * @param {Node} refNode The child node the node was inserted before
6716         */
6717        "insert" : true,
6718        /**
6719         * @event beforeappend
6720         * Fires before a new child is appended, return false to cancel the append.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node to be appended
6724         */
6725        "beforeappend" : true,
6726        /**
6727         * @event beforeremove
6728         * Fires before a child is removed, return false to cancel the remove.
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The child node to be removed
6732         */
6733        "beforeremove" : true,
6734        /**
6735         * @event beforemove
6736         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6737         * @param {Tree} tree The owner tree
6738         * @param {Node} this This node
6739         * @param {Node} oldParent The parent of this node
6740         * @param {Node} newParent The new parent this node is moving to
6741         * @param {Number} index The index it is being moved to
6742         */
6743        "beforemove" : true,
6744        /**
6745         * @event beforeinsert
6746         * Fires before a new child is inserted, return false to cancel the insert.
6747         * @param {Tree} tree The owner tree
6748         * @param {Node} this This node
6749         * @param {Node} node The child node to be inserted
6750         * @param {Node} refNode The child node the node is being inserted before
6751         */
6752        "beforeinsert" : true
6753    });
6754     this.listeners = this.attributes.listeners;
6755     Roo.data.Node.superclass.constructor.call(this);
6756 };
6757
6758 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6759     fireEvent : function(evtName){
6760         // first do standard event for this node
6761         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6762             return false;
6763         }
6764         // then bubble it up to the tree if the event wasn't cancelled
6765         var ot = this.getOwnerTree();
6766         if(ot){
6767             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6768                 return false;
6769             }
6770         }
6771         return true;
6772     },
6773
6774     /**
6775      * Returns true if this node is a leaf
6776      * @return {Boolean}
6777      */
6778     isLeaf : function(){
6779         return this.leaf === true;
6780     },
6781
6782     // private
6783     setFirstChild : function(node){
6784         this.firstChild = node;
6785     },
6786
6787     //private
6788     setLastChild : function(node){
6789         this.lastChild = node;
6790     },
6791
6792
6793     /**
6794      * Returns true if this node is the last child of its parent
6795      * @return {Boolean}
6796      */
6797     isLast : function(){
6798        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6799     },
6800
6801     /**
6802      * Returns true if this node is the first child of its parent
6803      * @return {Boolean}
6804      */
6805     isFirst : function(){
6806        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6807     },
6808
6809     hasChildNodes : function(){
6810         return !this.isLeaf() && this.childNodes.length > 0;
6811     },
6812
6813     /**
6814      * Insert node(s) as the last child node of this node.
6815      * @param {Node/Array} node The node or Array of nodes to append
6816      * @return {Node} The appended node if single append, or null if an array was passed
6817      */
6818     appendChild : function(node){
6819         var multi = false;
6820         if(node instanceof Array){
6821             multi = node;
6822         }else if(arguments.length > 1){
6823             multi = arguments;
6824         }
6825         // if passed an array or multiple args do them one by one
6826         if(multi){
6827             for(var i = 0, len = multi.length; i < len; i++) {
6828                 this.appendChild(multi[i]);
6829             }
6830         }else{
6831             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6832                 return false;
6833             }
6834             var index = this.childNodes.length;
6835             var oldParent = node.parentNode;
6836             // it's a move, make sure we move it cleanly
6837             if(oldParent){
6838                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6839                     return false;
6840                 }
6841                 oldParent.removeChild(node);
6842             }
6843             index = this.childNodes.length;
6844             if(index == 0){
6845                 this.setFirstChild(node);
6846             }
6847             this.childNodes.push(node);
6848             node.parentNode = this;
6849             var ps = this.childNodes[index-1];
6850             if(ps){
6851                 node.previousSibling = ps;
6852                 ps.nextSibling = node;
6853             }else{
6854                 node.previousSibling = null;
6855             }
6856             node.nextSibling = null;
6857             this.setLastChild(node);
6858             node.setOwnerTree(this.getOwnerTree());
6859             this.fireEvent("append", this.ownerTree, this, node, index);
6860             if(oldParent){
6861                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6862             }
6863             return node;
6864         }
6865     },
6866
6867     /**
6868      * Removes a child node from this node.
6869      * @param {Node} node The node to remove
6870      * @return {Node} The removed node
6871      */
6872     removeChild : function(node){
6873         var index = this.childNodes.indexOf(node);
6874         if(index == -1){
6875             return false;
6876         }
6877         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6878             return false;
6879         }
6880
6881         // remove it from childNodes collection
6882         this.childNodes.splice(index, 1);
6883
6884         // update siblings
6885         if(node.previousSibling){
6886             node.previousSibling.nextSibling = node.nextSibling;
6887         }
6888         if(node.nextSibling){
6889             node.nextSibling.previousSibling = node.previousSibling;
6890         }
6891
6892         // update child refs
6893         if(this.firstChild == node){
6894             this.setFirstChild(node.nextSibling);
6895         }
6896         if(this.lastChild == node){
6897             this.setLastChild(node.previousSibling);
6898         }
6899
6900         node.setOwnerTree(null);
6901         // clear any references from the node
6902         node.parentNode = null;
6903         node.previousSibling = null;
6904         node.nextSibling = null;
6905         this.fireEvent("remove", this.ownerTree, this, node);
6906         return node;
6907     },
6908
6909     /**
6910      * Inserts the first node before the second node in this nodes childNodes collection.
6911      * @param {Node} node The node to insert
6912      * @param {Node} refNode The node to insert before (if null the node is appended)
6913      * @return {Node} The inserted node
6914      */
6915     insertBefore : function(node, refNode){
6916         if(!refNode){ // like standard Dom, refNode can be null for append
6917             return this.appendChild(node);
6918         }
6919         // nothing to do
6920         if(node == refNode){
6921             return false;
6922         }
6923
6924         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6925             return false;
6926         }
6927         var index = this.childNodes.indexOf(refNode);
6928         var oldParent = node.parentNode;
6929         var refIndex = index;
6930
6931         // when moving internally, indexes will change after remove
6932         if(oldParent == this && this.childNodes.indexOf(node) < index){
6933             refIndex--;
6934         }
6935
6936         // it's a move, make sure we move it cleanly
6937         if(oldParent){
6938             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6939                 return false;
6940             }
6941             oldParent.removeChild(node);
6942         }
6943         if(refIndex == 0){
6944             this.setFirstChild(node);
6945         }
6946         this.childNodes.splice(refIndex, 0, node);
6947         node.parentNode = this;
6948         var ps = this.childNodes[refIndex-1];
6949         if(ps){
6950             node.previousSibling = ps;
6951             ps.nextSibling = node;
6952         }else{
6953             node.previousSibling = null;
6954         }
6955         node.nextSibling = refNode;
6956         refNode.previousSibling = node;
6957         node.setOwnerTree(this.getOwnerTree());
6958         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6959         if(oldParent){
6960             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6961         }
6962         return node;
6963     },
6964
6965     /**
6966      * Returns the child node at the specified index.
6967      * @param {Number} index
6968      * @return {Node}
6969      */
6970     item : function(index){
6971         return this.childNodes[index];
6972     },
6973
6974     /**
6975      * Replaces one child node in this node with another.
6976      * @param {Node} newChild The replacement node
6977      * @param {Node} oldChild The node to replace
6978      * @return {Node} The replaced node
6979      */
6980     replaceChild : function(newChild, oldChild){
6981         this.insertBefore(newChild, oldChild);
6982         this.removeChild(oldChild);
6983         return oldChild;
6984     },
6985
6986     /**
6987      * Returns the index of a child node
6988      * @param {Node} node
6989      * @return {Number} The index of the node or -1 if it was not found
6990      */
6991     indexOf : function(child){
6992         return this.childNodes.indexOf(child);
6993     },
6994
6995     /**
6996      * Returns the tree this node is in.
6997      * @return {Tree}
6998      */
6999     getOwnerTree : function(){
7000         // if it doesn't have one, look for one
7001         if(!this.ownerTree){
7002             var p = this;
7003             while(p){
7004                 if(p.ownerTree){
7005                     this.ownerTree = p.ownerTree;
7006                     break;
7007                 }
7008                 p = p.parentNode;
7009             }
7010         }
7011         return this.ownerTree;
7012     },
7013
7014     /**
7015      * Returns depth of this node (the root node has a depth of 0)
7016      * @return {Number}
7017      */
7018     getDepth : function(){
7019         var depth = 0;
7020         var p = this;
7021         while(p.parentNode){
7022             ++depth;
7023             p = p.parentNode;
7024         }
7025         return depth;
7026     },
7027
7028     // private
7029     setOwnerTree : function(tree){
7030         // if it's move, we need to update everyone
7031         if(tree != this.ownerTree){
7032             if(this.ownerTree){
7033                 this.ownerTree.unregisterNode(this);
7034             }
7035             this.ownerTree = tree;
7036             var cs = this.childNodes;
7037             for(var i = 0, len = cs.length; i < len; i++) {
7038                 cs[i].setOwnerTree(tree);
7039             }
7040             if(tree){
7041                 tree.registerNode(this);
7042             }
7043         }
7044     },
7045
7046     /**
7047      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7048      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7049      * @return {String} The path
7050      */
7051     getPath : function(attr){
7052         attr = attr || "id";
7053         var p = this.parentNode;
7054         var b = [this.attributes[attr]];
7055         while(p){
7056             b.unshift(p.attributes[attr]);
7057             p = p.parentNode;
7058         }
7059         var sep = this.getOwnerTree().pathSeparator;
7060         return sep + b.join(sep);
7061     },
7062
7063     /**
7064      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7065      * function call will be the scope provided or the current node. The arguments to the function
7066      * will be the args provided or the current node. If the function returns false at any point,
7067      * the bubble is stopped.
7068      * @param {Function} fn The function to call
7069      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7070      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7071      */
7072     bubble : function(fn, scope, args){
7073         var p = this;
7074         while(p){
7075             if(fn.call(scope || p, args || p) === false){
7076                 break;
7077             }
7078             p = p.parentNode;
7079         }
7080     },
7081
7082     /**
7083      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7084      * function call will be the scope provided or the current node. The arguments to the function
7085      * will be the args provided or the current node. If the function returns false at any point,
7086      * the cascade is stopped on that branch.
7087      * @param {Function} fn The function to call
7088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7090      */
7091     cascade : function(fn, scope, args){
7092         if(fn.call(scope || this, args || this) !== false){
7093             var cs = this.childNodes;
7094             for(var i = 0, len = cs.length; i < len; i++) {
7095                 cs[i].cascade(fn, scope, args);
7096             }
7097         }
7098     },
7099
7100     /**
7101      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7102      * function call will be the scope provided or the current node. The arguments to the function
7103      * will be the args provided or the current node. If the function returns false at any point,
7104      * the iteration stops.
7105      * @param {Function} fn The function to call
7106      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7107      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7108      */
7109     eachChild : function(fn, scope, args){
7110         var cs = this.childNodes;
7111         for(var i = 0, len = cs.length; i < len; i++) {
7112                 if(fn.call(scope || this, args || cs[i]) === false){
7113                     break;
7114                 }
7115         }
7116     },
7117
7118     /**
7119      * Finds the first child that has the attribute with the specified value.
7120      * @param {String} attribute The attribute name
7121      * @param {Mixed} value The value to search for
7122      * @return {Node} The found child or null if none was found
7123      */
7124     findChild : function(attribute, value){
7125         var cs = this.childNodes;
7126         for(var i = 0, len = cs.length; i < len; i++) {
7127                 if(cs[i].attributes[attribute] == value){
7128                     return cs[i];
7129                 }
7130         }
7131         return null;
7132     },
7133
7134     /**
7135      * Finds the first child by a custom function. The child matches if the function passed
7136      * returns true.
7137      * @param {Function} fn
7138      * @param {Object} scope (optional)
7139      * @return {Node} The found child or null if none was found
7140      */
7141     findChildBy : function(fn, scope){
7142         var cs = this.childNodes;
7143         for(var i = 0, len = cs.length; i < len; i++) {
7144                 if(fn.call(scope||cs[i], cs[i]) === true){
7145                     return cs[i];
7146                 }
7147         }
7148         return null;
7149     },
7150
7151     /**
7152      * Sorts this nodes children using the supplied sort function
7153      * @param {Function} fn
7154      * @param {Object} scope (optional)
7155      */
7156     sort : function(fn, scope){
7157         var cs = this.childNodes;
7158         var len = cs.length;
7159         if(len > 0){
7160             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7161             cs.sort(sortFn);
7162             for(var i = 0; i < len; i++){
7163                 var n = cs[i];
7164                 n.previousSibling = cs[i-1];
7165                 n.nextSibling = cs[i+1];
7166                 if(i == 0){
7167                     this.setFirstChild(n);
7168                 }
7169                 if(i == len-1){
7170                     this.setLastChild(n);
7171                 }
7172             }
7173         }
7174     },
7175
7176     /**
7177      * Returns true if this node is an ancestor (at any point) of the passed node.
7178      * @param {Node} node
7179      * @return {Boolean}
7180      */
7181     contains : function(node){
7182         return node.isAncestor(this);
7183     },
7184
7185     /**
7186      * Returns true if the passed node is an ancestor (at any point) of this node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     isAncestor : function(node){
7191         var p = this.parentNode;
7192         while(p){
7193             if(p == node){
7194                 return true;
7195             }
7196             p = p.parentNode;
7197         }
7198         return false;
7199     },
7200
7201     toString : function(){
7202         return "[Node"+(this.id?" "+this.id:"")+"]";
7203     }
7204 });/*
7205  * Based on:
7206  * Ext JS Library 1.1.1
7207  * Copyright(c) 2006-2007, Ext JS, LLC.
7208  *
7209  * Originally Released Under LGPL - original licence link has changed is not relivant.
7210  *
7211  * Fork - LGPL
7212  * <script type="text/javascript">
7213  */
7214  
7215
7216 /**
7217  * @class Roo.ComponentMgr
7218  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7219  * @singleton
7220  */
7221 Roo.ComponentMgr = function(){
7222     var all = new Roo.util.MixedCollection();
7223
7224     return {
7225         /**
7226          * Registers a component.
7227          * @param {Roo.Component} c The component
7228          */
7229         register : function(c){
7230             all.add(c);
7231         },
7232
7233         /**
7234          * Unregisters a component.
7235          * @param {Roo.Component} c The component
7236          */
7237         unregister : function(c){
7238             all.remove(c);
7239         },
7240
7241         /**
7242          * Returns a component by id
7243          * @param {String} id The component id
7244          */
7245         get : function(id){
7246             return all.get(id);
7247         },
7248
7249         /**
7250          * Registers a function that will be called when a specified component is added to ComponentMgr
7251          * @param {String} id The component id
7252          * @param {Funtction} fn The callback function
7253          * @param {Object} scope The scope of the callback
7254          */
7255         onAvailable : function(id, fn, scope){
7256             all.on("add", function(index, o){
7257                 if(o.id == id){
7258                     fn.call(scope || o, o);
7259                     all.un("add", fn, scope);
7260                 }
7261             });
7262         }
7263     };
7264 }();/*
7265  * Based on:
7266  * Ext JS Library 1.1.1
7267  * Copyright(c) 2006-2007, Ext JS, LLC.
7268  *
7269  * Originally Released Under LGPL - original licence link has changed is not relivant.
7270  *
7271  * Fork - LGPL
7272  * <script type="text/javascript">
7273  */
7274  
7275 /**
7276  * @class Roo.Component
7277  * @extends Roo.util.Observable
7278  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7279  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7280  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7281  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7282  * All visual components (widgets) that require rendering into a layout should subclass Component.
7283  * @constructor
7284  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7285  * 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
7286  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7287  */
7288 Roo.Component = function(config){
7289     config = config || {};
7290     if(config.tagName || config.dom || typeof config == "string"){ // element object
7291         config = {el: config, id: config.id || config};
7292     }
7293     this.initialConfig = config;
7294
7295     Roo.apply(this, config);
7296     this.addEvents({
7297         /**
7298          * @event disable
7299          * Fires after the component is disabled.
7300              * @param {Roo.Component} this
7301              */
7302         disable : true,
7303         /**
7304          * @event enable
7305          * Fires after the component is enabled.
7306              * @param {Roo.Component} this
7307              */
7308         enable : true,
7309         /**
7310          * @event beforeshow
7311          * Fires before the component is shown.  Return false to stop the show.
7312              * @param {Roo.Component} this
7313              */
7314         beforeshow : true,
7315         /**
7316          * @event show
7317          * Fires after the component is shown.
7318              * @param {Roo.Component} this
7319              */
7320         show : true,
7321         /**
7322          * @event beforehide
7323          * Fires before the component is hidden. Return false to stop the hide.
7324              * @param {Roo.Component} this
7325              */
7326         beforehide : true,
7327         /**
7328          * @event hide
7329          * Fires after the component is hidden.
7330              * @param {Roo.Component} this
7331              */
7332         hide : true,
7333         /**
7334          * @event beforerender
7335          * Fires before the component is rendered. Return false to stop the render.
7336              * @param {Roo.Component} this
7337              */
7338         beforerender : true,
7339         /**
7340          * @event render
7341          * Fires after the component is rendered.
7342              * @param {Roo.Component} this
7343              */
7344         render : true,
7345         /**
7346          * @event beforedestroy
7347          * Fires before the component is destroyed. Return false to stop the destroy.
7348              * @param {Roo.Component} this
7349              */
7350         beforedestroy : true,
7351         /**
7352          * @event destroy
7353          * Fires after the component is destroyed.
7354              * @param {Roo.Component} this
7355              */
7356         destroy : true
7357     });
7358     if(!this.id){
7359         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7360     }
7361     Roo.ComponentMgr.register(this);
7362     Roo.Component.superclass.constructor.call(this);
7363     this.initComponent();
7364     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7365         this.render(this.renderTo);
7366         delete this.renderTo;
7367     }
7368 };
7369
7370 // private
7371 Roo.Component.AUTO_ID = 1000;
7372
7373 Roo.extend(Roo.Component, Roo.util.Observable, {
7374     /**
7375      * @property {Boolean} hidden
7376      * true if this component is hidden. Read-only.
7377      */
7378     hidden : false,
7379     /**
7380      * true if this component is disabled. Read-only.
7381      */
7382     disabled : false,
7383     /**
7384      * true if this component has been rendered. Read-only.
7385      */
7386     rendered : false,
7387     
7388     /** @cfg {String} disableClass
7389      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7390      */
7391     disabledClass : "x-item-disabled",
7392         /** @cfg {Boolean} allowDomMove
7393          * Whether the component can move the Dom node when rendering (defaults to true).
7394          */
7395     allowDomMove : true,
7396     /** @cfg {String} hideMode
7397      * How this component should hidden. Supported values are
7398      * "visibility" (css visibility), "offsets" (negative offset position) and
7399      * "display" (css display) - defaults to "display".
7400      */
7401     hideMode: 'display',
7402
7403     // private
7404     ctype : "Roo.Component",
7405
7406     /** @cfg {String} actionMode 
7407      * which property holds the element that used for  hide() / show() / disable() / enable()
7408      * default is 'el' 
7409      */
7410     actionMode : "el",
7411
7412     // private
7413     getActionEl : function(){
7414         return this[this.actionMode];
7415     },
7416
7417     initComponent : Roo.emptyFn,
7418     /**
7419      * If this is a lazy rendering component, render it to its container element.
7420      * @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.
7421      */
7422     render : function(container, position){
7423         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7424             if(!container && this.el){
7425                 this.el = Roo.get(this.el);
7426                 container = this.el.dom.parentNode;
7427                 this.allowDomMove = false;
7428             }
7429             this.container = Roo.get(container);
7430             this.rendered = true;
7431             if(position !== undefined){
7432                 if(typeof position == 'number'){
7433                     position = this.container.dom.childNodes[position];
7434                 }else{
7435                     position = Roo.getDom(position);
7436                 }
7437             }
7438             this.onRender(this.container, position || null);
7439             if(this.cls){
7440                 this.el.addClass(this.cls);
7441                 delete this.cls;
7442             }
7443             if(this.style){
7444                 this.el.applyStyles(this.style);
7445                 delete this.style;
7446             }
7447             this.fireEvent("render", this);
7448             this.afterRender(this.container);
7449             if(this.hidden){
7450                 this.hide();
7451             }
7452             if(this.disabled){
7453                 this.disable();
7454             }
7455         }
7456         return this;
7457     },
7458
7459     // private
7460     // default function is not really useful
7461     onRender : function(ct, position){
7462         if(this.el){
7463             this.el = Roo.get(this.el);
7464             if(this.allowDomMove !== false){
7465                 ct.dom.insertBefore(this.el.dom, position);
7466             }
7467         }
7468     },
7469
7470     // private
7471     getAutoCreate : function(){
7472         var cfg = typeof this.autoCreate == "object" ?
7473                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7474         if(this.id && !cfg.id){
7475             cfg.id = this.id;
7476         }
7477         return cfg;
7478     },
7479
7480     // private
7481     afterRender : Roo.emptyFn,
7482
7483     /**
7484      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7485      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7486      */
7487     destroy : function(){
7488         if(this.fireEvent("beforedestroy", this) !== false){
7489             this.purgeListeners();
7490             this.beforeDestroy();
7491             if(this.rendered){
7492                 this.el.removeAllListeners();
7493                 this.el.remove();
7494                 if(this.actionMode == "container"){
7495                     this.container.remove();
7496                 }
7497             }
7498             this.onDestroy();
7499             Roo.ComponentMgr.unregister(this);
7500             this.fireEvent("destroy", this);
7501         }
7502     },
7503
7504         // private
7505     beforeDestroy : function(){
7506
7507     },
7508
7509         // private
7510         onDestroy : function(){
7511
7512     },
7513
7514     /**
7515      * Returns the underlying {@link Roo.Element}.
7516      * @return {Roo.Element} The element
7517      */
7518     getEl : function(){
7519         return this.el;
7520     },
7521
7522     /**
7523      * Returns the id of this component.
7524      * @return {String}
7525      */
7526     getId : function(){
7527         return this.id;
7528     },
7529
7530     /**
7531      * Try to focus this component.
7532      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7533      * @return {Roo.Component} this
7534      */
7535     focus : function(selectText){
7536         if(this.rendered){
7537             this.el.focus();
7538             if(selectText === true){
7539                 this.el.dom.select();
7540             }
7541         }
7542         return this;
7543     },
7544
7545     // private
7546     blur : function(){
7547         if(this.rendered){
7548             this.el.blur();
7549         }
7550         return this;
7551     },
7552
7553     /**
7554      * Disable this component.
7555      * @return {Roo.Component} this
7556      */
7557     disable : function(){
7558         if(this.rendered){
7559             this.onDisable();
7560         }
7561         this.disabled = true;
7562         this.fireEvent("disable", this);
7563         return this;
7564     },
7565
7566         // private
7567     onDisable : function(){
7568         this.getActionEl().addClass(this.disabledClass);
7569         this.el.dom.disabled = true;
7570     },
7571
7572     /**
7573      * Enable this component.
7574      * @return {Roo.Component} this
7575      */
7576     enable : function(){
7577         if(this.rendered){
7578             this.onEnable();
7579         }
7580         this.disabled = false;
7581         this.fireEvent("enable", this);
7582         return this;
7583     },
7584
7585         // private
7586     onEnable : function(){
7587         this.getActionEl().removeClass(this.disabledClass);
7588         this.el.dom.disabled = false;
7589     },
7590
7591     /**
7592      * Convenience function for setting disabled/enabled by boolean.
7593      * @param {Boolean} disabled
7594      */
7595     setDisabled : function(disabled){
7596         this[disabled ? "disable" : "enable"]();
7597     },
7598
7599     /**
7600      * Show this component.
7601      * @return {Roo.Component} this
7602      */
7603     show: function(){
7604         if(this.fireEvent("beforeshow", this) !== false){
7605             this.hidden = false;
7606             if(this.rendered){
7607                 this.onShow();
7608             }
7609             this.fireEvent("show", this);
7610         }
7611         return this;
7612     },
7613
7614     // private
7615     onShow : function(){
7616         var ae = this.getActionEl();
7617         if(this.hideMode == 'visibility'){
7618             ae.dom.style.visibility = "visible";
7619         }else if(this.hideMode == 'offsets'){
7620             ae.removeClass('x-hidden');
7621         }else{
7622             ae.dom.style.display = "";
7623         }
7624     },
7625
7626     /**
7627      * Hide this component.
7628      * @return {Roo.Component} this
7629      */
7630     hide: function(){
7631         if(this.fireEvent("beforehide", this) !== false){
7632             this.hidden = true;
7633             if(this.rendered){
7634                 this.onHide();
7635             }
7636             this.fireEvent("hide", this);
7637         }
7638         return this;
7639     },
7640
7641     // private
7642     onHide : function(){
7643         var ae = this.getActionEl();
7644         if(this.hideMode == 'visibility'){
7645             ae.dom.style.visibility = "hidden";
7646         }else if(this.hideMode == 'offsets'){
7647             ae.addClass('x-hidden');
7648         }else{
7649             ae.dom.style.display = "none";
7650         }
7651     },
7652
7653     /**
7654      * Convenience function to hide or show this component by boolean.
7655      * @param {Boolean} visible True to show, false to hide
7656      * @return {Roo.Component} this
7657      */
7658     setVisible: function(visible){
7659         if(visible) {
7660             this.show();
7661         }else{
7662             this.hide();
7663         }
7664         return this;
7665     },
7666
7667     /**
7668      * Returns true if this component is visible.
7669      */
7670     isVisible : function(){
7671         return this.getActionEl().isVisible();
7672     },
7673
7674     cloneConfig : function(overrides){
7675         overrides = overrides || {};
7676         var id = overrides.id || Roo.id();
7677         var cfg = Roo.applyIf(overrides, this.initialConfig);
7678         cfg.id = id; // prevent dup id
7679         return new this.constructor(cfg);
7680     }
7681 });/*
7682  * Based on:
7683  * Ext JS Library 1.1.1
7684  * Copyright(c) 2006-2007, Ext JS, LLC.
7685  *
7686  * Originally Released Under LGPL - original licence link has changed is not relivant.
7687  *
7688  * Fork - LGPL
7689  * <script type="text/javascript">
7690  */
7691  (function(){ 
7692 /**
7693  * @class Roo.Layer
7694  * @extends Roo.Element
7695  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7696  * automatic maintaining of shadow/shim positions.
7697  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7698  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7699  * you can pass a string with a CSS class name. False turns off the shadow.
7700  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7701  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7702  * @cfg {String} cls CSS class to add to the element
7703  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7704  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7705  * @constructor
7706  * @param {Object} config An object with config options.
7707  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7708  */
7709
7710 Roo.Layer = function(config, existingEl){
7711     config = config || {};
7712     var dh = Roo.DomHelper;
7713     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7714     if(existingEl){
7715         this.dom = Roo.getDom(existingEl);
7716     }
7717     if(!this.dom){
7718         var o = config.dh || {tag: "div", cls: "x-layer"};
7719         this.dom = dh.append(pel, o);
7720     }
7721     if(config.cls){
7722         this.addClass(config.cls);
7723     }
7724     this.constrain = config.constrain !== false;
7725     this.visibilityMode = Roo.Element.VISIBILITY;
7726     if(config.id){
7727         this.id = this.dom.id = config.id;
7728     }else{
7729         this.id = Roo.id(this.dom);
7730     }
7731     this.zindex = config.zindex || this.getZIndex();
7732     this.position("absolute", this.zindex);
7733     if(config.shadow){
7734         this.shadowOffset = config.shadowOffset || 4;
7735         this.shadow = new Roo.Shadow({
7736             offset : this.shadowOffset,
7737             mode : config.shadow
7738         });
7739     }else{
7740         this.shadowOffset = 0;
7741     }
7742     this.useShim = config.shim !== false && Roo.useShims;
7743     this.useDisplay = config.useDisplay;
7744     this.hide();
7745 };
7746
7747 var supr = Roo.Element.prototype;
7748
7749 // shims are shared among layer to keep from having 100 iframes
7750 var shims = [];
7751
7752 Roo.extend(Roo.Layer, Roo.Element, {
7753
7754     getZIndex : function(){
7755         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7756     },
7757
7758     getShim : function(){
7759         if(!this.useShim){
7760             return null;
7761         }
7762         if(this.shim){
7763             return this.shim;
7764         }
7765         var shim = shims.shift();
7766         if(!shim){
7767             shim = this.createShim();
7768             shim.enableDisplayMode('block');
7769             shim.dom.style.display = 'none';
7770             shim.dom.style.visibility = 'visible';
7771         }
7772         var pn = this.dom.parentNode;
7773         if(shim.dom.parentNode != pn){
7774             pn.insertBefore(shim.dom, this.dom);
7775         }
7776         shim.setStyle('z-index', this.getZIndex()-2);
7777         this.shim = shim;
7778         return shim;
7779     },
7780
7781     hideShim : function(){
7782         if(this.shim){
7783             this.shim.setDisplayed(false);
7784             shims.push(this.shim);
7785             delete this.shim;
7786         }
7787     },
7788
7789     disableShadow : function(){
7790         if(this.shadow){
7791             this.shadowDisabled = true;
7792             this.shadow.hide();
7793             this.lastShadowOffset = this.shadowOffset;
7794             this.shadowOffset = 0;
7795         }
7796     },
7797
7798     enableShadow : function(show){
7799         if(this.shadow){
7800             this.shadowDisabled = false;
7801             this.shadowOffset = this.lastShadowOffset;
7802             delete this.lastShadowOffset;
7803             if(show){
7804                 this.sync(true);
7805             }
7806         }
7807     },
7808
7809     // private
7810     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7811     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7812     sync : function(doShow){
7813         var sw = this.shadow;
7814         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7815             var sh = this.getShim();
7816
7817             var w = this.getWidth(),
7818                 h = this.getHeight();
7819
7820             var l = this.getLeft(true),
7821                 t = this.getTop(true);
7822
7823             if(sw && !this.shadowDisabled){
7824                 if(doShow && !sw.isVisible()){
7825                     sw.show(this);
7826                 }else{
7827                     sw.realign(l, t, w, h);
7828                 }
7829                 if(sh){
7830                     if(doShow){
7831                        sh.show();
7832                     }
7833                     // fit the shim behind the shadow, so it is shimmed too
7834                     var a = sw.adjusts, s = sh.dom.style;
7835                     s.left = (Math.min(l, l+a.l))+"px";
7836                     s.top = (Math.min(t, t+a.t))+"px";
7837                     s.width = (w+a.w)+"px";
7838                     s.height = (h+a.h)+"px";
7839                 }
7840             }else if(sh){
7841                 if(doShow){
7842                    sh.show();
7843                 }
7844                 sh.setSize(w, h);
7845                 sh.setLeftTop(l, t);
7846             }
7847             
7848         }
7849     },
7850
7851     // private
7852     destroy : function(){
7853         this.hideShim();
7854         if(this.shadow){
7855             this.shadow.hide();
7856         }
7857         this.removeAllListeners();
7858         var pn = this.dom.parentNode;
7859         if(pn){
7860             pn.removeChild(this.dom);
7861         }
7862         Roo.Element.uncache(this.id);
7863     },
7864
7865     remove : function(){
7866         this.destroy();
7867     },
7868
7869     // private
7870     beginUpdate : function(){
7871         this.updating = true;
7872     },
7873
7874     // private
7875     endUpdate : function(){
7876         this.updating = false;
7877         this.sync(true);
7878     },
7879
7880     // private
7881     hideUnders : function(negOffset){
7882         if(this.shadow){
7883             this.shadow.hide();
7884         }
7885         this.hideShim();
7886     },
7887
7888     // private
7889     constrainXY : function(){
7890         if(this.constrain){
7891             var vw = Roo.lib.Dom.getViewWidth(),
7892                 vh = Roo.lib.Dom.getViewHeight();
7893             var s = Roo.get(document).getScroll();
7894
7895             var xy = this.getXY();
7896             var x = xy[0], y = xy[1];   
7897             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7898             // only move it if it needs it
7899             var moved = false;
7900             // first validate right/bottom
7901             if((x + w) > vw+s.left){
7902                 x = vw - w - this.shadowOffset;
7903                 moved = true;
7904             }
7905             if((y + h) > vh+s.top){
7906                 y = vh - h - this.shadowOffset;
7907                 moved = true;
7908             }
7909             // then make sure top/left isn't negative
7910             if(x < s.left){
7911                 x = s.left;
7912                 moved = true;
7913             }
7914             if(y < s.top){
7915                 y = s.top;
7916                 moved = true;
7917             }
7918             if(moved){
7919                 if(this.avoidY){
7920                     var ay = this.avoidY;
7921                     if(y <= ay && (y+h) >= ay){
7922                         y = ay-h-5;   
7923                     }
7924                 }
7925                 xy = [x, y];
7926                 this.storeXY(xy);
7927                 supr.setXY.call(this, xy);
7928                 this.sync();
7929             }
7930         }
7931     },
7932
7933     isVisible : function(){
7934         return this.visible;    
7935     },
7936
7937     // private
7938     showAction : function(){
7939         this.visible = true; // track visibility to prevent getStyle calls
7940         if(this.useDisplay === true){
7941             this.setDisplayed("");
7942         }else if(this.lastXY){
7943             supr.setXY.call(this, this.lastXY);
7944         }else if(this.lastLT){
7945             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7946         }
7947     },
7948
7949     // private
7950     hideAction : function(){
7951         this.visible = false;
7952         if(this.useDisplay === true){
7953             this.setDisplayed(false);
7954         }else{
7955             this.setLeftTop(-10000,-10000);
7956         }
7957     },
7958
7959     // overridden Element method
7960     setVisible : function(v, a, d, c, e){
7961         if(v){
7962             this.showAction();
7963         }
7964         if(a && v){
7965             var cb = function(){
7966                 this.sync(true);
7967                 if(c){
7968                     c();
7969                 }
7970             }.createDelegate(this);
7971             supr.setVisible.call(this, true, true, d, cb, e);
7972         }else{
7973             if(!v){
7974                 this.hideUnders(true);
7975             }
7976             var cb = c;
7977             if(a){
7978                 cb = function(){
7979                     this.hideAction();
7980                     if(c){
7981                         c();
7982                     }
7983                 }.createDelegate(this);
7984             }
7985             supr.setVisible.call(this, v, a, d, cb, e);
7986             if(v){
7987                 this.sync(true);
7988             }else if(!a){
7989                 this.hideAction();
7990             }
7991         }
7992     },
7993
7994     storeXY : function(xy){
7995         delete this.lastLT;
7996         this.lastXY = xy;
7997     },
7998
7999     storeLeftTop : function(left, top){
8000         delete this.lastXY;
8001         this.lastLT = [left, top];
8002     },
8003
8004     // private
8005     beforeFx : function(){
8006         this.beforeAction();
8007         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8008     },
8009
8010     // private
8011     afterFx : function(){
8012         Roo.Layer.superclass.afterFx.apply(this, arguments);
8013         this.sync(this.isVisible());
8014     },
8015
8016     // private
8017     beforeAction : function(){
8018         if(!this.updating && this.shadow){
8019             this.shadow.hide();
8020         }
8021     },
8022
8023     // overridden Element method
8024     setLeft : function(left){
8025         this.storeLeftTop(left, this.getTop(true));
8026         supr.setLeft.apply(this, arguments);
8027         this.sync();
8028     },
8029
8030     setTop : function(top){
8031         this.storeLeftTop(this.getLeft(true), top);
8032         supr.setTop.apply(this, arguments);
8033         this.sync();
8034     },
8035
8036     setLeftTop : function(left, top){
8037         this.storeLeftTop(left, top);
8038         supr.setLeftTop.apply(this, arguments);
8039         this.sync();
8040     },
8041
8042     setXY : function(xy, a, d, c, e){
8043         this.fixDisplay();
8044         this.beforeAction();
8045         this.storeXY(xy);
8046         var cb = this.createCB(c);
8047         supr.setXY.call(this, xy, a, d, cb, e);
8048         if(!a){
8049             cb();
8050         }
8051     },
8052
8053     // private
8054     createCB : function(c){
8055         var el = this;
8056         return function(){
8057             el.constrainXY();
8058             el.sync(true);
8059             if(c){
8060                 c();
8061             }
8062         };
8063     },
8064
8065     // overridden Element method
8066     setX : function(x, a, d, c, e){
8067         this.setXY([x, this.getY()], a, d, c, e);
8068     },
8069
8070     // overridden Element method
8071     setY : function(y, a, d, c, e){
8072         this.setXY([this.getX(), y], a, d, c, e);
8073     },
8074
8075     // overridden Element method
8076     setSize : function(w, h, a, d, c, e){
8077         this.beforeAction();
8078         var cb = this.createCB(c);
8079         supr.setSize.call(this, w, h, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // overridden Element method
8086     setWidth : function(w, a, d, c, e){
8087         this.beforeAction();
8088         var cb = this.createCB(c);
8089         supr.setWidth.call(this, w, a, d, cb, e);
8090         if(!a){
8091             cb();
8092         }
8093     },
8094
8095     // overridden Element method
8096     setHeight : function(h, a, d, c, e){
8097         this.beforeAction();
8098         var cb = this.createCB(c);
8099         supr.setHeight.call(this, h, a, d, cb, e);
8100         if(!a){
8101             cb();
8102         }
8103     },
8104
8105     // overridden Element method
8106     setBounds : function(x, y, w, h, a, d, c, e){
8107         this.beforeAction();
8108         var cb = this.createCB(c);
8109         if(!a){
8110             this.storeXY([x, y]);
8111             supr.setXY.call(this, [x, y]);
8112             supr.setSize.call(this, w, h, a, d, cb, e);
8113             cb();
8114         }else{
8115             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8116         }
8117         return this;
8118     },
8119     
8120     /**
8121      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8122      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8123      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8124      * @param {Number} zindex The new z-index to set
8125      * @return {this} The Layer
8126      */
8127     setZIndex : function(zindex){
8128         this.zindex = zindex;
8129         this.setStyle("z-index", zindex + 2);
8130         if(this.shadow){
8131             this.shadow.setZIndex(zindex + 1);
8132         }
8133         if(this.shim){
8134             this.shim.setStyle("z-index", zindex);
8135         }
8136     }
8137 });
8138 })();/*
8139  * Based on:
8140  * Ext JS Library 1.1.1
8141  * Copyright(c) 2006-2007, Ext JS, LLC.
8142  *
8143  * Originally Released Under LGPL - original licence link has changed is not relivant.
8144  *
8145  * Fork - LGPL
8146  * <script type="text/javascript">
8147  */
8148
8149
8150 /**
8151  * @class Roo.Shadow
8152  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8153  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8154  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8155  * @constructor
8156  * Create a new Shadow
8157  * @param {Object} config The config object
8158  */
8159 Roo.Shadow = function(config){
8160     Roo.apply(this, config);
8161     if(typeof this.mode != "string"){
8162         this.mode = this.defaultMode;
8163     }
8164     var o = this.offset, a = {h: 0};
8165     var rad = Math.floor(this.offset/2);
8166     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8167         case "drop":
8168             a.w = 0;
8169             a.l = a.t = o;
8170             a.t -= 1;
8171             if(Roo.isIE){
8172                 a.l -= this.offset + rad;
8173                 a.t -= this.offset + rad;
8174                 a.w -= rad;
8175                 a.h -= rad;
8176                 a.t += 1;
8177             }
8178         break;
8179         case "sides":
8180             a.w = (o*2);
8181             a.l = -o;
8182             a.t = o-1;
8183             if(Roo.isIE){
8184                 a.l -= (this.offset - rad);
8185                 a.t -= this.offset + rad;
8186                 a.l += 1;
8187                 a.w -= (this.offset - rad)*2;
8188                 a.w -= rad + 1;
8189                 a.h -= 1;
8190             }
8191         break;
8192         case "frame":
8193             a.w = a.h = (o*2);
8194             a.l = a.t = -o;
8195             a.t += 1;
8196             a.h -= 2;
8197             if(Roo.isIE){
8198                 a.l -= (this.offset - rad);
8199                 a.t -= (this.offset - rad);
8200                 a.l += 1;
8201                 a.w -= (this.offset + rad + 1);
8202                 a.h -= (this.offset + rad);
8203                 a.h += 1;
8204             }
8205         break;
8206     };
8207
8208     this.adjusts = a;
8209 };
8210
8211 Roo.Shadow.prototype = {
8212     /**
8213      * @cfg {String} mode
8214      * The shadow display mode.  Supports the following options:<br />
8215      * sides: Shadow displays on both sides and bottom only<br />
8216      * frame: Shadow displays equally on all four sides<br />
8217      * drop: Traditional bottom-right drop shadow (default)
8218      */
8219     /**
8220      * @cfg {String} offset
8221      * The number of pixels to offset the shadow from the element (defaults to 4)
8222      */
8223     offset: 4,
8224
8225     // private
8226     defaultMode: "drop",
8227
8228     /**
8229      * Displays the shadow under the target element
8230      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8231      */
8232     show : function(target){
8233         target = Roo.get(target);
8234         if(!this.el){
8235             this.el = Roo.Shadow.Pool.pull();
8236             if(this.el.dom.nextSibling != target.dom){
8237                 this.el.insertBefore(target);
8238             }
8239         }
8240         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8241         if(Roo.isIE){
8242             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8243         }
8244         this.realign(
8245             target.getLeft(true),
8246             target.getTop(true),
8247             target.getWidth(),
8248             target.getHeight()
8249         );
8250         this.el.dom.style.display = "block";
8251     },
8252
8253     /**
8254      * Returns true if the shadow is visible, else false
8255      */
8256     isVisible : function(){
8257         return this.el ? true : false;  
8258     },
8259
8260     /**
8261      * Direct alignment when values are already available. Show must be called at least once before
8262      * calling this method to ensure it is initialized.
8263      * @param {Number} left The target element left position
8264      * @param {Number} top The target element top position
8265      * @param {Number} width The target element width
8266      * @param {Number} height The target element height
8267      */
8268     realign : function(l, t, w, h){
8269         if(!this.el){
8270             return;
8271         }
8272         var a = this.adjusts, d = this.el.dom, s = d.style;
8273         var iea = 0;
8274         s.left = (l+a.l)+"px";
8275         s.top = (t+a.t)+"px";
8276         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8277  
8278         if(s.width != sws || s.height != shs){
8279             s.width = sws;
8280             s.height = shs;
8281             if(!Roo.isIE){
8282                 var cn = d.childNodes;
8283                 var sww = Math.max(0, (sw-12))+"px";
8284                 cn[0].childNodes[1].style.width = sww;
8285                 cn[1].childNodes[1].style.width = sww;
8286                 cn[2].childNodes[1].style.width = sww;
8287                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8288             }
8289         }
8290     },
8291
8292     /**
8293      * Hides this shadow
8294      */
8295     hide : function(){
8296         if(this.el){
8297             this.el.dom.style.display = "none";
8298             Roo.Shadow.Pool.push(this.el);
8299             delete this.el;
8300         }
8301     },
8302
8303     /**
8304      * Adjust the z-index of this shadow
8305      * @param {Number} zindex The new z-index
8306      */
8307     setZIndex : function(z){
8308         this.zIndex = z;
8309         if(this.el){
8310             this.el.setStyle("z-index", z);
8311         }
8312     }
8313 };
8314
8315 // Private utility class that manages the internal Shadow cache
8316 Roo.Shadow.Pool = function(){
8317     var p = [];
8318     var markup = Roo.isIE ?
8319                  '<div class="x-ie-shadow"></div>' :
8320                  '<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>';
8321     return {
8322         pull : function(){
8323             var sh = p.shift();
8324             if(!sh){
8325                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8326                 sh.autoBoxAdjust = false;
8327             }
8328             return sh;
8329         },
8330
8331         push : function(sh){
8332             p.push(sh);
8333         }
8334     };
8335 }();/*
8336  * Based on:
8337  * Ext JS Library 1.1.1
8338  * Copyright(c) 2006-2007, Ext JS, LLC.
8339  *
8340  * Originally Released Under LGPL - original licence link has changed is not relivant.
8341  *
8342  * Fork - LGPL
8343  * <script type="text/javascript">
8344  */
8345
8346 /**
8347  * @class Roo.BoxComponent
8348  * @extends Roo.Component
8349  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8350  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8351  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8352  * layout containers.
8353  * @constructor
8354  * @param {Roo.Element/String/Object} config The configuration options.
8355  */
8356 Roo.BoxComponent = function(config){
8357     Roo.Component.call(this, config);
8358     this.addEvents({
8359         /**
8360          * @event resize
8361          * Fires after the component is resized.
8362              * @param {Roo.Component} this
8363              * @param {Number} adjWidth The box-adjusted width that was set
8364              * @param {Number} adjHeight The box-adjusted height that was set
8365              * @param {Number} rawWidth The width that was originally specified
8366              * @param {Number} rawHeight The height that was originally specified
8367              */
8368         resize : true,
8369         /**
8370          * @event move
8371          * Fires after the component is moved.
8372              * @param {Roo.Component} this
8373              * @param {Number} x The new x position
8374              * @param {Number} y The new y position
8375              */
8376         move : true
8377     });
8378 };
8379
8380 Roo.extend(Roo.BoxComponent, Roo.Component, {
8381     // private, set in afterRender to signify that the component has been rendered
8382     boxReady : false,
8383     // private, used to defer height settings to subclasses
8384     deferHeight: false,
8385     /** @cfg {Number} width
8386      * width (optional) size of component
8387      */
8388      /** @cfg {Number} height
8389      * height (optional) size of component
8390      */
8391      
8392     /**
8393      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8394      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8395      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8396      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8397      * @return {Roo.BoxComponent} this
8398      */
8399     setSize : function(w, h){
8400         // support for standard size objects
8401         if(typeof w == 'object'){
8402             h = w.height;
8403             w = w.width;
8404         }
8405         // not rendered
8406         if(!this.boxReady){
8407             this.width = w;
8408             this.height = h;
8409             return this;
8410         }
8411
8412         // prevent recalcs when not needed
8413         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8414             return this;
8415         }
8416         this.lastSize = {width: w, height: h};
8417
8418         var adj = this.adjustSize(w, h);
8419         var aw = adj.width, ah = adj.height;
8420         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8421             var rz = this.getResizeEl();
8422             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8423                 rz.setSize(aw, ah);
8424             }else if(!this.deferHeight && ah !== undefined){
8425                 rz.setHeight(ah);
8426             }else if(aw !== undefined){
8427                 rz.setWidth(aw);
8428             }
8429             this.onResize(aw, ah, w, h);
8430             this.fireEvent('resize', this, aw, ah, w, h);
8431         }
8432         return this;
8433     },
8434
8435     /**
8436      * Gets the current size of the component's underlying element.
8437      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8438      */
8439     getSize : function(){
8440         return this.el.getSize();
8441     },
8442
8443     /**
8444      * Gets the current XY position of the component's underlying element.
8445      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8446      * @return {Array} The XY position of the element (e.g., [100, 200])
8447      */
8448     getPosition : function(local){
8449         if(local === true){
8450             return [this.el.getLeft(true), this.el.getTop(true)];
8451         }
8452         return this.xy || this.el.getXY();
8453     },
8454
8455     /**
8456      * Gets the current box measurements of the component's underlying element.
8457      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8458      * @returns {Object} box An object in the format {x, y, width, height}
8459      */
8460     getBox : function(local){
8461         var s = this.el.getSize();
8462         if(local){
8463             s.x = this.el.getLeft(true);
8464             s.y = this.el.getTop(true);
8465         }else{
8466             var xy = this.xy || this.el.getXY();
8467             s.x = xy[0];
8468             s.y = xy[1];
8469         }
8470         return s;
8471     },
8472
8473     /**
8474      * Sets the current box measurements of the component's underlying element.
8475      * @param {Object} box An object in the format {x, y, width, height}
8476      * @returns {Roo.BoxComponent} this
8477      */
8478     updateBox : function(box){
8479         this.setSize(box.width, box.height);
8480         this.setPagePosition(box.x, box.y);
8481         return this;
8482     },
8483
8484     // protected
8485     getResizeEl : function(){
8486         return this.resizeEl || this.el;
8487     },
8488
8489     // protected
8490     getPositionEl : function(){
8491         return this.positionEl || this.el;
8492     },
8493
8494     /**
8495      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8496      * This method fires the move event.
8497      * @param {Number} left The new left
8498      * @param {Number} top The new top
8499      * @returns {Roo.BoxComponent} this
8500      */
8501     setPosition : function(x, y){
8502         this.x = x;
8503         this.y = y;
8504         if(!this.boxReady){
8505             return this;
8506         }
8507         var adj = this.adjustPosition(x, y);
8508         var ax = adj.x, ay = adj.y;
8509
8510         var el = this.getPositionEl();
8511         if(ax !== undefined || ay !== undefined){
8512             if(ax !== undefined && ay !== undefined){
8513                 el.setLeftTop(ax, ay);
8514             }else if(ax !== undefined){
8515                 el.setLeft(ax);
8516             }else if(ay !== undefined){
8517                 el.setTop(ay);
8518             }
8519             this.onPosition(ax, ay);
8520             this.fireEvent('move', this, ax, ay);
8521         }
8522         return this;
8523     },
8524
8525     /**
8526      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8527      * This method fires the move event.
8528      * @param {Number} x The new x position
8529      * @param {Number} y The new y position
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     setPagePosition : function(x, y){
8533         this.pageX = x;
8534         this.pageY = y;
8535         if(!this.boxReady){
8536             return;
8537         }
8538         if(x === undefined || y === undefined){ // cannot translate undefined points
8539             return;
8540         }
8541         var p = this.el.translatePoints(x, y);
8542         this.setPosition(p.left, p.top);
8543         return this;
8544     },
8545
8546     // private
8547     onRender : function(ct, position){
8548         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8549         if(this.resizeEl){
8550             this.resizeEl = Roo.get(this.resizeEl);
8551         }
8552         if(this.positionEl){
8553             this.positionEl = Roo.get(this.positionEl);
8554         }
8555     },
8556
8557     // private
8558     afterRender : function(){
8559         Roo.BoxComponent.superclass.afterRender.call(this);
8560         this.boxReady = true;
8561         this.setSize(this.width, this.height);
8562         if(this.x || this.y){
8563             this.setPosition(this.x, this.y);
8564         }
8565         if(this.pageX || this.pageY){
8566             this.setPagePosition(this.pageX, this.pageY);
8567         }
8568     },
8569
8570     /**
8571      * Force the component's size to recalculate based on the underlying element's current height and width.
8572      * @returns {Roo.BoxComponent} this
8573      */
8574     syncSize : function(){
8575         delete this.lastSize;
8576         this.setSize(this.el.getWidth(), this.el.getHeight());
8577         return this;
8578     },
8579
8580     /**
8581      * Called after the component is resized, this method is empty by default but can be implemented by any
8582      * subclass that needs to perform custom logic after a resize occurs.
8583      * @param {Number} adjWidth The box-adjusted width that was set
8584      * @param {Number} adjHeight The box-adjusted height that was set
8585      * @param {Number} rawWidth The width that was originally specified
8586      * @param {Number} rawHeight The height that was originally specified
8587      */
8588     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8589
8590     },
8591
8592     /**
8593      * Called after the component is moved, this method is empty by default but can be implemented by any
8594      * subclass that needs to perform custom logic after a move occurs.
8595      * @param {Number} x The new x position
8596      * @param {Number} y The new y position
8597      */
8598     onPosition : function(x, y){
8599
8600     },
8601
8602     // private
8603     adjustSize : function(w, h){
8604         if(this.autoWidth){
8605             w = 'auto';
8606         }
8607         if(this.autoHeight){
8608             h = 'auto';
8609         }
8610         return {width : w, height: h};
8611     },
8612
8613     // private
8614     adjustPosition : function(x, y){
8615         return {x : x, y: y};
8616     }
8617 });/*
8618  * Based on:
8619  * Ext JS Library 1.1.1
8620  * Copyright(c) 2006-2007, Ext JS, LLC.
8621  *
8622  * Originally Released Under LGPL - original licence link has changed is not relivant.
8623  *
8624  * Fork - LGPL
8625  * <script type="text/javascript">
8626  */
8627
8628
8629 /**
8630  * @class Roo.SplitBar
8631  * @extends Roo.util.Observable
8632  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8633  * <br><br>
8634  * Usage:
8635  * <pre><code>
8636 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8637                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8638 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8639 split.minSize = 100;
8640 split.maxSize = 600;
8641 split.animate = true;
8642 split.on('moved', splitterMoved);
8643 </code></pre>
8644  * @constructor
8645  * Create a new SplitBar
8646  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8647  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8648  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8649  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8650                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8651                         position of the SplitBar).
8652  */
8653 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8654     
8655     /** @private */
8656     this.el = Roo.get(dragElement, true);
8657     this.el.dom.unselectable = "on";
8658     /** @private */
8659     this.resizingEl = Roo.get(resizingElement, true);
8660
8661     /**
8662      * @private
8663      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8664      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8665      * @type Number
8666      */
8667     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8668     
8669     /**
8670      * The minimum size of the resizing element. (Defaults to 0)
8671      * @type Number
8672      */
8673     this.minSize = 0;
8674     
8675     /**
8676      * The maximum size of the resizing element. (Defaults to 2000)
8677      * @type Number
8678      */
8679     this.maxSize = 2000;
8680     
8681     /**
8682      * Whether to animate the transition to the new size
8683      * @type Boolean
8684      */
8685     this.animate = false;
8686     
8687     /**
8688      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8689      * @type Boolean
8690      */
8691     this.useShim = false;
8692     
8693     /** @private */
8694     this.shim = null;
8695     
8696     if(!existingProxy){
8697         /** @private */
8698         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8699     }else{
8700         this.proxy = Roo.get(existingProxy).dom;
8701     }
8702     /** @private */
8703     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8704     
8705     /** @private */
8706     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8707     
8708     /** @private */
8709     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8710     
8711     /** @private */
8712     this.dragSpecs = {};
8713     
8714     /**
8715      * @private The adapter to use to positon and resize elements
8716      */
8717     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8718     this.adapter.init(this);
8719     
8720     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8721         /** @private */
8722         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8723         this.el.addClass("x-splitbar-h");
8724     }else{
8725         /** @private */
8726         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8727         this.el.addClass("x-splitbar-v");
8728     }
8729     
8730     this.addEvents({
8731         /**
8732          * @event resize
8733          * Fires when the splitter is moved (alias for {@link #event-moved})
8734          * @param {Roo.SplitBar} this
8735          * @param {Number} newSize the new width or height
8736          */
8737         "resize" : true,
8738         /**
8739          * @event moved
8740          * Fires when the splitter is moved
8741          * @param {Roo.SplitBar} this
8742          * @param {Number} newSize the new width or height
8743          */
8744         "moved" : true,
8745         /**
8746          * @event beforeresize
8747          * Fires before the splitter is dragged
8748          * @param {Roo.SplitBar} this
8749          */
8750         "beforeresize" : true,
8751
8752         "beforeapply" : true
8753     });
8754
8755     Roo.util.Observable.call(this);
8756 };
8757
8758 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8759     onStartProxyDrag : function(x, y){
8760         this.fireEvent("beforeresize", this);
8761         if(!this.overlay){
8762             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8763             o.unselectable();
8764             o.enableDisplayMode("block");
8765             // all splitbars share the same overlay
8766             Roo.SplitBar.prototype.overlay = o;
8767         }
8768         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8769         this.overlay.show();
8770         Roo.get(this.proxy).setDisplayed("block");
8771         var size = this.adapter.getElementSize(this);
8772         this.activeMinSize = this.getMinimumSize();;
8773         this.activeMaxSize = this.getMaximumSize();;
8774         var c1 = size - this.activeMinSize;
8775         var c2 = Math.max(this.activeMaxSize - size, 0);
8776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8777             this.dd.resetConstraints();
8778             this.dd.setXConstraint(
8779                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8780                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8781             );
8782             this.dd.setYConstraint(0, 0);
8783         }else{
8784             this.dd.resetConstraints();
8785             this.dd.setXConstraint(0, 0);
8786             this.dd.setYConstraint(
8787                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8788                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8789             );
8790          }
8791         this.dragSpecs.startSize = size;
8792         this.dragSpecs.startPoint = [x, y];
8793         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8794     },
8795     
8796     /** 
8797      * @private Called after the drag operation by the DDProxy
8798      */
8799     onEndProxyDrag : function(e){
8800         Roo.get(this.proxy).setDisplayed(false);
8801         var endPoint = Roo.lib.Event.getXY(e);
8802         if(this.overlay){
8803             this.overlay.hide();
8804         }
8805         var newSize;
8806         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8807             newSize = this.dragSpecs.startSize + 
8808                 (this.placement == Roo.SplitBar.LEFT ?
8809                     endPoint[0] - this.dragSpecs.startPoint[0] :
8810                     this.dragSpecs.startPoint[0] - endPoint[0]
8811                 );
8812         }else{
8813             newSize = this.dragSpecs.startSize + 
8814                 (this.placement == Roo.SplitBar.TOP ?
8815                     endPoint[1] - this.dragSpecs.startPoint[1] :
8816                     this.dragSpecs.startPoint[1] - endPoint[1]
8817                 );
8818         }
8819         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8820         if(newSize != this.dragSpecs.startSize){
8821             if(this.fireEvent('beforeapply', this, newSize) !== false){
8822                 this.adapter.setElementSize(this, newSize);
8823                 this.fireEvent("moved", this, newSize);
8824                 this.fireEvent("resize", this, newSize);
8825             }
8826         }
8827     },
8828     
8829     /**
8830      * Get the adapter this SplitBar uses
8831      * @return The adapter object
8832      */
8833     getAdapter : function(){
8834         return this.adapter;
8835     },
8836     
8837     /**
8838      * Set the adapter this SplitBar uses
8839      * @param {Object} adapter A SplitBar adapter object
8840      */
8841     setAdapter : function(adapter){
8842         this.adapter = adapter;
8843         this.adapter.init(this);
8844     },
8845     
8846     /**
8847      * Gets the minimum size for the resizing element
8848      * @return {Number} The minimum size
8849      */
8850     getMinimumSize : function(){
8851         return this.minSize;
8852     },
8853     
8854     /**
8855      * Sets the minimum size for the resizing element
8856      * @param {Number} minSize The minimum size
8857      */
8858     setMinimumSize : function(minSize){
8859         this.minSize = minSize;
8860     },
8861     
8862     /**
8863      * Gets the maximum size for the resizing element
8864      * @return {Number} The maximum size
8865      */
8866     getMaximumSize : function(){
8867         return this.maxSize;
8868     },
8869     
8870     /**
8871      * Sets the maximum size for the resizing element
8872      * @param {Number} maxSize The maximum size
8873      */
8874     setMaximumSize : function(maxSize){
8875         this.maxSize = maxSize;
8876     },
8877     
8878     /**
8879      * Sets the initialize size for the resizing element
8880      * @param {Number} size The initial size
8881      */
8882     setCurrentSize : function(size){
8883         var oldAnimate = this.animate;
8884         this.animate = false;
8885         this.adapter.setElementSize(this, size);
8886         this.animate = oldAnimate;
8887     },
8888     
8889     /**
8890      * Destroy this splitbar. 
8891      * @param {Boolean} removeEl True to remove the element
8892      */
8893     destroy : function(removeEl){
8894         if(this.shim){
8895             this.shim.remove();
8896         }
8897         this.dd.unreg();
8898         this.proxy.parentNode.removeChild(this.proxy);
8899         if(removeEl){
8900             this.el.remove();
8901         }
8902     }
8903 });
8904
8905 /**
8906  * @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.
8907  */
8908 Roo.SplitBar.createProxy = function(dir){
8909     var proxy = new Roo.Element(document.createElement("div"));
8910     proxy.unselectable();
8911     var cls = 'x-splitbar-proxy';
8912     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8913     document.body.appendChild(proxy.dom);
8914     return proxy.dom;
8915 };
8916
8917 /** 
8918  * @class Roo.SplitBar.BasicLayoutAdapter
8919  * Default Adapter. It assumes the splitter and resizing element are not positioned
8920  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8921  */
8922 Roo.SplitBar.BasicLayoutAdapter = function(){
8923 };
8924
8925 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8926     // do nothing for now
8927     init : function(s){
8928     
8929     },
8930     /**
8931      * Called before drag operations to get the current size of the resizing element. 
8932      * @param {Roo.SplitBar} s The SplitBar using this adapter
8933      */
8934      getElementSize : function(s){
8935         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8936             return s.resizingEl.getWidth();
8937         }else{
8938             return s.resizingEl.getHeight();
8939         }
8940     },
8941     
8942     /**
8943      * Called after drag operations to set the size of the resizing element.
8944      * @param {Roo.SplitBar} s The SplitBar using this adapter
8945      * @param {Number} newSize The new size to set
8946      * @param {Function} onComplete A function to be invoked when resizing is complete
8947      */
8948     setElementSize : function(s, newSize, onComplete){
8949         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8950             if(!s.animate){
8951                 s.resizingEl.setWidth(newSize);
8952                 if(onComplete){
8953                     onComplete(s, newSize);
8954                 }
8955             }else{
8956                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8957             }
8958         }else{
8959             
8960             if(!s.animate){
8961                 s.resizingEl.setHeight(newSize);
8962                 if(onComplete){
8963                     onComplete(s, newSize);
8964                 }
8965             }else{
8966                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8967             }
8968         }
8969     }
8970 };
8971
8972 /** 
8973  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8974  * @extends Roo.SplitBar.BasicLayoutAdapter
8975  * Adapter that  moves the splitter element to align with the resized sizing element. 
8976  * Used with an absolute positioned SplitBar.
8977  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8978  * document.body, make sure you assign an id to the body element.
8979  */
8980 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8981     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8982     this.container = Roo.get(container);
8983 };
8984
8985 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8986     init : function(s){
8987         this.basic.init(s);
8988     },
8989     
8990     getElementSize : function(s){
8991         return this.basic.getElementSize(s);
8992     },
8993     
8994     setElementSize : function(s, newSize, onComplete){
8995         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8996     },
8997     
8998     moveSplitter : function(s){
8999         var yes = Roo.SplitBar;
9000         switch(s.placement){
9001             case yes.LEFT:
9002                 s.el.setX(s.resizingEl.getRight());
9003                 break;
9004             case yes.RIGHT:
9005                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9006                 break;
9007             case yes.TOP:
9008                 s.el.setY(s.resizingEl.getBottom());
9009                 break;
9010             case yes.BOTTOM:
9011                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9012                 break;
9013         }
9014     }
9015 };
9016
9017 /**
9018  * Orientation constant - Create a vertical SplitBar
9019  * @static
9020  * @type Number
9021  */
9022 Roo.SplitBar.VERTICAL = 1;
9023
9024 /**
9025  * Orientation constant - Create a horizontal SplitBar
9026  * @static
9027  * @type Number
9028  */
9029 Roo.SplitBar.HORIZONTAL = 2;
9030
9031 /**
9032  * Placement constant - The resizing element is to the left of the splitter element
9033  * @static
9034  * @type Number
9035  */
9036 Roo.SplitBar.LEFT = 1;
9037
9038 /**
9039  * Placement constant - The resizing element is to the right of the splitter element
9040  * @static
9041  * @type Number
9042  */
9043 Roo.SplitBar.RIGHT = 2;
9044
9045 /**
9046  * Placement constant - The resizing element is positioned above the splitter element
9047  * @static
9048  * @type Number
9049  */
9050 Roo.SplitBar.TOP = 3;
9051
9052 /**
9053  * Placement constant - The resizing element is positioned under splitter element
9054  * @static
9055  * @type Number
9056  */
9057 Roo.SplitBar.BOTTOM = 4;
9058 /*
9059  * Based on:
9060  * Ext JS Library 1.1.1
9061  * Copyright(c) 2006-2007, Ext JS, LLC.
9062  *
9063  * Originally Released Under LGPL - original licence link has changed is not relivant.
9064  *
9065  * Fork - LGPL
9066  * <script type="text/javascript">
9067  */
9068
9069 /**
9070  * @class Roo.View
9071  * @extends Roo.util.Observable
9072  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9073  * This class also supports single and multi selection modes. <br>
9074  * Create a data model bound view:
9075  <pre><code>
9076  var store = new Roo.data.Store(...);
9077
9078  var view = new Roo.View({
9079     el : "my-element",
9080     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9081  
9082     singleSelect: true,
9083     selectedClass: "ydataview-selected",
9084     store: store
9085  });
9086
9087  // listen for node click?
9088  view.on("click", function(vw, index, node, e){
9089  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9090  });
9091
9092  // load XML data
9093  dataModel.load("foobar.xml");
9094  </code></pre>
9095  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9096  * <br><br>
9097  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9098  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9099  * 
9100  * Note: old style constructor is still suported (container, template, config)
9101  * 
9102  * @constructor
9103  * Create a new View
9104  * @param {Object} config The config object
9105  * 
9106  */
9107 Roo.View = function(config, depreciated_tpl, depreciated_config){
9108     
9109     if (typeof(depreciated_tpl) == 'undefined') {
9110         // new way.. - universal constructor.
9111         Roo.apply(this, config);
9112         this.el  = Roo.get(this.el);
9113     } else {
9114         // old format..
9115         this.el  = Roo.get(config);
9116         this.tpl = depreciated_tpl;
9117         Roo.apply(this, depreciated_config);
9118     }
9119      
9120     
9121     if(typeof(this.tpl) == "string"){
9122         this.tpl = new Roo.Template(this.tpl);
9123     } else {
9124         // support xtype ctors..
9125         this.tpl = new Roo.factory(this.tpl, Roo);
9126     }
9127     
9128     
9129     this.tpl.compile();
9130    
9131
9132      
9133     /** @private */
9134     this.addEvents({
9135     /**
9136      * @event beforeclick
9137      * Fires before a click is processed. Returns false to cancel the default action.
9138      * @param {Roo.View} this
9139      * @param {Number} index The index of the target node
9140      * @param {HTMLElement} node The target node
9141      * @param {Roo.EventObject} e The raw event object
9142      */
9143         "beforeclick" : true,
9144     /**
9145      * @event click
9146      * Fires when a template node is clicked.
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         "click" : true,
9153     /**
9154      * @event dblclick
9155      * Fires when a template node is double 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         "dblclick" : true,
9162     /**
9163      * @event contextmenu
9164      * Fires when a template node is right 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         "contextmenu" : true,
9171     /**
9172      * @event selectionchange
9173      * Fires when the selected nodes change.
9174      * @param {Roo.View} this
9175      * @param {Array} selections Array of the selected nodes
9176      */
9177         "selectionchange" : true,
9178
9179     /**
9180      * @event beforeselect
9181      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9182      * @param {Roo.View} this
9183      * @param {HTMLElement} node The node to be selected
9184      * @param {Array} selections Array of currently selected nodes
9185      */
9186         "beforeselect" : true
9187     });
9188
9189     this.el.on({
9190         "click": this.onClick,
9191         "dblclick": this.onDblClick,
9192         "contextmenu": this.onContextMenu,
9193         scope:this
9194     });
9195
9196     this.selections = [];
9197     this.nodes = [];
9198     this.cmp = new Roo.CompositeElementLite([]);
9199     if(this.store){
9200         this.store = Roo.factory(this.store, Roo.data);
9201         this.setStore(this.store, true);
9202     }
9203     Roo.View.superclass.constructor.call(this);
9204 };
9205
9206 Roo.extend(Roo.View, Roo.util.Observable, {
9207     
9208      /**
9209      * @cfg {Roo.data.Store} store Data store to load data from.
9210      */
9211     store : false,
9212     
9213     /**
9214      * @cfg {String|Roo.Element} el The container element.
9215      */
9216     el : '',
9217     
9218     /**
9219      * @cfg {String|Roo.Template} tpl The template used by this View 
9220      */
9221     tpl : false,
9222     
9223     /**
9224      * @cfg {String} selectedClass The css class to add to selected nodes
9225      */
9226     selectedClass : "x-view-selected",
9227      /**
9228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9229      */
9230     emptyText : "",
9231     /**
9232      * @cfg {Boolean} multiSelect Allow multiple selection
9233      */
9234     
9235     multiSelect : false,
9236     /**
9237      * @cfg {Boolean} singleSelect Allow single selection
9238      */
9239     singleSelect:  false,
9240     
9241     /**
9242      * Returns the element this view is bound to.
9243      * @return {Roo.Element}
9244      */
9245     getEl : function(){
9246         return this.el;
9247     },
9248
9249     /**
9250      * Refreshes the view.
9251      */
9252     refresh : function(){
9253         var t = this.tpl;
9254         this.clearSelections();
9255         this.el.update("");
9256         var html = [];
9257         var records = this.store.getRange();
9258         if(records.length < 1){
9259             this.el.update(this.emptyText);
9260             return;
9261         }
9262         for(var i = 0, len = records.length; i < len; i++){
9263             var data = this.prepareData(records[i].data, i, records[i]);
9264             html[html.length] = t.apply(data);
9265         }
9266         this.el.update(html.join(""));
9267         this.nodes = this.el.dom.childNodes;
9268         this.updateIndexes(0);
9269     },
9270
9271     /**
9272      * Function to override to reformat the data that is sent to
9273      * the template for each node.
9274      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9275      * a JSON object for an UpdateManager bound view).
9276      */
9277     prepareData : function(data){
9278         return data;
9279     },
9280
9281     onUpdate : function(ds, record){
9282         this.clearSelections();
9283         var index = this.store.indexOf(record);
9284         var n = this.nodes[index];
9285         this.tpl.insertBefore(n, this.prepareData(record.data));
9286         n.parentNode.removeChild(n);
9287         this.updateIndexes(index, index);
9288     },
9289
9290     onAdd : function(ds, records, index){
9291         this.clearSelections();
9292         if(this.nodes.length == 0){
9293             this.refresh();
9294             return;
9295         }
9296         var n = this.nodes[index];
9297         for(var i = 0, len = records.length; i < len; i++){
9298             var d = this.prepareData(records[i].data);
9299             if(n){
9300                 this.tpl.insertBefore(n, d);
9301             }else{
9302                 this.tpl.append(this.el, d);
9303             }
9304         }
9305         this.updateIndexes(index);
9306     },
9307
9308     onRemove : function(ds, record, index){
9309         this.clearSelections();
9310         this.el.dom.removeChild(this.nodes[index]);
9311         this.updateIndexes(index);
9312     },
9313
9314     /**
9315      * Refresh an individual node.
9316      * @param {Number} index
9317      */
9318     refreshNode : function(index){
9319         this.onUpdate(this.store, this.store.getAt(index));
9320     },
9321
9322     updateIndexes : function(startIndex, endIndex){
9323         var ns = this.nodes;
9324         startIndex = startIndex || 0;
9325         endIndex = endIndex || ns.length - 1;
9326         for(var i = startIndex; i <= endIndex; i++){
9327             ns[i].nodeIndex = i;
9328         }
9329     },
9330
9331     /**
9332      * Changes the data store this view uses and refresh the view.
9333      * @param {Store} store
9334      */
9335     setStore : function(store, initial){
9336         if(!initial && this.store){
9337             this.store.un("datachanged", this.refresh);
9338             this.store.un("add", this.onAdd);
9339             this.store.un("remove", this.onRemove);
9340             this.store.un("update", this.onUpdate);
9341             this.store.un("clear", this.refresh);
9342         }
9343         if(store){
9344           
9345             store.on("datachanged", this.refresh, this);
9346             store.on("add", this.onAdd, this);
9347             store.on("remove", this.onRemove, this);
9348             store.on("update", this.onUpdate, this);
9349             store.on("clear", this.refresh, this);
9350         }
9351         
9352         if(store){
9353             this.refresh();
9354         }
9355     },
9356
9357     /**
9358      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9359      * @param {HTMLElement} node
9360      * @return {HTMLElement} The template node
9361      */
9362     findItemFromChild : function(node){
9363         var el = this.el.dom;
9364         if(!node || node.parentNode == el){
9365                     return node;
9366             }
9367             var p = node.parentNode;
9368             while(p && p != el){
9369             if(p.parentNode == el){
9370                 return p;
9371             }
9372             p = p.parentNode;
9373         }
9374             return null;
9375     },
9376
9377     /** @ignore */
9378     onClick : function(e){
9379         var item = this.findItemFromChild(e.getTarget());
9380         if(item){
9381             var index = this.indexOf(item);
9382             if(this.onItemClick(item, index, e) !== false){
9383                 this.fireEvent("click", this, index, item, e);
9384             }
9385         }else{
9386             this.clearSelections();
9387         }
9388     },
9389
9390     /** @ignore */
9391     onContextMenu : function(e){
9392         var item = this.findItemFromChild(e.getTarget());
9393         if(item){
9394             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9395         }
9396     },
9397
9398     /** @ignore */
9399     onDblClick : function(e){
9400         var item = this.findItemFromChild(e.getTarget());
9401         if(item){
9402             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9403         }
9404     },
9405
9406     onItemClick : function(item, index, e){
9407         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9408             return false;
9409         }
9410         if(this.multiSelect || this.singleSelect){
9411             if(this.multiSelect && e.shiftKey && this.lastSelection){
9412                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9413             }else{
9414                 this.select(item, this.multiSelect && e.ctrlKey);
9415                 this.lastSelection = item;
9416             }
9417             e.preventDefault();
9418         }
9419         return true;
9420     },
9421
9422     /**
9423      * Get the number of selected nodes.
9424      * @return {Number}
9425      */
9426     getSelectionCount : function(){
9427         return this.selections.length;
9428     },
9429
9430     /**
9431      * Get the currently selected nodes.
9432      * @return {Array} An array of HTMLElements
9433      */
9434     getSelectedNodes : function(){
9435         return this.selections;
9436     },
9437
9438     /**
9439      * Get the indexes of the selected nodes.
9440      * @return {Array}
9441      */
9442     getSelectedIndexes : function(){
9443         var indexes = [], s = this.selections;
9444         for(var i = 0, len = s.length; i < len; i++){
9445             indexes.push(s[i].nodeIndex);
9446         }
9447         return indexes;
9448     },
9449
9450     /**
9451      * Clear all selections
9452      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9453      */
9454     clearSelections : function(suppressEvent){
9455         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9456             this.cmp.elements = this.selections;
9457             this.cmp.removeClass(this.selectedClass);
9458             this.selections = [];
9459             if(!suppressEvent){
9460                 this.fireEvent("selectionchange", this, this.selections);
9461             }
9462         }
9463     },
9464
9465     /**
9466      * Returns true if the passed node is selected
9467      * @param {HTMLElement/Number} node The node or node index
9468      * @return {Boolean}
9469      */
9470     isSelected : function(node){
9471         var s = this.selections;
9472         if(s.length < 1){
9473             return false;
9474         }
9475         node = this.getNode(node);
9476         return s.indexOf(node) !== -1;
9477     },
9478
9479     /**
9480      * Selects nodes.
9481      * @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
9482      * @param {Boolean} keepExisting (optional) true to keep existing selections
9483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9484      */
9485     select : function(nodeInfo, keepExisting, suppressEvent){
9486         if(nodeInfo instanceof Array){
9487             if(!keepExisting){
9488                 this.clearSelections(true);
9489             }
9490             for(var i = 0, len = nodeInfo.length; i < len; i++){
9491                 this.select(nodeInfo[i], true, true);
9492             }
9493         } else{
9494             var node = this.getNode(nodeInfo);
9495             if(node && !this.isSelected(node)){
9496                 if(!keepExisting){
9497                     this.clearSelections(true);
9498                 }
9499                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9500                     Roo.fly(node).addClass(this.selectedClass);
9501                     this.selections.push(node);
9502                     if(!suppressEvent){
9503                         this.fireEvent("selectionchange", this, this.selections);
9504                     }
9505                 }
9506             }
9507         }
9508     },
9509
9510     /**
9511      * Gets a template node.
9512      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9513      * @return {HTMLElement} The node or null if it wasn't found
9514      */
9515     getNode : function(nodeInfo){
9516         if(typeof nodeInfo == "string"){
9517             return document.getElementById(nodeInfo);
9518         }else if(typeof nodeInfo == "number"){
9519             return this.nodes[nodeInfo];
9520         }
9521         return nodeInfo;
9522     },
9523
9524     /**
9525      * Gets a range template nodes.
9526      * @param {Number} startIndex
9527      * @param {Number} endIndex
9528      * @return {Array} An array of nodes
9529      */
9530     getNodes : function(start, end){
9531         var ns = this.nodes;
9532         start = start || 0;
9533         end = typeof end == "undefined" ? ns.length - 1 : end;
9534         var nodes = [];
9535         if(start <= end){
9536             for(var i = start; i <= end; i++){
9537                 nodes.push(ns[i]);
9538             }
9539         } else{
9540             for(var i = start; i >= end; i--){
9541                 nodes.push(ns[i]);
9542             }
9543         }
9544         return nodes;
9545     },
9546
9547     /**
9548      * Finds the index of the passed node
9549      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9550      * @return {Number} The index of the node or -1
9551      */
9552     indexOf : function(node){
9553         node = this.getNode(node);
9554         if(typeof node.nodeIndex == "number"){
9555             return node.nodeIndex;
9556         }
9557         var ns = this.nodes;
9558         for(var i = 0, len = ns.length; i < len; i++){
9559             if(ns[i] == node){
9560                 return i;
9561             }
9562         }
9563         return -1;
9564     }
9565 });
9566 /*
9567  * Based on:
9568  * Ext JS Library 1.1.1
9569  * Copyright(c) 2006-2007, Ext JS, LLC.
9570  *
9571  * Originally Released Under LGPL - original licence link has changed is not relivant.
9572  *
9573  * Fork - LGPL
9574  * <script type="text/javascript">
9575  */
9576
9577 /**
9578  * @class Roo.JsonView
9579  * @extends Roo.View
9580  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9581 <pre><code>
9582 var view = new Roo.JsonView({
9583     container: "my-element",
9584     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9585     multiSelect: true, 
9586     jsonRoot: "data" 
9587 });
9588
9589 // listen for node click?
9590 view.on("click", function(vw, index, node, e){
9591     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9592 });
9593
9594 // direct load of JSON data
9595 view.load("foobar.php");
9596
9597 // Example from my blog list
9598 var tpl = new Roo.Template(
9599     '&lt;div class="entry"&gt;' +
9600     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9601     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9602     "&lt;/div&gt;&lt;hr /&gt;"
9603 );
9604
9605 var moreView = new Roo.JsonView({
9606     container :  "entry-list", 
9607     template : tpl,
9608     jsonRoot: "posts"
9609 });
9610 moreView.on("beforerender", this.sortEntries, this);
9611 moreView.load({
9612     url: "/blog/get-posts.php",
9613     params: "allposts=true",
9614     text: "Loading Blog Entries..."
9615 });
9616 </code></pre>
9617
9618 * Note: old code is supported with arguments : (container, template, config)
9619
9620
9621  * @constructor
9622  * Create a new JsonView
9623  * 
9624  * @param {Object} config The config object
9625  * 
9626  */
9627 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9628     
9629     
9630     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9631
9632     var um = this.el.getUpdateManager();
9633     um.setRenderer(this);
9634     um.on("update", this.onLoad, this);
9635     um.on("failure", this.onLoadException, this);
9636
9637     /**
9638      * @event beforerender
9639      * Fires before rendering of the downloaded JSON data.
9640      * @param {Roo.JsonView} this
9641      * @param {Object} data The JSON data loaded
9642      */
9643     /**
9644      * @event load
9645      * Fires when data is loaded.
9646      * @param {Roo.JsonView} this
9647      * @param {Object} data The JSON data loaded
9648      * @param {Object} response The raw Connect response object
9649      */
9650     /**
9651      * @event loadexception
9652      * Fires when loading fails.
9653      * @param {Roo.JsonView} this
9654      * @param {Object} response The raw Connect response object
9655      */
9656     this.addEvents({
9657         'beforerender' : true,
9658         'load' : true,
9659         'loadexception' : true
9660     });
9661 };
9662 Roo.extend(Roo.JsonView, Roo.View, {
9663     /**
9664      * @type {String} The root property in the loaded JSON object that contains the data
9665      */
9666     jsonRoot : "",
9667
9668     /**
9669      * Refreshes the view.
9670      */
9671     refresh : function(){
9672         this.clearSelections();
9673         this.el.update("");
9674         var html = [];
9675         var o = this.jsonData;
9676         if(o && o.length > 0){
9677             for(var i = 0, len = o.length; i < len; i++){
9678                 var data = this.prepareData(o[i], i, o);
9679                 html[html.length] = this.tpl.apply(data);
9680             }
9681         }else{
9682             html.push(this.emptyText);
9683         }
9684         this.el.update(html.join(""));
9685         this.nodes = this.el.dom.childNodes;
9686         this.updateIndexes(0);
9687     },
9688
9689     /**
9690      * 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.
9691      * @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:
9692      <pre><code>
9693      view.load({
9694          url: "your-url.php",
9695          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9696          callback: yourFunction,
9697          scope: yourObject, //(optional scope)
9698          discardUrl: false,
9699          nocache: false,
9700          text: "Loading...",
9701          timeout: 30,
9702          scripts: false
9703      });
9704      </code></pre>
9705      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9706      * 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.
9707      * @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}
9708      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9709      * @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.
9710      */
9711     load : function(){
9712         var um = this.el.getUpdateManager();
9713         um.update.apply(um, arguments);
9714     },
9715
9716     render : function(el, response){
9717         this.clearSelections();
9718         this.el.update("");
9719         var o;
9720         try{
9721             o = Roo.util.JSON.decode(response.responseText);
9722             if(this.jsonRoot){
9723                 
9724                 o = o[this.jsonRoot];
9725             }
9726         } catch(e){
9727         }
9728         /**
9729          * The current JSON data or null
9730          */
9731         this.jsonData = o;
9732         this.beforeRender();
9733         this.refresh();
9734     },
9735
9736 /**
9737  * Get the number of records in the current JSON dataset
9738  * @return {Number}
9739  */
9740     getCount : function(){
9741         return this.jsonData ? this.jsonData.length : 0;
9742     },
9743
9744 /**
9745  * Returns the JSON object for the specified node(s)
9746  * @param {HTMLElement/Array} node The node or an array of nodes
9747  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9748  * you get the JSON object for the node
9749  */
9750     getNodeData : function(node){
9751         if(node instanceof Array){
9752             var data = [];
9753             for(var i = 0, len = node.length; i < len; i++){
9754                 data.push(this.getNodeData(node[i]));
9755             }
9756             return data;
9757         }
9758         return this.jsonData[this.indexOf(node)] || null;
9759     },
9760
9761     beforeRender : function(){
9762         this.snapshot = this.jsonData;
9763         if(this.sortInfo){
9764             this.sort.apply(this, this.sortInfo);
9765         }
9766         this.fireEvent("beforerender", this, this.jsonData);
9767     },
9768
9769     onLoad : function(el, o){
9770         this.fireEvent("load", this, this.jsonData, o);
9771     },
9772
9773     onLoadException : function(el, o){
9774         this.fireEvent("loadexception", this, o);
9775     },
9776
9777 /**
9778  * Filter the data by a specific property.
9779  * @param {String} property A property on your JSON objects
9780  * @param {String/RegExp} value Either string that the property values
9781  * should start with, or a RegExp to test against the property
9782  */
9783     filter : function(property, value){
9784         if(this.jsonData){
9785             var data = [];
9786             var ss = this.snapshot;
9787             if(typeof value == "string"){
9788                 var vlen = value.length;
9789                 if(vlen == 0){
9790                     this.clearFilter();
9791                     return;
9792                 }
9793                 value = value.toLowerCase();
9794                 for(var i = 0, len = ss.length; i < len; i++){
9795                     var o = ss[i];
9796                     if(o[property].substr(0, vlen).toLowerCase() == value){
9797                         data.push(o);
9798                     }
9799                 }
9800             } else if(value.exec){ // regex?
9801                 for(var i = 0, len = ss.length; i < len; i++){
9802                     var o = ss[i];
9803                     if(value.test(o[property])){
9804                         data.push(o);
9805                     }
9806                 }
9807             } else{
9808                 return;
9809             }
9810             this.jsonData = data;
9811             this.refresh();
9812         }
9813     },
9814
9815 /**
9816  * Filter by a function. The passed function will be called with each
9817  * object in the current dataset. If the function returns true the value is kept,
9818  * otherwise it is filtered.
9819  * @param {Function} fn
9820  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9821  */
9822     filterBy : function(fn, scope){
9823         if(this.jsonData){
9824             var data = [];
9825             var ss = this.snapshot;
9826             for(var i = 0, len = ss.length; i < len; i++){
9827                 var o = ss[i];
9828                 if(fn.call(scope || this, o)){
9829                     data.push(o);
9830                 }
9831             }
9832             this.jsonData = data;
9833             this.refresh();
9834         }
9835     },
9836
9837 /**
9838  * Clears the current filter.
9839  */
9840     clearFilter : function(){
9841         if(this.snapshot && this.jsonData != this.snapshot){
9842             this.jsonData = this.snapshot;
9843             this.refresh();
9844         }
9845     },
9846
9847
9848 /**
9849  * Sorts the data for this view and refreshes it.
9850  * @param {String} property A property on your JSON objects to sort on
9851  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9852  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9853  */
9854     sort : function(property, dir, sortType){
9855         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9856         if(this.jsonData){
9857             var p = property;
9858             var dsc = dir && dir.toLowerCase() == "desc";
9859             var f = function(o1, o2){
9860                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9861                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9862                 ;
9863                 if(v1 < v2){
9864                     return dsc ? +1 : -1;
9865                 } else if(v1 > v2){
9866                     return dsc ? -1 : +1;
9867                 } else{
9868                     return 0;
9869                 }
9870             };
9871             this.jsonData.sort(f);
9872             this.refresh();
9873             if(this.jsonData != this.snapshot){
9874                 this.snapshot.sort(f);
9875             }
9876         }
9877     }
9878 });/*
9879  * Based on:
9880  * Ext JS Library 1.1.1
9881  * Copyright(c) 2006-2007, Ext JS, LLC.
9882  *
9883  * Originally Released Under LGPL - original licence link has changed is not relivant.
9884  *
9885  * Fork - LGPL
9886  * <script type="text/javascript">
9887  */
9888  
9889
9890 /**
9891  * @class Roo.ColorPalette
9892  * @extends Roo.Component
9893  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9894  * Here's an example of typical usage:
9895  * <pre><code>
9896 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9897 cp.render('my-div');
9898
9899 cp.on('select', function(palette, selColor){
9900     // do something with selColor
9901 });
9902 </code></pre>
9903  * @constructor
9904  * Create a new ColorPalette
9905  * @param {Object} config The config object
9906  */
9907 Roo.ColorPalette = function(config){
9908     Roo.ColorPalette.superclass.constructor.call(this, config);
9909     this.addEvents({
9910         /**
9911              * @event select
9912              * Fires when a color is selected
9913              * @param {ColorPalette} this
9914              * @param {String} color The 6-digit color hex code (without the # symbol)
9915              */
9916         select: true
9917     });
9918
9919     if(this.handler){
9920         this.on("select", this.handler, this.scope, true);
9921     }
9922 };
9923 Roo.extend(Roo.ColorPalette, Roo.Component, {
9924     /**
9925      * @cfg {String} itemCls
9926      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9927      */
9928     itemCls : "x-color-palette",
9929     /**
9930      * @cfg {String} value
9931      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9932      * the hex codes are case-sensitive.
9933      */
9934     value : null,
9935     clickEvent:'click',
9936     // private
9937     ctype: "Roo.ColorPalette",
9938
9939     /**
9940      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9941      */
9942     allowReselect : false,
9943
9944     /**
9945      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9946      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9947      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9948      * of colors with the width setting until the box is symmetrical.</p>
9949      * <p>You can override individual colors if needed:</p>
9950      * <pre><code>
9951 var cp = new Roo.ColorPalette();
9952 cp.colors[0] = "FF0000";  // change the first box to red
9953 </code></pre>
9954
9955 Or you can provide a custom array of your own for complete control:
9956 <pre><code>
9957 var cp = new Roo.ColorPalette();
9958 cp.colors = ["000000", "993300", "333300"];
9959 </code></pre>
9960      * @type Array
9961      */
9962     colors : [
9963         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9964         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9965         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9966         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9967         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9968     ],
9969
9970     // private
9971     onRender : function(container, position){
9972         var t = new Roo.MasterTemplate(
9973             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9974         );
9975         var c = this.colors;
9976         for(var i = 0, len = c.length; i < len; i++){
9977             t.add([c[i]]);
9978         }
9979         var el = document.createElement("div");
9980         el.className = this.itemCls;
9981         t.overwrite(el);
9982         container.dom.insertBefore(el, position);
9983         this.el = Roo.get(el);
9984         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9985         if(this.clickEvent != 'click'){
9986             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9987         }
9988     },
9989
9990     // private
9991     afterRender : function(){
9992         Roo.ColorPalette.superclass.afterRender.call(this);
9993         if(this.value){
9994             var s = this.value;
9995             this.value = null;
9996             this.select(s);
9997         }
9998     },
9999
10000     // private
10001     handleClick : function(e, t){
10002         e.preventDefault();
10003         if(!this.disabled){
10004             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10005             this.select(c.toUpperCase());
10006         }
10007     },
10008
10009     /**
10010      * Selects the specified color in the palette (fires the select event)
10011      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10012      */
10013     select : function(color){
10014         color = color.replace("#", "");
10015         if(color != this.value || this.allowReselect){
10016             var el = this.el;
10017             if(this.value){
10018                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10019             }
10020             el.child("a.color-"+color).addClass("x-color-palette-sel");
10021             this.value = color;
10022             this.fireEvent("select", this, color);
10023         }
10024     }
10025 });/*
10026  * Based on:
10027  * Ext JS Library 1.1.1
10028  * Copyright(c) 2006-2007, Ext JS, LLC.
10029  *
10030  * Originally Released Under LGPL - original licence link has changed is not relivant.
10031  *
10032  * Fork - LGPL
10033  * <script type="text/javascript">
10034  */
10035  
10036 /**
10037  * @class Roo.DatePicker
10038  * @extends Roo.Component
10039  * Simple date picker class.
10040  * @constructor
10041  * Create a new DatePicker
10042  * @param {Object} config The config object
10043  */
10044 Roo.DatePicker = function(config){
10045     Roo.DatePicker.superclass.constructor.call(this, config);
10046
10047     this.value = config && config.value ?
10048                  config.value.clearTime() : new Date().clearTime();
10049
10050     this.addEvents({
10051         /**
10052              * @event select
10053              * Fires when a date is selected
10054              * @param {DatePicker} this
10055              * @param {Date} date The selected date
10056              */
10057         select: true
10058     });
10059
10060     if(this.handler){
10061         this.on("select", this.handler,  this.scope || this);
10062     }
10063     // build the disabledDatesRE
10064     if(!this.disabledDatesRE && this.disabledDates){
10065         var dd = this.disabledDates;
10066         var re = "(?:";
10067         for(var i = 0; i < dd.length; i++){
10068             re += dd[i];
10069             if(i != dd.length-1) re += "|";
10070         }
10071         this.disabledDatesRE = new RegExp(re + ")");
10072     }
10073 };
10074
10075 Roo.extend(Roo.DatePicker, Roo.Component, {
10076     /**
10077      * @cfg {String} todayText
10078      * The text to display on the button that selects the current date (defaults to "Today")
10079      */
10080     todayText : "Today",
10081     /**
10082      * @cfg {String} okText
10083      * The text to display on the ok button
10084      */
10085     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10086     /**
10087      * @cfg {String} cancelText
10088      * The text to display on the cancel button
10089      */
10090     cancelText : "Cancel",
10091     /**
10092      * @cfg {String} todayTip
10093      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10094      */
10095     todayTip : "{0} (Spacebar)",
10096     /**
10097      * @cfg {Date} minDate
10098      * Minimum allowable date (JavaScript date object, defaults to null)
10099      */
10100     minDate : null,
10101     /**
10102      * @cfg {Date} maxDate
10103      * Maximum allowable date (JavaScript date object, defaults to null)
10104      */
10105     maxDate : null,
10106     /**
10107      * @cfg {String} minText
10108      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10109      */
10110     minText : "This date is before the minimum date",
10111     /**
10112      * @cfg {String} maxText
10113      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10114      */
10115     maxText : "This date is after the maximum date",
10116     /**
10117      * @cfg {String} format
10118      * The default date format string which can be overriden for localization support.  The format must be
10119      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10120      */
10121     format : "m/d/y",
10122     /**
10123      * @cfg {Array} disabledDays
10124      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10125      */
10126     disabledDays : null,
10127     /**
10128      * @cfg {String} disabledDaysText
10129      * The tooltip to display when the date falls on a disabled day (defaults to "")
10130      */
10131     disabledDaysText : "",
10132     /**
10133      * @cfg {RegExp} disabledDatesRE
10134      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10135      */
10136     disabledDatesRE : null,
10137     /**
10138      * @cfg {String} disabledDatesText
10139      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10140      */
10141     disabledDatesText : "",
10142     /**
10143      * @cfg {Boolean} constrainToViewport
10144      * True to constrain the date picker to the viewport (defaults to true)
10145      */
10146     constrainToViewport : true,
10147     /**
10148      * @cfg {Array} monthNames
10149      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10150      */
10151     monthNames : Date.monthNames,
10152     /**
10153      * @cfg {Array} dayNames
10154      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10155      */
10156     dayNames : Date.dayNames,
10157     /**
10158      * @cfg {String} nextText
10159      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10160      */
10161     nextText: 'Next Month (Control+Right)',
10162     /**
10163      * @cfg {String} prevText
10164      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10165      */
10166     prevText: 'Previous Month (Control+Left)',
10167     /**
10168      * @cfg {String} monthYearText
10169      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10170      */
10171     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10172     /**
10173      * @cfg {Number} startDay
10174      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10175      */
10176     startDay : 0,
10177     /**
10178      * @cfg {Bool} showClear
10179      * Show a clear button (usefull for date form elements that can be blank.)
10180      */
10181     
10182     showClear: false,
10183     
10184     /**
10185      * Sets the value of the date field
10186      * @param {Date} value The date to set
10187      */
10188     setValue : function(value){
10189         var old = this.value;
10190         this.value = value.clearTime(true);
10191         if(this.el){
10192             this.update(this.value);
10193         }
10194     },
10195
10196     /**
10197      * Gets the current selected value of the date field
10198      * @return {Date} The selected date
10199      */
10200     getValue : function(){
10201         return this.value;
10202     },
10203
10204     // private
10205     focus : function(){
10206         if(this.el){
10207             this.update(this.activeDate);
10208         }
10209     },
10210
10211     // private
10212     onRender : function(container, position){
10213         var m = [
10214              '<table cellspacing="0">',
10215                 '<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>',
10216                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10217         var dn = this.dayNames;
10218         for(var i = 0; i < 7; i++){
10219             var d = this.startDay+i;
10220             if(d > 6){
10221                 d = d-7;
10222             }
10223             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10224         }
10225         m[m.length] = "</tr></thead><tbody><tr>";
10226         for(var i = 0; i < 42; i++) {
10227             if(i % 7 == 0 && i != 0){
10228                 m[m.length] = "</tr><tr>";
10229             }
10230             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10231         }
10232         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10233             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10234
10235         var el = document.createElement("div");
10236         el.className = "x-date-picker";
10237         el.innerHTML = m.join("");
10238
10239         container.dom.insertBefore(el, position);
10240
10241         this.el = Roo.get(el);
10242         this.eventEl = Roo.get(el.firstChild);
10243
10244         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10245             handler: this.showPrevMonth,
10246             scope: this,
10247             preventDefault:true,
10248             stopDefault:true
10249         });
10250
10251         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10252             handler: this.showNextMonth,
10253             scope: this,
10254             preventDefault:true,
10255             stopDefault:true
10256         });
10257
10258         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10259
10260         this.monthPicker = this.el.down('div.x-date-mp');
10261         this.monthPicker.enableDisplayMode('block');
10262         
10263         var kn = new Roo.KeyNav(this.eventEl, {
10264             "left" : function(e){
10265                 e.ctrlKey ?
10266                     this.showPrevMonth() :
10267                     this.update(this.activeDate.add("d", -1));
10268             },
10269
10270             "right" : function(e){
10271                 e.ctrlKey ?
10272                     this.showNextMonth() :
10273                     this.update(this.activeDate.add("d", 1));
10274             },
10275
10276             "up" : function(e){
10277                 e.ctrlKey ?
10278                     this.showNextYear() :
10279                     this.update(this.activeDate.add("d", -7));
10280             },
10281
10282             "down" : function(e){
10283                 e.ctrlKey ?
10284                     this.showPrevYear() :
10285                     this.update(this.activeDate.add("d", 7));
10286             },
10287
10288             "pageUp" : function(e){
10289                 this.showNextMonth();
10290             },
10291
10292             "pageDown" : function(e){
10293                 this.showPrevMonth();
10294             },
10295
10296             "enter" : function(e){
10297                 e.stopPropagation();
10298                 return true;
10299             },
10300
10301             scope : this
10302         });
10303
10304         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10305
10306         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10307
10308         this.el.unselectable();
10309         
10310         this.cells = this.el.select("table.x-date-inner tbody td");
10311         this.textNodes = this.el.query("table.x-date-inner tbody span");
10312
10313         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10314             text: "&#160;",
10315             tooltip: this.monthYearText
10316         });
10317
10318         this.mbtn.on('click', this.showMonthPicker, this);
10319         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10320
10321
10322         var today = (new Date()).dateFormat(this.format);
10323         
10324         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10325         if (this.showClear) {
10326             baseTb.add( new Roo.Toolbar.Fill());
10327         }
10328         baseTb.add({
10329             text: String.format(this.todayText, today),
10330             tooltip: String.format(this.todayTip, today),
10331             handler: this.selectToday,
10332             scope: this
10333         });
10334         
10335         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10336             
10337         //});
10338         if (this.showClear) {
10339             
10340             baseTb.add( new Roo.Toolbar.Fill());
10341             baseTb.add({
10342                 text: '&#160;',
10343                 cls: 'x-btn-icon x-btn-clear',
10344                 handler: function() {
10345                     //this.value = '';
10346                     this.fireEvent("select", this, '');
10347                 },
10348                 scope: this
10349             });
10350         }
10351         
10352         
10353         if(Roo.isIE){
10354             this.el.repaint();
10355         }
10356         this.update(this.value);
10357     },
10358
10359     createMonthPicker : function(){
10360         if(!this.monthPicker.dom.firstChild){
10361             var buf = ['<table border="0" cellspacing="0">'];
10362             for(var i = 0; i < 6; i++){
10363                 buf.push(
10364                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10365                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10366                     i == 0 ?
10367                     '<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>' :
10368                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10369                 );
10370             }
10371             buf.push(
10372                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10373                     this.okText,
10374                     '</button><button type="button" class="x-date-mp-cancel">',
10375                     this.cancelText,
10376                     '</button></td></tr>',
10377                 '</table>'
10378             );
10379             this.monthPicker.update(buf.join(''));
10380             this.monthPicker.on('click', this.onMonthClick, this);
10381             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10382
10383             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10384             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10385
10386             this.mpMonths.each(function(m, a, i){
10387                 i += 1;
10388                 if((i%2) == 0){
10389                     m.dom.xmonth = 5 + Math.round(i * .5);
10390                 }else{
10391                     m.dom.xmonth = Math.round((i-1) * .5);
10392                 }
10393             });
10394         }
10395     },
10396
10397     showMonthPicker : function(){
10398         this.createMonthPicker();
10399         var size = this.el.getSize();
10400         this.monthPicker.setSize(size);
10401         this.monthPicker.child('table').setSize(size);
10402
10403         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10404         this.updateMPMonth(this.mpSelMonth);
10405         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10406         this.updateMPYear(this.mpSelYear);
10407
10408         this.monthPicker.slideIn('t', {duration:.2});
10409     },
10410
10411     updateMPYear : function(y){
10412         this.mpyear = y;
10413         var ys = this.mpYears.elements;
10414         for(var i = 1; i <= 10; i++){
10415             var td = ys[i-1], y2;
10416             if((i%2) == 0){
10417                 y2 = y + Math.round(i * .5);
10418                 td.firstChild.innerHTML = y2;
10419                 td.xyear = y2;
10420             }else{
10421                 y2 = y - (5-Math.round(i * .5));
10422                 td.firstChild.innerHTML = y2;
10423                 td.xyear = y2;
10424             }
10425             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10426         }
10427     },
10428
10429     updateMPMonth : function(sm){
10430         this.mpMonths.each(function(m, a, i){
10431             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10432         });
10433     },
10434
10435     selectMPMonth: function(m){
10436         
10437     },
10438
10439     onMonthClick : function(e, t){
10440         e.stopEvent();
10441         var el = new Roo.Element(t), pn;
10442         if(el.is('button.x-date-mp-cancel')){
10443             this.hideMonthPicker();
10444         }
10445         else if(el.is('button.x-date-mp-ok')){
10446             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10447             this.hideMonthPicker();
10448         }
10449         else if(pn = el.up('td.x-date-mp-month', 2)){
10450             this.mpMonths.removeClass('x-date-mp-sel');
10451             pn.addClass('x-date-mp-sel');
10452             this.mpSelMonth = pn.dom.xmonth;
10453         }
10454         else if(pn = el.up('td.x-date-mp-year', 2)){
10455             this.mpYears.removeClass('x-date-mp-sel');
10456             pn.addClass('x-date-mp-sel');
10457             this.mpSelYear = pn.dom.xyear;
10458         }
10459         else if(el.is('a.x-date-mp-prev')){
10460             this.updateMPYear(this.mpyear-10);
10461         }
10462         else if(el.is('a.x-date-mp-next')){
10463             this.updateMPYear(this.mpyear+10);
10464         }
10465     },
10466
10467     onMonthDblClick : function(e, t){
10468         e.stopEvent();
10469         var el = new Roo.Element(t), pn;
10470         if(pn = el.up('td.x-date-mp-month', 2)){
10471             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10472             this.hideMonthPicker();
10473         }
10474         else if(pn = el.up('td.x-date-mp-year', 2)){
10475             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10476             this.hideMonthPicker();
10477         }
10478     },
10479
10480     hideMonthPicker : function(disableAnim){
10481         if(this.monthPicker){
10482             if(disableAnim === true){
10483                 this.monthPicker.hide();
10484             }else{
10485                 this.monthPicker.slideOut('t', {duration:.2});
10486             }
10487         }
10488     },
10489
10490     // private
10491     showPrevMonth : function(e){
10492         this.update(this.activeDate.add("mo", -1));
10493     },
10494
10495     // private
10496     showNextMonth : function(e){
10497         this.update(this.activeDate.add("mo", 1));
10498     },
10499
10500     // private
10501     showPrevYear : function(){
10502         this.update(this.activeDate.add("y", -1));
10503     },
10504
10505     // private
10506     showNextYear : function(){
10507         this.update(this.activeDate.add("y", 1));
10508     },
10509
10510     // private
10511     handleMouseWheel : function(e){
10512         var delta = e.getWheelDelta();
10513         if(delta > 0){
10514             this.showPrevMonth();
10515             e.stopEvent();
10516         } else if(delta < 0){
10517             this.showNextMonth();
10518             e.stopEvent();
10519         }
10520     },
10521
10522     // private
10523     handleDateClick : function(e, t){
10524         e.stopEvent();
10525         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10526             this.setValue(new Date(t.dateValue));
10527             this.fireEvent("select", this, this.value);
10528         }
10529     },
10530
10531     // private
10532     selectToday : function(){
10533         this.setValue(new Date().clearTime());
10534         this.fireEvent("select", this, this.value);
10535     },
10536
10537     // private
10538     update : function(date){
10539         var vd = this.activeDate;
10540         this.activeDate = date;
10541         if(vd && this.el){
10542             var t = date.getTime();
10543             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10544                 this.cells.removeClass("x-date-selected");
10545                 this.cells.each(function(c){
10546                    if(c.dom.firstChild.dateValue == t){
10547                        c.addClass("x-date-selected");
10548                        setTimeout(function(){
10549                             try{c.dom.firstChild.focus();}catch(e){}
10550                        }, 50);
10551                        return false;
10552                    }
10553                 });
10554                 return;
10555             }
10556         }
10557         var days = date.getDaysInMonth();
10558         var firstOfMonth = date.getFirstDateOfMonth();
10559         var startingPos = firstOfMonth.getDay()-this.startDay;
10560
10561         if(startingPos <= this.startDay){
10562             startingPos += 7;
10563         }
10564
10565         var pm = date.add("mo", -1);
10566         var prevStart = pm.getDaysInMonth()-startingPos;
10567
10568         var cells = this.cells.elements;
10569         var textEls = this.textNodes;
10570         days += startingPos;
10571
10572         // convert everything to numbers so it's fast
10573         var day = 86400000;
10574         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10575         var today = new Date().clearTime().getTime();
10576         var sel = date.clearTime().getTime();
10577         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10578         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10579         var ddMatch = this.disabledDatesRE;
10580         var ddText = this.disabledDatesText;
10581         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10582         var ddaysText = this.disabledDaysText;
10583         var format = this.format;
10584
10585         var setCellClass = function(cal, cell){
10586             cell.title = "";
10587             var t = d.getTime();
10588             cell.firstChild.dateValue = t;
10589             if(t == today){
10590                 cell.className += " x-date-today";
10591                 cell.title = cal.todayText;
10592             }
10593             if(t == sel){
10594                 cell.className += " x-date-selected";
10595                 setTimeout(function(){
10596                     try{cell.firstChild.focus();}catch(e){}
10597                 }, 50);
10598             }
10599             // disabling
10600             if(t < min) {
10601                 cell.className = " x-date-disabled";
10602                 cell.title = cal.minText;
10603                 return;
10604             }
10605             if(t > max) {
10606                 cell.className = " x-date-disabled";
10607                 cell.title = cal.maxText;
10608                 return;
10609             }
10610             if(ddays){
10611                 if(ddays.indexOf(d.getDay()) != -1){
10612                     cell.title = ddaysText;
10613                     cell.className = " x-date-disabled";
10614                 }
10615             }
10616             if(ddMatch && format){
10617                 var fvalue = d.dateFormat(format);
10618                 if(ddMatch.test(fvalue)){
10619                     cell.title = ddText.replace("%0", fvalue);
10620                     cell.className = " x-date-disabled";
10621                 }
10622             }
10623         };
10624
10625         var i = 0;
10626         for(; i < startingPos; i++) {
10627             textEls[i].innerHTML = (++prevStart);
10628             d.setDate(d.getDate()+1);
10629             cells[i].className = "x-date-prevday";
10630             setCellClass(this, cells[i]);
10631         }
10632         for(; i < days; i++){
10633             intDay = i - startingPos + 1;
10634             textEls[i].innerHTML = (intDay);
10635             d.setDate(d.getDate()+1);
10636             cells[i].className = "x-date-active";
10637             setCellClass(this, cells[i]);
10638         }
10639         var extraDays = 0;
10640         for(; i < 42; i++) {
10641              textEls[i].innerHTML = (++extraDays);
10642              d.setDate(d.getDate()+1);
10643              cells[i].className = "x-date-nextday";
10644              setCellClass(this, cells[i]);
10645         }
10646
10647         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10648
10649         if(!this.internalRender){
10650             var main = this.el.dom.firstChild;
10651             var w = main.offsetWidth;
10652             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10653             Roo.fly(main).setWidth(w);
10654             this.internalRender = true;
10655             // opera does not respect the auto grow header center column
10656             // then, after it gets a width opera refuses to recalculate
10657             // without a second pass
10658             if(Roo.isOpera && !this.secondPass){
10659                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10660                 this.secondPass = true;
10661                 this.update.defer(10, this, [date]);
10662             }
10663         }
10664     }
10665 });/*
10666  * Based on:
10667  * Ext JS Library 1.1.1
10668  * Copyright(c) 2006-2007, Ext JS, LLC.
10669  *
10670  * Originally Released Under LGPL - original licence link has changed is not relivant.
10671  *
10672  * Fork - LGPL
10673  * <script type="text/javascript">
10674  */
10675 /**
10676  * @class Roo.TabPanel
10677  * @extends Roo.util.Observable
10678  * A lightweight tab container.
10679  * <br><br>
10680  * Usage:
10681  * <pre><code>
10682 // basic tabs 1, built from existing content
10683 var tabs = new Roo.TabPanel("tabs1");
10684 tabs.addTab("script", "View Script");
10685 tabs.addTab("markup", "View Markup");
10686 tabs.activate("script");
10687
10688 // more advanced tabs, built from javascript
10689 var jtabs = new Roo.TabPanel("jtabs");
10690 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10691
10692 // set up the UpdateManager
10693 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10694 var updater = tab2.getUpdateManager();
10695 updater.setDefaultUrl("ajax1.htm");
10696 tab2.on('activate', updater.refresh, updater, true);
10697
10698 // Use setUrl for Ajax loading
10699 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10700 tab3.setUrl("ajax2.htm", null, true);
10701
10702 // Disabled tab
10703 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10704 tab4.disable();
10705
10706 jtabs.activate("jtabs-1");
10707  * </code></pre>
10708  * @constructor
10709  * Create a new TabPanel.
10710  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10711  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10712  */
10713 Roo.TabPanel = function(container, config){
10714     /**
10715     * The container element for this TabPanel.
10716     * @type Roo.Element
10717     */
10718     this.el = Roo.get(container, true);
10719     if(config){
10720         if(typeof config == "boolean"){
10721             this.tabPosition = config ? "bottom" : "top";
10722         }else{
10723             Roo.apply(this, config);
10724         }
10725     }
10726     if(this.tabPosition == "bottom"){
10727         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10728         this.el.addClass("x-tabs-bottom");
10729     }
10730     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10731     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10732     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10733     if(Roo.isIE){
10734         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10735     }
10736     if(this.tabPosition != "bottom"){
10737     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10738      * @type Roo.Element
10739      */
10740       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10741       this.el.addClass("x-tabs-top");
10742     }
10743     this.items = [];
10744
10745     this.bodyEl.setStyle("position", "relative");
10746
10747     this.active = null;
10748     this.activateDelegate = this.activate.createDelegate(this);
10749
10750     this.addEvents({
10751         /**
10752          * @event tabchange
10753          * Fires when the active tab changes
10754          * @param {Roo.TabPanel} this
10755          * @param {Roo.TabPanelItem} activePanel The new active tab
10756          */
10757         "tabchange": true,
10758         /**
10759          * @event beforetabchange
10760          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10761          * @param {Roo.TabPanel} this
10762          * @param {Object} e Set cancel to true on this object to cancel the tab change
10763          * @param {Roo.TabPanelItem} tab The tab being changed to
10764          */
10765         "beforetabchange" : true
10766     });
10767
10768     Roo.EventManager.onWindowResize(this.onResize, this);
10769     this.cpad = this.el.getPadding("lr");
10770     this.hiddenCount = 0;
10771
10772     Roo.TabPanel.superclass.constructor.call(this);
10773 };
10774
10775 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10776         /*
10777          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10778          */
10779     tabPosition : "top",
10780         /*
10781          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10782          */
10783     currentTabWidth : 0,
10784         /*
10785          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10786          */
10787     minTabWidth : 40,
10788         /*
10789          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10790          */
10791     maxTabWidth : 250,
10792         /*
10793          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10794          */
10795     preferredTabWidth : 175,
10796         /*
10797          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10798          */
10799     resizeTabs : false,
10800         /*
10801          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10802          */
10803     monitorResize : true,
10804
10805     /**
10806      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10807      * @param {String} id The id of the div to use <b>or create</b>
10808      * @param {String} text The text for the tab
10809      * @param {String} content (optional) Content to put in the TabPanelItem body
10810      * @param {Boolean} closable (optional) True to create a close icon on the tab
10811      * @return {Roo.TabPanelItem} The created TabPanelItem
10812      */
10813     addTab : function(id, text, content, closable){
10814         var item = new Roo.TabPanelItem(this, id, text, closable);
10815         this.addTabItem(item);
10816         if(content){
10817             item.setContent(content);
10818         }
10819         return item;
10820     },
10821
10822     /**
10823      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10824      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10825      * @return {Roo.TabPanelItem}
10826      */
10827     getTab : function(id){
10828         return this.items[id];
10829     },
10830
10831     /**
10832      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10834      */
10835     hideTab : function(id){
10836         var t = this.items[id];
10837         if(!t.isHidden()){
10838            t.setHidden(true);
10839            this.hiddenCount++;
10840            this.autoSizeTabs();
10841         }
10842     },
10843
10844     /**
10845      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10846      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10847      */
10848     unhideTab : function(id){
10849         var t = this.items[id];
10850         if(t.isHidden()){
10851            t.setHidden(false);
10852            this.hiddenCount--;
10853            this.autoSizeTabs();
10854         }
10855     },
10856
10857     /**
10858      * Adds an existing {@link Roo.TabPanelItem}.
10859      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10860      */
10861     addTabItem : function(item){
10862         this.items[item.id] = item;
10863         this.items.push(item);
10864         if(this.resizeTabs){
10865            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10866            this.autoSizeTabs();
10867         }else{
10868             item.autoSize();
10869         }
10870     },
10871
10872     /**
10873      * Removes a {@link Roo.TabPanelItem}.
10874      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10875      */
10876     removeTab : function(id){
10877         var items = this.items;
10878         var tab = items[id];
10879         if(!tab) { return; }
10880         var index = items.indexOf(tab);
10881         if(this.active == tab && items.length > 1){
10882             var newTab = this.getNextAvailable(index);
10883             if(newTab) {
10884                 newTab.activate();
10885             }
10886         }
10887         this.stripEl.dom.removeChild(tab.pnode.dom);
10888         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10889             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10890         }
10891         items.splice(index, 1);
10892         delete this.items[tab.id];
10893         tab.fireEvent("close", tab);
10894         tab.purgeListeners();
10895         this.autoSizeTabs();
10896     },
10897
10898     getNextAvailable : function(start){
10899         var items = this.items;
10900         var index = start;
10901         // look for a next tab that will slide over to
10902         // replace the one being removed
10903         while(index < items.length){
10904             var item = items[++index];
10905             if(item && !item.isHidden()){
10906                 return item;
10907             }
10908         }
10909         // if one isn't found select the previous tab (on the left)
10910         index = start;
10911         while(index >= 0){
10912             var item = items[--index];
10913             if(item && !item.isHidden()){
10914                 return item;
10915             }
10916         }
10917         return null;
10918     },
10919
10920     /**
10921      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10922      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10923      */
10924     disableTab : function(id){
10925         var tab = this.items[id];
10926         if(tab && this.active != tab){
10927             tab.disable();
10928         }
10929     },
10930
10931     /**
10932      * Enables a {@link Roo.TabPanelItem} that is disabled.
10933      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10934      */
10935     enableTab : function(id){
10936         var tab = this.items[id];
10937         tab.enable();
10938     },
10939
10940     /**
10941      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10942      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10943      * @return {Roo.TabPanelItem} The TabPanelItem.
10944      */
10945     activate : function(id){
10946         var tab = this.items[id];
10947         if(!tab){
10948             return null;
10949         }
10950         if(tab == this.active || tab.disabled){
10951             return tab;
10952         }
10953         var e = {};
10954         this.fireEvent("beforetabchange", this, e, tab);
10955         if(e.cancel !== true && !tab.disabled){
10956             if(this.active){
10957                 this.active.hide();
10958             }
10959             this.active = this.items[id];
10960             this.active.show();
10961             this.fireEvent("tabchange", this, this.active);
10962         }
10963         return tab;
10964     },
10965
10966     /**
10967      * Gets the active {@link Roo.TabPanelItem}.
10968      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10969      */
10970     getActiveTab : function(){
10971         return this.active;
10972     },
10973
10974     /**
10975      * Updates the tab body element to fit the height of the container element
10976      * for overflow scrolling
10977      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10978      */
10979     syncHeight : function(targetHeight){
10980         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10981         var bm = this.bodyEl.getMargins();
10982         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10983         this.bodyEl.setHeight(newHeight);
10984         return newHeight;
10985     },
10986
10987     onResize : function(){
10988         if(this.monitorResize){
10989             this.autoSizeTabs();
10990         }
10991     },
10992
10993     /**
10994      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10995      */
10996     beginUpdate : function(){
10997         this.updating = true;
10998     },
10999
11000     /**
11001      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11002      */
11003     endUpdate : function(){
11004         this.updating = false;
11005         this.autoSizeTabs();
11006     },
11007
11008     /**
11009      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11010      */
11011     autoSizeTabs : function(){
11012         var count = this.items.length;
11013         var vcount = count - this.hiddenCount;
11014         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11015         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11016         var availWidth = Math.floor(w / vcount);
11017         var b = this.stripBody;
11018         if(b.getWidth() > w){
11019             var tabs = this.items;
11020             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11021             if(availWidth < this.minTabWidth){
11022                 /*if(!this.sleft){    // incomplete scrolling code
11023                     this.createScrollButtons();
11024                 }
11025                 this.showScroll();
11026                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11027             }
11028         }else{
11029             if(this.currentTabWidth < this.preferredTabWidth){
11030                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11031             }
11032         }
11033     },
11034
11035     /**
11036      * Returns the number of tabs in this TabPanel.
11037      * @return {Number}
11038      */
11039      getCount : function(){
11040          return this.items.length;
11041      },
11042
11043     /**
11044      * Resizes all the tabs to the passed width
11045      * @param {Number} The new width
11046      */
11047     setTabWidth : function(width){
11048         this.currentTabWidth = width;
11049         for(var i = 0, len = this.items.length; i < len; i++) {
11050                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11051         }
11052     },
11053
11054     /**
11055      * Destroys this TabPanel
11056      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11057      */
11058     destroy : function(removeEl){
11059         Roo.EventManager.removeResizeListener(this.onResize, this);
11060         for(var i = 0, len = this.items.length; i < len; i++){
11061             this.items[i].purgeListeners();
11062         }
11063         if(removeEl === true){
11064             this.el.update("");
11065             this.el.remove();
11066         }
11067     }
11068 });
11069
11070 /**
11071  * @class Roo.TabPanelItem
11072  * @extends Roo.util.Observable
11073  * Represents an individual item (tab plus body) in a TabPanel.
11074  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11075  * @param {String} id The id of this TabPanelItem
11076  * @param {String} text The text for the tab of this TabPanelItem
11077  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11078  */
11079 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11080     /**
11081      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11082      * @type Roo.TabPanel
11083      */
11084     this.tabPanel = tabPanel;
11085     /**
11086      * The id for this TabPanelItem
11087      * @type String
11088      */
11089     this.id = id;
11090     /** @private */
11091     this.disabled = false;
11092     /** @private */
11093     this.text = text;
11094     /** @private */
11095     this.loaded = false;
11096     this.closable = closable;
11097
11098     /**
11099      * The body element for this TabPanelItem.
11100      * @type Roo.Element
11101      */
11102     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11103     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11104     this.bodyEl.setStyle("display", "block");
11105     this.bodyEl.setStyle("zoom", "1");
11106     this.hideAction();
11107
11108     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11109     /** @private */
11110     this.el = Roo.get(els.el, true);
11111     this.inner = Roo.get(els.inner, true);
11112     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11113     this.pnode = Roo.get(els.el.parentNode, true);
11114     this.el.on("mousedown", this.onTabMouseDown, this);
11115     this.el.on("click", this.onTabClick, this);
11116     /** @private */
11117     if(closable){
11118         var c = Roo.get(els.close, true);
11119         c.dom.title = this.closeText;
11120         c.addClassOnOver("close-over");
11121         c.on("click", this.closeClick, this);
11122      }
11123
11124     this.addEvents({
11125          /**
11126          * @event activate
11127          * Fires when this tab becomes the active tab.
11128          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11129          * @param {Roo.TabPanelItem} this
11130          */
11131         "activate": true,
11132         /**
11133          * @event beforeclose
11134          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11135          * @param {Roo.TabPanelItem} this
11136          * @param {Object} e Set cancel to true on this object to cancel the close.
11137          */
11138         "beforeclose": true,
11139         /**
11140          * @event close
11141          * Fires when this tab is closed.
11142          * @param {Roo.TabPanelItem} this
11143          */
11144          "close": true,
11145         /**
11146          * @event deactivate
11147          * Fires when this tab is no longer the active tab.
11148          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11149          * @param {Roo.TabPanelItem} this
11150          */
11151          "deactivate" : true
11152     });
11153     this.hidden = false;
11154
11155     Roo.TabPanelItem.superclass.constructor.call(this);
11156 };
11157
11158 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11159     purgeListeners : function(){
11160        Roo.util.Observable.prototype.purgeListeners.call(this);
11161        this.el.removeAllListeners();
11162     },
11163     /**
11164      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11165      */
11166     show : function(){
11167         this.pnode.addClass("on");
11168         this.showAction();
11169         if(Roo.isOpera){
11170             this.tabPanel.stripWrap.repaint();
11171         }
11172         this.fireEvent("activate", this.tabPanel, this);
11173     },
11174
11175     /**
11176      * Returns true if this tab is the active tab.
11177      * @return {Boolean}
11178      */
11179     isActive : function(){
11180         return this.tabPanel.getActiveTab() == this;
11181     },
11182
11183     /**
11184      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11185      */
11186     hide : function(){
11187         this.pnode.removeClass("on");
11188         this.hideAction();
11189         this.fireEvent("deactivate", this.tabPanel, this);
11190     },
11191
11192     hideAction : function(){
11193         this.bodyEl.hide();
11194         this.bodyEl.setStyle("position", "absolute");
11195         this.bodyEl.setLeft("-20000px");
11196         this.bodyEl.setTop("-20000px");
11197     },
11198
11199     showAction : function(){
11200         this.bodyEl.setStyle("position", "relative");
11201         this.bodyEl.setTop("");
11202         this.bodyEl.setLeft("");
11203         this.bodyEl.show();
11204     },
11205
11206     /**
11207      * Set the tooltip for the tab.
11208      * @param {String} tooltip The tab's tooltip
11209      */
11210     setTooltip : function(text){
11211         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11212             this.textEl.dom.qtip = text;
11213             this.textEl.dom.removeAttribute('title');
11214         }else{
11215             this.textEl.dom.title = text;
11216         }
11217     },
11218
11219     onTabClick : function(e){
11220         e.preventDefault();
11221         this.tabPanel.activate(this.id);
11222     },
11223
11224     onTabMouseDown : function(e){
11225         e.preventDefault();
11226         this.tabPanel.activate(this.id);
11227     },
11228
11229     getWidth : function(){
11230         return this.inner.getWidth();
11231     },
11232
11233     setWidth : function(width){
11234         var iwidth = width - this.pnode.getPadding("lr");
11235         this.inner.setWidth(iwidth);
11236         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11237         this.pnode.setWidth(width);
11238     },
11239
11240     /**
11241      * Show or hide the tab
11242      * @param {Boolean} hidden True to hide or false to show.
11243      */
11244     setHidden : function(hidden){
11245         this.hidden = hidden;
11246         this.pnode.setStyle("display", hidden ? "none" : "");
11247     },
11248
11249     /**
11250      * Returns true if this tab is "hidden"
11251      * @return {Boolean}
11252      */
11253     isHidden : function(){
11254         return this.hidden;
11255     },
11256
11257     /**
11258      * Returns the text for this tab
11259      * @return {String}
11260      */
11261     getText : function(){
11262         return this.text;
11263     },
11264
11265     autoSize : function(){
11266         //this.el.beginMeasure();
11267         this.textEl.setWidth(1);
11268         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11269         //this.el.endMeasure();
11270     },
11271
11272     /**
11273      * Sets the text for the tab (Note: this also sets the tooltip text)
11274      * @param {String} text The tab's text and tooltip
11275      */
11276     setText : function(text){
11277         this.text = text;
11278         this.textEl.update(text);
11279         this.setTooltip(text);
11280         if(!this.tabPanel.resizeTabs){
11281             this.autoSize();
11282         }
11283     },
11284     /**
11285      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11286      */
11287     activate : function(){
11288         this.tabPanel.activate(this.id);
11289     },
11290
11291     /**
11292      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11293      */
11294     disable : function(){
11295         if(this.tabPanel.active != this){
11296             this.disabled = true;
11297             this.pnode.addClass("disabled");
11298         }
11299     },
11300
11301     /**
11302      * Enables this TabPanelItem if it was previously disabled.
11303      */
11304     enable : function(){
11305         this.disabled = false;
11306         this.pnode.removeClass("disabled");
11307     },
11308
11309     /**
11310      * Sets the content for this TabPanelItem.
11311      * @param {String} content The content
11312      * @param {Boolean} loadScripts true to look for and load scripts
11313      */
11314     setContent : function(content, loadScripts){
11315         this.bodyEl.update(content, loadScripts);
11316     },
11317
11318     /**
11319      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11320      * @return {Roo.UpdateManager} The UpdateManager
11321      */
11322     getUpdateManager : function(){
11323         return this.bodyEl.getUpdateManager();
11324     },
11325
11326     /**
11327      * Set a URL to be used to load the content for this TabPanelItem.
11328      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11329      * @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)
11330      * @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)
11331      * @return {Roo.UpdateManager} The UpdateManager
11332      */
11333     setUrl : function(url, params, loadOnce){
11334         if(this.refreshDelegate){
11335             this.un('activate', this.refreshDelegate);
11336         }
11337         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11338         this.on("activate", this.refreshDelegate);
11339         return this.bodyEl.getUpdateManager();
11340     },
11341
11342     /** @private */
11343     _handleRefresh : function(url, params, loadOnce){
11344         if(!loadOnce || !this.loaded){
11345             var updater = this.bodyEl.getUpdateManager();
11346             updater.update(url, params, this._setLoaded.createDelegate(this));
11347         }
11348     },
11349
11350     /**
11351      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11352      *   Will fail silently if the setUrl method has not been called.
11353      *   This does not activate the panel, just updates its content.
11354      */
11355     refresh : function(){
11356         if(this.refreshDelegate){
11357            this.loaded = false;
11358            this.refreshDelegate();
11359         }
11360     },
11361
11362     /** @private */
11363     _setLoaded : function(){
11364         this.loaded = true;
11365     },
11366
11367     /** @private */
11368     closeClick : function(e){
11369         var o = {};
11370         e.stopEvent();
11371         this.fireEvent("beforeclose", this, o);
11372         if(o.cancel !== true){
11373             this.tabPanel.removeTab(this.id);
11374         }
11375     },
11376     /**
11377      * The text displayed in the tooltip for the close icon.
11378      * @type String
11379      */
11380     closeText : "Close this tab"
11381 });
11382
11383 /** @private */
11384 Roo.TabPanel.prototype.createStrip = function(container){
11385     var strip = document.createElement("div");
11386     strip.className = "x-tabs-wrap";
11387     container.appendChild(strip);
11388     return strip;
11389 };
11390 /** @private */
11391 Roo.TabPanel.prototype.createStripList = function(strip){
11392     // div wrapper for retard IE
11393     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>';
11394     return strip.firstChild.firstChild.firstChild.firstChild;
11395 };
11396 /** @private */
11397 Roo.TabPanel.prototype.createBody = function(container){
11398     var body = document.createElement("div");
11399     Roo.id(body, "tab-body");
11400     Roo.fly(body).addClass("x-tabs-body");
11401     container.appendChild(body);
11402     return body;
11403 };
11404 /** @private */
11405 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11406     var body = Roo.getDom(id);
11407     if(!body){
11408         body = document.createElement("div");
11409         body.id = id;
11410     }
11411     Roo.fly(body).addClass("x-tabs-item-body");
11412     bodyEl.insertBefore(body, bodyEl.firstChild);
11413     return body;
11414 };
11415 /** @private */
11416 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11417     var td = document.createElement("td");
11418     stripEl.appendChild(td);
11419     if(closable){
11420         td.className = "x-tabs-closable";
11421         if(!this.closeTpl){
11422             this.closeTpl = new Roo.Template(
11423                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11424                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11425                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11426             );
11427         }
11428         var el = this.closeTpl.overwrite(td, {"text": text});
11429         var close = el.getElementsByTagName("div")[0];
11430         var inner = el.getElementsByTagName("em")[0];
11431         return {"el": el, "close": close, "inner": inner};
11432     } else {
11433         if(!this.tabTpl){
11434             this.tabTpl = new Roo.Template(
11435                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11436                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11437             );
11438         }
11439         var el = this.tabTpl.overwrite(td, {"text": text});
11440         var inner = el.getElementsByTagName("em")[0];
11441         return {"el": el, "inner": inner};
11442     }
11443 };/*
11444  * Based on:
11445  * Ext JS Library 1.1.1
11446  * Copyright(c) 2006-2007, Ext JS, LLC.
11447  *
11448  * Originally Released Under LGPL - original licence link has changed is not relivant.
11449  *
11450  * Fork - LGPL
11451  * <script type="text/javascript">
11452  */
11453
11454 /**
11455  * @class Roo.Button
11456  * @extends Roo.util.Observable
11457  * Simple Button class
11458  * @cfg {String} text The button text
11459  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11460  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11461  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11462  * @cfg {Object} scope The scope of the handler
11463  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11464  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11465  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11466  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11467  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11468  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11469    applies if enableToggle = true)
11470  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11471  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11472   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11473  * @constructor
11474  * Create a new button
11475  * @param {Object} config The config object
11476  */
11477 Roo.Button = function(renderTo, config)
11478 {
11479     if (!config) {
11480         config = renderTo;
11481         renderTo = config.renderTo || false;
11482     }
11483     
11484     Roo.apply(this, config);
11485     this.addEvents({
11486         /**
11487              * @event click
11488              * Fires when this button is clicked
11489              * @param {Button} this
11490              * @param {EventObject} e The click event
11491              */
11492             "click" : true,
11493         /**
11494              * @event toggle
11495              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11496              * @param {Button} this
11497              * @param {Boolean} pressed
11498              */
11499             "toggle" : true,
11500         /**
11501              * @event mouseover
11502              * Fires when the mouse hovers over the button
11503              * @param {Button} this
11504              * @param {Event} e The event object
11505              */
11506         'mouseover' : true,
11507         /**
11508              * @event mouseout
11509              * Fires when the mouse exits the button
11510              * @param {Button} this
11511              * @param {Event} e The event object
11512              */
11513         'mouseout': true,
11514          /**
11515              * @event render
11516              * Fires when the button is rendered
11517              * @param {Button} this
11518              */
11519         'render': true
11520     });
11521     if(this.menu){
11522         this.menu = Roo.menu.MenuMgr.get(this.menu);
11523     }
11524     // register listeners first!!  - so render can be captured..
11525     Roo.util.Observable.call(this);
11526     if(renderTo){
11527         this.render(renderTo);
11528     }
11529     
11530   
11531 };
11532
11533 Roo.extend(Roo.Button, Roo.util.Observable, {
11534     /**
11535      * 
11536      */
11537     
11538     /**
11539      * Read-only. True if this button is hidden
11540      * @type Boolean
11541      */
11542     hidden : false,
11543     /**
11544      * Read-only. True if this button is disabled
11545      * @type Boolean
11546      */
11547     disabled : false,
11548     /**
11549      * Read-only. True if this button is pressed (only if enableToggle = true)
11550      * @type Boolean
11551      */
11552     pressed : false,
11553
11554     /**
11555      * @cfg {Number} tabIndex 
11556      * The DOM tabIndex for this button (defaults to undefined)
11557      */
11558     tabIndex : undefined,
11559
11560     /**
11561      * @cfg {Boolean} enableToggle
11562      * True to enable pressed/not pressed toggling (defaults to false)
11563      */
11564     enableToggle: false,
11565     /**
11566      * @cfg {Mixed} menu
11567      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11568      */
11569     menu : undefined,
11570     /**
11571      * @cfg {String} menuAlign
11572      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11573      */
11574     menuAlign : "tl-bl?",
11575
11576     /**
11577      * @cfg {String} iconCls
11578      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11579      */
11580     iconCls : undefined,
11581     /**
11582      * @cfg {String} type
11583      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11584      */
11585     type : 'button',
11586
11587     // private
11588     menuClassTarget: 'tr',
11589
11590     /**
11591      * @cfg {String} clickEvent
11592      * The type of event to map to the button's event handler (defaults to 'click')
11593      */
11594     clickEvent : 'click',
11595
11596     /**
11597      * @cfg {Boolean} handleMouseEvents
11598      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11599      */
11600     handleMouseEvents : true,
11601
11602     /**
11603      * @cfg {String} tooltipType
11604      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11605      */
11606     tooltipType : 'qtip',
11607
11608     /**
11609      * @cfg {String} cls
11610      * A CSS class to apply to the button's main element.
11611      */
11612     
11613     /**
11614      * @cfg {Roo.Template} template (Optional)
11615      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11616      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11617      * require code modifications if required elements (e.g. a button) aren't present.
11618      */
11619
11620     // private
11621     render : function(renderTo){
11622         var btn;
11623         if(this.hideParent){
11624             this.parentEl = Roo.get(renderTo);
11625         }
11626         if(!this.dhconfig){
11627             if(!this.template){
11628                 if(!Roo.Button.buttonTemplate){
11629                     // hideous table template
11630                     Roo.Button.buttonTemplate = new Roo.Template(
11631                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11632                         '<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>',
11633                         "</tr></tbody></table>");
11634                 }
11635                 this.template = Roo.Button.buttonTemplate;
11636             }
11637             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11638             var btnEl = btn.child("button:first");
11639             btnEl.on('focus', this.onFocus, this);
11640             btnEl.on('blur', this.onBlur, this);
11641             if(this.cls){
11642                 btn.addClass(this.cls);
11643             }
11644             if(this.icon){
11645                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11646             }
11647             if(this.iconCls){
11648                 btnEl.addClass(this.iconCls);
11649                 if(!this.cls){
11650                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11651                 }
11652             }
11653             if(this.tabIndex !== undefined){
11654                 btnEl.dom.tabIndex = this.tabIndex;
11655             }
11656             if(this.tooltip){
11657                 if(typeof this.tooltip == 'object'){
11658                     Roo.QuickTips.tips(Roo.apply({
11659                           target: btnEl.id
11660                     }, this.tooltip));
11661                 } else {
11662                     btnEl.dom[this.tooltipType] = this.tooltip;
11663                 }
11664             }
11665         }else{
11666             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11667         }
11668         this.el = btn;
11669         if(this.id){
11670             this.el.dom.id = this.el.id = this.id;
11671         }
11672         if(this.menu){
11673             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11674             this.menu.on("show", this.onMenuShow, this);
11675             this.menu.on("hide", this.onMenuHide, this);
11676         }
11677         btn.addClass("x-btn");
11678         if(Roo.isIE && !Roo.isIE7){
11679             this.autoWidth.defer(1, this);
11680         }else{
11681             this.autoWidth();
11682         }
11683         if(this.handleMouseEvents){
11684             btn.on("mouseover", this.onMouseOver, this);
11685             btn.on("mouseout", this.onMouseOut, this);
11686             btn.on("mousedown", this.onMouseDown, this);
11687         }
11688         btn.on(this.clickEvent, this.onClick, this);
11689         //btn.on("mouseup", this.onMouseUp, this);
11690         if(this.hidden){
11691             this.hide();
11692         }
11693         if(this.disabled){
11694             this.disable();
11695         }
11696         Roo.ButtonToggleMgr.register(this);
11697         if(this.pressed){
11698             this.el.addClass("x-btn-pressed");
11699         }
11700         if(this.repeat){
11701             var repeater = new Roo.util.ClickRepeater(btn,
11702                 typeof this.repeat == "object" ? this.repeat : {}
11703             );
11704             repeater.on("click", this.onClick,  this);
11705         }
11706         
11707         this.fireEvent('render', this);
11708         
11709     },
11710     /**
11711      * Returns the button's underlying element
11712      * @return {Roo.Element} The element
11713      */
11714     getEl : function(){
11715         return this.el;  
11716     },
11717     
11718     /**
11719      * Destroys this Button and removes any listeners.
11720      */
11721     destroy : function(){
11722         Roo.ButtonToggleMgr.unregister(this);
11723         this.el.removeAllListeners();
11724         this.purgeListeners();
11725         this.el.remove();
11726     },
11727
11728     // private
11729     autoWidth : function(){
11730         if(this.el){
11731             this.el.setWidth("auto");
11732             if(Roo.isIE7 && Roo.isStrict){
11733                 var ib = this.el.child('button');
11734                 if(ib && ib.getWidth() > 20){
11735                     ib.clip();
11736                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11737                 }
11738             }
11739             if(this.minWidth){
11740                 if(this.hidden){
11741                     this.el.beginMeasure();
11742                 }
11743                 if(this.el.getWidth() < this.minWidth){
11744                     this.el.setWidth(this.minWidth);
11745                 }
11746                 if(this.hidden){
11747                     this.el.endMeasure();
11748                 }
11749             }
11750         }
11751     },
11752
11753     /**
11754      * Assigns this button's click handler
11755      * @param {Function} handler The function to call when the button is clicked
11756      * @param {Object} scope (optional) Scope for the function passed in
11757      */
11758     setHandler : function(handler, scope){
11759         this.handler = handler;
11760         this.scope = scope;  
11761     },
11762     
11763     /**
11764      * Sets this button's text
11765      * @param {String} text The button text
11766      */
11767     setText : function(text){
11768         this.text = text;
11769         if(this.el){
11770             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11771         }
11772         this.autoWidth();
11773     },
11774     
11775     /**
11776      * Gets the text for this button
11777      * @return {String} The button text
11778      */
11779     getText : function(){
11780         return this.text;  
11781     },
11782     
11783     /**
11784      * Show this button
11785      */
11786     show: function(){
11787         this.hidden = false;
11788         if(this.el){
11789             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11790         }
11791     },
11792     
11793     /**
11794      * Hide this button
11795      */
11796     hide: function(){
11797         this.hidden = true;
11798         if(this.el){
11799             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11800         }
11801     },
11802     
11803     /**
11804      * Convenience function for boolean show/hide
11805      * @param {Boolean} visible True to show, false to hide
11806      */
11807     setVisible: function(visible){
11808         if(visible) {
11809             this.show();
11810         }else{
11811             this.hide();
11812         }
11813     },
11814     
11815     /**
11816      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11817      * @param {Boolean} state (optional) Force a particular state
11818      */
11819     toggle : function(state){
11820         state = state === undefined ? !this.pressed : state;
11821         if(state != this.pressed){
11822             if(state){
11823                 this.el.addClass("x-btn-pressed");
11824                 this.pressed = true;
11825                 this.fireEvent("toggle", this, true);
11826             }else{
11827                 this.el.removeClass("x-btn-pressed");
11828                 this.pressed = false;
11829                 this.fireEvent("toggle", this, false);
11830             }
11831             if(this.toggleHandler){
11832                 this.toggleHandler.call(this.scope || this, this, state);
11833             }
11834         }
11835     },
11836     
11837     /**
11838      * Focus the button
11839      */
11840     focus : function(){
11841         this.el.child('button:first').focus();
11842     },
11843     
11844     /**
11845      * Disable this button
11846      */
11847     disable : function(){
11848         if(this.el){
11849             this.el.addClass("x-btn-disabled");
11850         }
11851         this.disabled = true;
11852     },
11853     
11854     /**
11855      * Enable this button
11856      */
11857     enable : function(){
11858         if(this.el){
11859             this.el.removeClass("x-btn-disabled");
11860         }
11861         this.disabled = false;
11862     },
11863
11864     /**
11865      * Convenience function for boolean enable/disable
11866      * @param {Boolean} enabled True to enable, false to disable
11867      */
11868     setDisabled : function(v){
11869         this[v !== true ? "enable" : "disable"]();
11870     },
11871
11872     // private
11873     onClick : function(e){
11874         if(e){
11875             e.preventDefault();
11876         }
11877         if(e.button != 0){
11878             return;
11879         }
11880         if(!this.disabled){
11881             if(this.enableToggle){
11882                 this.toggle();
11883             }
11884             if(this.menu && !this.menu.isVisible()){
11885                 this.menu.show(this.el, this.menuAlign);
11886             }
11887             this.fireEvent("click", this, e);
11888             if(this.handler){
11889                 this.el.removeClass("x-btn-over");
11890                 this.handler.call(this.scope || this, this, e);
11891             }
11892         }
11893     },
11894     // private
11895     onMouseOver : function(e){
11896         if(!this.disabled){
11897             this.el.addClass("x-btn-over");
11898             this.fireEvent('mouseover', this, e);
11899         }
11900     },
11901     // private
11902     onMouseOut : function(e){
11903         if(!e.within(this.el,  true)){
11904             this.el.removeClass("x-btn-over");
11905             this.fireEvent('mouseout', this, e);
11906         }
11907     },
11908     // private
11909     onFocus : function(e){
11910         if(!this.disabled){
11911             this.el.addClass("x-btn-focus");
11912         }
11913     },
11914     // private
11915     onBlur : function(e){
11916         this.el.removeClass("x-btn-focus");
11917     },
11918     // private
11919     onMouseDown : function(e){
11920         if(!this.disabled && e.button == 0){
11921             this.el.addClass("x-btn-click");
11922             Roo.get(document).on('mouseup', this.onMouseUp, this);
11923         }
11924     },
11925     // private
11926     onMouseUp : function(e){
11927         if(e.button == 0){
11928             this.el.removeClass("x-btn-click");
11929             Roo.get(document).un('mouseup', this.onMouseUp, this);
11930         }
11931     },
11932     // private
11933     onMenuShow : function(e){
11934         this.el.addClass("x-btn-menu-active");
11935     },
11936     // private
11937     onMenuHide : function(e){
11938         this.el.removeClass("x-btn-menu-active");
11939     }   
11940 });
11941
11942 // Private utility class used by Button
11943 Roo.ButtonToggleMgr = function(){
11944    var groups = {};
11945    
11946    function toggleGroup(btn, state){
11947        if(state){
11948            var g = groups[btn.toggleGroup];
11949            for(var i = 0, l = g.length; i < l; i++){
11950                if(g[i] != btn){
11951                    g[i].toggle(false);
11952                }
11953            }
11954        }
11955    }
11956    
11957    return {
11958        register : function(btn){
11959            if(!btn.toggleGroup){
11960                return;
11961            }
11962            var g = groups[btn.toggleGroup];
11963            if(!g){
11964                g = groups[btn.toggleGroup] = [];
11965            }
11966            g.push(btn);
11967            btn.on("toggle", toggleGroup);
11968        },
11969        
11970        unregister : function(btn){
11971            if(!btn.toggleGroup){
11972                return;
11973            }
11974            var g = groups[btn.toggleGroup];
11975            if(g){
11976                g.remove(btn);
11977                btn.un("toggle", toggleGroup);
11978            }
11979        }
11980    };
11981 }();/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991  
11992 /**
11993  * @class Roo.SplitButton
11994  * @extends Roo.Button
11995  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11996  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11997  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11998  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11999  * @cfg {String} arrowTooltip The title attribute of the arrow
12000  * @constructor
12001  * Create a new menu button
12002  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12003  * @param {Object} config The config object
12004  */
12005 Roo.SplitButton = function(renderTo, config){
12006     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12007     /**
12008      * @event arrowclick
12009      * Fires when this button's arrow is clicked
12010      * @param {SplitButton} this
12011      * @param {EventObject} e The click event
12012      */
12013     this.addEvents({"arrowclick":true});
12014 };
12015
12016 Roo.extend(Roo.SplitButton, Roo.Button, {
12017     render : function(renderTo){
12018         // this is one sweet looking template!
12019         var tpl = new Roo.Template(
12020             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12021             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12022             '<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>',
12023             "</tbody></table></td><td>",
12024             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12025             '<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>',
12026             "</tbody></table></td></tr></table>"
12027         );
12028         var btn = tpl.append(renderTo, [this.text, this.type], true);
12029         var btnEl = btn.child("button");
12030         if(this.cls){
12031             btn.addClass(this.cls);
12032         }
12033         if(this.icon){
12034             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12035         }
12036         if(this.iconCls){
12037             btnEl.addClass(this.iconCls);
12038             if(!this.cls){
12039                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12040             }
12041         }
12042         this.el = btn;
12043         if(this.handleMouseEvents){
12044             btn.on("mouseover", this.onMouseOver, this);
12045             btn.on("mouseout", this.onMouseOut, this);
12046             btn.on("mousedown", this.onMouseDown, this);
12047             btn.on("mouseup", this.onMouseUp, this);
12048         }
12049         btn.on(this.clickEvent, this.onClick, this);
12050         if(this.tooltip){
12051             if(typeof this.tooltip == 'object'){
12052                 Roo.QuickTips.tips(Roo.apply({
12053                       target: btnEl.id
12054                 }, this.tooltip));
12055             } else {
12056                 btnEl.dom[this.tooltipType] = this.tooltip;
12057             }
12058         }
12059         if(this.arrowTooltip){
12060             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12061         }
12062         if(this.hidden){
12063             this.hide();
12064         }
12065         if(this.disabled){
12066             this.disable();
12067         }
12068         if(this.pressed){
12069             this.el.addClass("x-btn-pressed");
12070         }
12071         if(Roo.isIE && !Roo.isIE7){
12072             this.autoWidth.defer(1, this);
12073         }else{
12074             this.autoWidth();
12075         }
12076         if(this.menu){
12077             this.menu.on("show", this.onMenuShow, this);
12078             this.menu.on("hide", this.onMenuHide, this);
12079         }
12080         this.fireEvent('render', this);
12081     },
12082
12083     // private
12084     autoWidth : function(){
12085         if(this.el){
12086             var tbl = this.el.child("table:first");
12087             var tbl2 = this.el.child("table:last");
12088             this.el.setWidth("auto");
12089             tbl.setWidth("auto");
12090             if(Roo.isIE7 && Roo.isStrict){
12091                 var ib = this.el.child('button:first');
12092                 if(ib && ib.getWidth() > 20){
12093                     ib.clip();
12094                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12095                 }
12096             }
12097             if(this.minWidth){
12098                 if(this.hidden){
12099                     this.el.beginMeasure();
12100                 }
12101                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12102                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12103                 }
12104                 if(this.hidden){
12105                     this.el.endMeasure();
12106                 }
12107             }
12108             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12109         } 
12110     },
12111     /**
12112      * Sets this button's click handler
12113      * @param {Function} handler The function to call when the button is clicked
12114      * @param {Object} scope (optional) Scope for the function passed above
12115      */
12116     setHandler : function(handler, scope){
12117         this.handler = handler;
12118         this.scope = scope;  
12119     },
12120     
12121     /**
12122      * Sets this button's arrow click handler
12123      * @param {Function} handler The function to call when the arrow is clicked
12124      * @param {Object} scope (optional) Scope for the function passed above
12125      */
12126     setArrowHandler : function(handler, scope){
12127         this.arrowHandler = handler;
12128         this.scope = scope;  
12129     },
12130     
12131     /**
12132      * Focus the button
12133      */
12134     focus : function(){
12135         if(this.el){
12136             this.el.child("button:first").focus();
12137         }
12138     },
12139
12140     // private
12141     onClick : function(e){
12142         e.preventDefault();
12143         if(!this.disabled){
12144             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12145                 if(this.menu && !this.menu.isVisible()){
12146                     this.menu.show(this.el, this.menuAlign);
12147                 }
12148                 this.fireEvent("arrowclick", this, e);
12149                 if(this.arrowHandler){
12150                     this.arrowHandler.call(this.scope || this, this, e);
12151                 }
12152             }else{
12153                 this.fireEvent("click", this, e);
12154                 if(this.handler){
12155                     this.handler.call(this.scope || this, this, e);
12156                 }
12157             }
12158         }
12159     },
12160     // private
12161     onMouseDown : function(e){
12162         if(!this.disabled){
12163             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12164         }
12165     },
12166     // private
12167     onMouseUp : function(e){
12168         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12169     }   
12170 });
12171
12172
12173 // backwards compat
12174 Roo.MenuButton = Roo.SplitButton;/*
12175  * Based on:
12176  * Ext JS Library 1.1.1
12177  * Copyright(c) 2006-2007, Ext JS, LLC.
12178  *
12179  * Originally Released Under LGPL - original licence link has changed is not relivant.
12180  *
12181  * Fork - LGPL
12182  * <script type="text/javascript">
12183  */
12184
12185 /**
12186  * @class Roo.Toolbar
12187  * Basic Toolbar class.
12188  * @constructor
12189  * Creates a new Toolbar
12190  * @param {Object} config The config object
12191  */ 
12192 Roo.Toolbar = function(container, buttons, config)
12193 {
12194     /// old consturctor format still supported..
12195     if(container instanceof Array){ // omit the container for later rendering
12196         buttons = container;
12197         config = buttons;
12198         container = null;
12199     }
12200     if (typeof(container) == 'object' && container.xtype) {
12201         config = container;
12202         container = config.container;
12203         buttons = config.buttons; // not really - use items!!
12204     }
12205     var xitems = [];
12206     if (config && config.items) {
12207         xitems = config.items;
12208         delete config.items;
12209     }
12210     Roo.apply(this, config);
12211     this.buttons = buttons;
12212     
12213     if(container){
12214         this.render(container);
12215     }
12216     Roo.each(xitems, function(b) {
12217         this.add(b);
12218     }, this);
12219     
12220 };
12221
12222 Roo.Toolbar.prototype = {
12223     /**
12224      * @cfg {Roo.data.Store} items
12225      * array of button configs or elements to add
12226      */
12227     
12228     /**
12229      * @cfg {String/HTMLElement/Element} container
12230      * The id or element that will contain the toolbar
12231      */
12232     // private
12233     render : function(ct){
12234         this.el = Roo.get(ct);
12235         if(this.cls){
12236             this.el.addClass(this.cls);
12237         }
12238         // using a table allows for vertical alignment
12239         // 100% width is needed by Safari...
12240         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12241         this.tr = this.el.child("tr", true);
12242         var autoId = 0;
12243         this.items = new Roo.util.MixedCollection(false, function(o){
12244             return o.id || ("item" + (++autoId));
12245         });
12246         if(this.buttons){
12247             this.add.apply(this, this.buttons);
12248             delete this.buttons;
12249         }
12250     },
12251
12252     /**
12253      * Adds element(s) to the toolbar -- this function takes a variable number of 
12254      * arguments of mixed type and adds them to the toolbar.
12255      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12256      * <ul>
12257      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12258      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12259      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12260      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12261      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12262      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12263      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12264      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12265      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12266      * </ul>
12267      * @param {Mixed} arg2
12268      * @param {Mixed} etc.
12269      */
12270     add : function(){
12271         var a = arguments, l = a.length;
12272         for(var i = 0; i < l; i++){
12273             this._add(a[i]);
12274         }
12275     },
12276     // private..
12277     _add : function(el) {
12278         
12279         if (el.xtype) {
12280             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12281         }
12282         
12283         if (el.applyTo){ // some kind of form field
12284             return this.addField(el);
12285         } 
12286         if (el.render){ // some kind of Toolbar.Item
12287             return this.addItem(el);
12288         }
12289         if (typeof el == "string"){ // string
12290             if(el == "separator" || el == "-"){
12291                 return this.addSeparator();
12292             }
12293             if (el == " "){
12294                 return this.addSpacer();
12295             }
12296             if(el == "->"){
12297                 return this.addFill();
12298             }
12299             return this.addText(el);
12300             
12301         }
12302         if(el.tagName){ // element
12303             return this.addElement(el);
12304         }
12305         if(typeof el == "object"){ // must be button config?
12306             return this.addButton(el);
12307         }
12308         // and now what?!?!
12309         return false;
12310         
12311     },
12312     
12313     /**
12314      * Add an Xtype element
12315      * @param {Object} xtype Xtype Object
12316      * @return {Object} created Object
12317      */
12318     addxtype : function(e){
12319         return this.add(e);  
12320     },
12321     
12322     /**
12323      * Returns the Element for this toolbar.
12324      * @return {Roo.Element}
12325      */
12326     getEl : function(){
12327         return this.el;  
12328     },
12329     
12330     /**
12331      * Adds a separator
12332      * @return {Roo.Toolbar.Item} The separator item
12333      */
12334     addSeparator : function(){
12335         return this.addItem(new Roo.Toolbar.Separator());
12336     },
12337
12338     /**
12339      * Adds a spacer element
12340      * @return {Roo.Toolbar.Spacer} The spacer item
12341      */
12342     addSpacer : function(){
12343         return this.addItem(new Roo.Toolbar.Spacer());
12344     },
12345
12346     /**
12347      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12348      * @return {Roo.Toolbar.Fill} The fill item
12349      */
12350     addFill : function(){
12351         return this.addItem(new Roo.Toolbar.Fill());
12352     },
12353
12354     /**
12355      * Adds any standard HTML element to the toolbar
12356      * @param {String/HTMLElement/Element} el The element or id of the element to add
12357      * @return {Roo.Toolbar.Item} The element's item
12358      */
12359     addElement : function(el){
12360         return this.addItem(new Roo.Toolbar.Item(el));
12361     },
12362     /**
12363      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12364      * @type Roo.util.MixedCollection  
12365      */
12366     items : false,
12367      
12368     /**
12369      * Adds any Toolbar.Item or subclass
12370      * @param {Roo.Toolbar.Item} item
12371      * @return {Roo.Toolbar.Item} The item
12372      */
12373     addItem : function(item){
12374         var td = this.nextBlock();
12375         item.render(td);
12376         this.items.add(item);
12377         return item;
12378     },
12379     
12380     /**
12381      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12382      * @param {Object/Array} config A button config or array of configs
12383      * @return {Roo.Toolbar.Button/Array}
12384      */
12385     addButton : function(config){
12386         if(config instanceof Array){
12387             var buttons = [];
12388             for(var i = 0, len = config.length; i < len; i++) {
12389                 buttons.push(this.addButton(config[i]));
12390             }
12391             return buttons;
12392         }
12393         var b = config;
12394         if(!(config instanceof Roo.Toolbar.Button)){
12395             b = config.split ?
12396                 new Roo.Toolbar.SplitButton(config) :
12397                 new Roo.Toolbar.Button(config);
12398         }
12399         var td = this.nextBlock();
12400         b.render(td);
12401         this.items.add(b);
12402         return b;
12403     },
12404     
12405     /**
12406      * Adds text to the toolbar
12407      * @param {String} text The text to add
12408      * @return {Roo.Toolbar.Item} The element's item
12409      */
12410     addText : function(text){
12411         return this.addItem(new Roo.Toolbar.TextItem(text));
12412     },
12413     
12414     /**
12415      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12416      * @param {Number} index The index where the item is to be inserted
12417      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12418      * @return {Roo.Toolbar.Button/Item}
12419      */
12420     insertButton : function(index, item){
12421         if(item instanceof Array){
12422             var buttons = [];
12423             for(var i = 0, len = item.length; i < len; i++) {
12424                buttons.push(this.insertButton(index + i, item[i]));
12425             }
12426             return buttons;
12427         }
12428         if (!(item instanceof Roo.Toolbar.Button)){
12429            item = new Roo.Toolbar.Button(item);
12430         }
12431         var td = document.createElement("td");
12432         this.tr.insertBefore(td, this.tr.childNodes[index]);
12433         item.render(td);
12434         this.items.insert(index, item);
12435         return item;
12436     },
12437     
12438     /**
12439      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12440      * @param {Object} config
12441      * @return {Roo.Toolbar.Item} The element's item
12442      */
12443     addDom : function(config, returnEl){
12444         var td = this.nextBlock();
12445         Roo.DomHelper.overwrite(td, config);
12446         var ti = new Roo.Toolbar.Item(td.firstChild);
12447         ti.render(td);
12448         this.items.add(ti);
12449         return ti;
12450     },
12451
12452     /**
12453      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12454      * @type Roo.util.MixedCollection  
12455      */
12456     fields : false,
12457     
12458     /**
12459      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12460      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12461      * @param {Roo.form.Field} field
12462      * @return {Roo.ToolbarItem}
12463      */
12464      
12465       
12466     addField : function(field) {
12467         if (!this.fields) {
12468             var autoId = 0;
12469             this.fields = new Roo.util.MixedCollection(false, function(o){
12470                 return o.id || ("item" + (++autoId));
12471             });
12472
12473         }
12474         
12475         var td = this.nextBlock();
12476         field.render(td);
12477         var ti = new Roo.Toolbar.Item(td.firstChild);
12478         ti.render(td);
12479         this.items.add(ti);
12480         this.fields.add(field);
12481         return ti;
12482     },
12483     /**
12484      * Hide the toolbar
12485      * @method hide
12486      */
12487      
12488       
12489     hide : function()
12490     {
12491         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12492         this.el.child('div').hide();
12493     },
12494     /**
12495      * Show the toolbar
12496      * @method show
12497      */
12498     show : function()
12499     {
12500         this.el.child('div').show();
12501     },
12502       
12503     // private
12504     nextBlock : function(){
12505         var td = document.createElement("td");
12506         this.tr.appendChild(td);
12507         return td;
12508     },
12509
12510     // private
12511     destroy : function(){
12512         if(this.items){ // rendered?
12513             Roo.destroy.apply(Roo, this.items.items);
12514         }
12515         if(this.fields){ // rendered?
12516             Roo.destroy.apply(Roo, this.fields.items);
12517         }
12518         Roo.Element.uncache(this.el, this.tr);
12519     }
12520 };
12521
12522 /**
12523  * @class Roo.Toolbar.Item
12524  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12525  * @constructor
12526  * Creates a new Item
12527  * @param {HTMLElement} el 
12528  */
12529 Roo.Toolbar.Item = function(el){
12530     this.el = Roo.getDom(el);
12531     this.id = Roo.id(this.el);
12532     this.hidden = false;
12533 };
12534
12535 Roo.Toolbar.Item.prototype = {
12536     
12537     /**
12538      * Get this item's HTML Element
12539      * @return {HTMLElement}
12540      */
12541     getEl : function(){
12542        return this.el;  
12543     },
12544
12545     // private
12546     render : function(td){
12547         this.td = td;
12548         td.appendChild(this.el);
12549     },
12550     
12551     /**
12552      * Removes and destroys this item.
12553      */
12554     destroy : function(){
12555         this.td.parentNode.removeChild(this.td);
12556     },
12557     
12558     /**
12559      * Shows this item.
12560      */
12561     show: function(){
12562         this.hidden = false;
12563         this.td.style.display = "";
12564     },
12565     
12566     /**
12567      * Hides this item.
12568      */
12569     hide: function(){
12570         this.hidden = true;
12571         this.td.style.display = "none";
12572     },
12573     
12574     /**
12575      * Convenience function for boolean show/hide.
12576      * @param {Boolean} visible true to show/false to hide
12577      */
12578     setVisible: function(visible){
12579         if(visible) {
12580             this.show();
12581         }else{
12582             this.hide();
12583         }
12584     },
12585     
12586     /**
12587      * Try to focus this item.
12588      */
12589     focus : function(){
12590         Roo.fly(this.el).focus();
12591     },
12592     
12593     /**
12594      * Disables this item.
12595      */
12596     disable : function(){
12597         Roo.fly(this.td).addClass("x-item-disabled");
12598         this.disabled = true;
12599         this.el.disabled = true;
12600     },
12601     
12602     /**
12603      * Enables this item.
12604      */
12605     enable : function(){
12606         Roo.fly(this.td).removeClass("x-item-disabled");
12607         this.disabled = false;
12608         this.el.disabled = false;
12609     }
12610 };
12611
12612
12613 /**
12614  * @class Roo.Toolbar.Separator
12615  * @extends Roo.Toolbar.Item
12616  * A simple toolbar separator class
12617  * @constructor
12618  * Creates a new Separator
12619  */
12620 Roo.Toolbar.Separator = function(){
12621     var s = document.createElement("span");
12622     s.className = "ytb-sep";
12623     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12624 };
12625 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12626     enable:Roo.emptyFn,
12627     disable:Roo.emptyFn,
12628     focus:Roo.emptyFn
12629 });
12630
12631 /**
12632  * @class Roo.Toolbar.Spacer
12633  * @extends Roo.Toolbar.Item
12634  * A simple element that adds extra horizontal space to a toolbar.
12635  * @constructor
12636  * Creates a new Spacer
12637  */
12638 Roo.Toolbar.Spacer = function(){
12639     var s = document.createElement("div");
12640     s.className = "ytb-spacer";
12641     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12642 };
12643 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12644     enable:Roo.emptyFn,
12645     disable:Roo.emptyFn,
12646     focus:Roo.emptyFn
12647 });
12648
12649 /**
12650  * @class Roo.Toolbar.Fill
12651  * @extends Roo.Toolbar.Spacer
12652  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12653  * @constructor
12654  * Creates a new Spacer
12655  */
12656 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12657     // private
12658     render : function(td){
12659         td.style.width = '100%';
12660         Roo.Toolbar.Fill.superclass.render.call(this, td);
12661     }
12662 });
12663
12664 /**
12665  * @class Roo.Toolbar.TextItem
12666  * @extends Roo.Toolbar.Item
12667  * A simple class that renders text directly into a toolbar.
12668  * @constructor
12669  * Creates a new TextItem
12670  * @param {String} text
12671  */
12672 Roo.Toolbar.TextItem = function(text){
12673     if (typeof(text) == 'object') {
12674         text = text.text;
12675     }
12676     var s = document.createElement("span");
12677     s.className = "ytb-text";
12678     s.innerHTML = text;
12679     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12680 };
12681 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12682     enable:Roo.emptyFn,
12683     disable:Roo.emptyFn,
12684     focus:Roo.emptyFn
12685 });
12686
12687 /**
12688  * @class Roo.Toolbar.Button
12689  * @extends Roo.Button
12690  * A button that renders into a toolbar.
12691  * @constructor
12692  * Creates a new Button
12693  * @param {Object} config A standard {@link Roo.Button} config object
12694  */
12695 Roo.Toolbar.Button = function(config){
12696     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12697 };
12698 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12699     render : function(td){
12700         this.td = td;
12701         Roo.Toolbar.Button.superclass.render.call(this, td);
12702     },
12703     
12704     /**
12705      * Removes and destroys this button
12706      */
12707     destroy : function(){
12708         Roo.Toolbar.Button.superclass.destroy.call(this);
12709         this.td.parentNode.removeChild(this.td);
12710     },
12711     
12712     /**
12713      * Shows this button
12714      */
12715     show: function(){
12716         this.hidden = false;
12717         this.td.style.display = "";
12718     },
12719     
12720     /**
12721      * Hides this button
12722      */
12723     hide: function(){
12724         this.hidden = true;
12725         this.td.style.display = "none";
12726     },
12727
12728     /**
12729      * Disables this item
12730      */
12731     disable : function(){
12732         Roo.fly(this.td).addClass("x-item-disabled");
12733         this.disabled = true;
12734     },
12735
12736     /**
12737      * Enables this item
12738      */
12739     enable : function(){
12740         Roo.fly(this.td).removeClass("x-item-disabled");
12741         this.disabled = false;
12742     }
12743 });
12744 // backwards compat
12745 Roo.ToolbarButton = Roo.Toolbar.Button;
12746
12747 /**
12748  * @class Roo.Toolbar.SplitButton
12749  * @extends Roo.SplitButton
12750  * A menu button that renders into a toolbar.
12751  * @constructor
12752  * Creates a new SplitButton
12753  * @param {Object} config A standard {@link Roo.SplitButton} config object
12754  */
12755 Roo.Toolbar.SplitButton = function(config){
12756     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12757 };
12758 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12759     render : function(td){
12760         this.td = td;
12761         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12762     },
12763     
12764     /**
12765      * Removes and destroys this button
12766      */
12767     destroy : function(){
12768         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12769         this.td.parentNode.removeChild(this.td);
12770     },
12771     
12772     /**
12773      * Shows this button
12774      */
12775     show: function(){
12776         this.hidden = false;
12777         this.td.style.display = "";
12778     },
12779     
12780     /**
12781      * Hides this button
12782      */
12783     hide: function(){
12784         this.hidden = true;
12785         this.td.style.display = "none";
12786     }
12787 });
12788
12789 // backwards compat
12790 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800  
12801 /**
12802  * @class Roo.PagingToolbar
12803  * @extends Roo.Toolbar
12804  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12805  * @constructor
12806  * Create a new PagingToolbar
12807  * @param {Object} config The config object
12808  */
12809 Roo.PagingToolbar = function(el, ds, config)
12810 {
12811     // old args format still supported... - xtype is prefered..
12812     if (typeof(el) == 'object' && el.xtype) {
12813         // created from xtype...
12814         config = el;
12815         ds = el.dataSource;
12816         el = config.container;
12817     }
12818     var items = [];
12819     if (config.items) {
12820         items = config.items;
12821         config.items = [];
12822     }
12823     
12824     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12825     this.ds = ds;
12826     this.cursor = 0;
12827     this.renderButtons(this.el);
12828     this.bind(ds);
12829     
12830     // supprot items array.
12831    
12832     Roo.each(items, function(e) {
12833         this.add(Roo.factory(e));
12834     },this);
12835     
12836 };
12837
12838 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12839     /**
12840      * @cfg {Roo.data.Store} dataSource
12841      * The underlying data store providing the paged data
12842      */
12843     /**
12844      * @cfg {String/HTMLElement/Element} container
12845      * container The id or element that will contain the toolbar
12846      */
12847     /**
12848      * @cfg {Boolean} displayInfo
12849      * True to display the displayMsg (defaults to false)
12850      */
12851     /**
12852      * @cfg {Number} pageSize
12853      * The number of records to display per page (defaults to 20)
12854      */
12855     pageSize: 20,
12856     /**
12857      * @cfg {String} displayMsg
12858      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12859      */
12860     displayMsg : 'Displaying {0} - {1} of {2}',
12861     /**
12862      * @cfg {String} emptyMsg
12863      * The message to display when no records are found (defaults to "No data to display")
12864      */
12865     emptyMsg : 'No data to display',
12866     /**
12867      * Customizable piece of the default paging text (defaults to "Page")
12868      * @type String
12869      */
12870     beforePageText : "Page",
12871     /**
12872      * Customizable piece of the default paging text (defaults to "of %0")
12873      * @type String
12874      */
12875     afterPageText : "of {0}",
12876     /**
12877      * Customizable piece of the default paging text (defaults to "First Page")
12878      * @type String
12879      */
12880     firstText : "First Page",
12881     /**
12882      * Customizable piece of the default paging text (defaults to "Previous Page")
12883      * @type String
12884      */
12885     prevText : "Previous Page",
12886     /**
12887      * Customizable piece of the default paging text (defaults to "Next Page")
12888      * @type String
12889      */
12890     nextText : "Next Page",
12891     /**
12892      * Customizable piece of the default paging text (defaults to "Last Page")
12893      * @type String
12894      */
12895     lastText : "Last Page",
12896     /**
12897      * Customizable piece of the default paging text (defaults to "Refresh")
12898      * @type String
12899      */
12900     refreshText : "Refresh",
12901
12902     // private
12903     renderButtons : function(el){
12904         Roo.PagingToolbar.superclass.render.call(this, el);
12905         this.first = this.addButton({
12906             tooltip: this.firstText,
12907             cls: "x-btn-icon x-grid-page-first",
12908             disabled: true,
12909             handler: this.onClick.createDelegate(this, ["first"])
12910         });
12911         this.prev = this.addButton({
12912             tooltip: this.prevText,
12913             cls: "x-btn-icon x-grid-page-prev",
12914             disabled: true,
12915             handler: this.onClick.createDelegate(this, ["prev"])
12916         });
12917         //this.addSeparator();
12918         this.add(this.beforePageText);
12919         this.field = Roo.get(this.addDom({
12920            tag: "input",
12921            type: "text",
12922            size: "3",
12923            value: "1",
12924            cls: "x-grid-page-number"
12925         }).el);
12926         this.field.on("keydown", this.onPagingKeydown, this);
12927         this.field.on("focus", function(){this.dom.select();});
12928         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12929         this.field.setHeight(18);
12930         //this.addSeparator();
12931         this.next = this.addButton({
12932             tooltip: this.nextText,
12933             cls: "x-btn-icon x-grid-page-next",
12934             disabled: true,
12935             handler: this.onClick.createDelegate(this, ["next"])
12936         });
12937         this.last = this.addButton({
12938             tooltip: this.lastText,
12939             cls: "x-btn-icon x-grid-page-last",
12940             disabled: true,
12941             handler: this.onClick.createDelegate(this, ["last"])
12942         });
12943         //this.addSeparator();
12944         this.loading = this.addButton({
12945             tooltip: this.refreshText,
12946             cls: "x-btn-icon x-grid-loading",
12947             handler: this.onClick.createDelegate(this, ["refresh"])
12948         });
12949
12950         if(this.displayInfo){
12951             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12952         }
12953     },
12954
12955     // private
12956     updateInfo : function(){
12957         if(this.displayEl){
12958             var count = this.ds.getCount();
12959             var msg = count == 0 ?
12960                 this.emptyMsg :
12961                 String.format(
12962                     this.displayMsg,
12963                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12964                 );
12965             this.displayEl.update(msg);
12966         }
12967     },
12968
12969     // private
12970     onLoad : function(ds, r, o){
12971        this.cursor = o.params ? o.params.start : 0;
12972        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12973
12974        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12975        this.field.dom.value = ap;
12976        this.first.setDisabled(ap == 1);
12977        this.prev.setDisabled(ap == 1);
12978        this.next.setDisabled(ap == ps);
12979        this.last.setDisabled(ap == ps);
12980        this.loading.enable();
12981        this.updateInfo();
12982     },
12983
12984     // private
12985     getPageData : function(){
12986         var total = this.ds.getTotalCount();
12987         return {
12988             total : total,
12989             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12990             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12991         };
12992     },
12993
12994     // private
12995     onLoadError : function(){
12996         this.loading.enable();
12997     },
12998
12999     // private
13000     onPagingKeydown : function(e){
13001         var k = e.getKey();
13002         var d = this.getPageData();
13003         if(k == e.RETURN){
13004             var v = this.field.dom.value, pageNum;
13005             if(!v || isNaN(pageNum = parseInt(v, 10))){
13006                 this.field.dom.value = d.activePage;
13007                 return;
13008             }
13009             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13010             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13011             e.stopEvent();
13012         }
13013         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))
13014         {
13015           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13016           this.field.dom.value = pageNum;
13017           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13018           e.stopEvent();
13019         }
13020         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13021         {
13022           var v = this.field.dom.value, pageNum; 
13023           var increment = (e.shiftKey) ? 10 : 1;
13024           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13025             increment *= -1;
13026           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13027             this.field.dom.value = d.activePage;
13028             return;
13029           }
13030           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13031           {
13032             this.field.dom.value = parseInt(v, 10) + increment;
13033             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13034             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13035           }
13036           e.stopEvent();
13037         }
13038     },
13039
13040     // private
13041     beforeLoad : function(){
13042         if(this.loading){
13043             this.loading.disable();
13044         }
13045     },
13046
13047     // private
13048     onClick : function(which){
13049         var ds = this.ds;
13050         switch(which){
13051             case "first":
13052                 ds.load({params:{start: 0, limit: this.pageSize}});
13053             break;
13054             case "prev":
13055                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13056             break;
13057             case "next":
13058                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13059             break;
13060             case "last":
13061                 var total = ds.getTotalCount();
13062                 var extra = total % this.pageSize;
13063                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13064                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13065             break;
13066             case "refresh":
13067                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13068             break;
13069         }
13070     },
13071
13072     /**
13073      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13074      * @param {Roo.data.Store} store The data store to unbind
13075      */
13076     unbind : function(ds){
13077         ds.un("beforeload", this.beforeLoad, this);
13078         ds.un("load", this.onLoad, this);
13079         ds.un("loadexception", this.onLoadError, this);
13080         ds.un("remove", this.updateInfo, this);
13081         ds.un("add", this.updateInfo, this);
13082         this.ds = undefined;
13083     },
13084
13085     /**
13086      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13087      * @param {Roo.data.Store} store The data store to bind
13088      */
13089     bind : function(ds){
13090         ds.on("beforeload", this.beforeLoad, this);
13091         ds.on("load", this.onLoad, this);
13092         ds.on("loadexception", this.onLoadError, this);
13093         ds.on("remove", this.updateInfo, this);
13094         ds.on("add", this.updateInfo, this);
13095         this.ds = ds;
13096     }
13097 });/*
13098  * Based on:
13099  * Ext JS Library 1.1.1
13100  * Copyright(c) 2006-2007, Ext JS, LLC.
13101  *
13102  * Originally Released Under LGPL - original licence link has changed is not relivant.
13103  *
13104  * Fork - LGPL
13105  * <script type="text/javascript">
13106  */
13107
13108 /**
13109  * @class Roo.Resizable
13110  * @extends Roo.util.Observable
13111  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13112  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13113  * 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
13114  * the element will be wrapped for you automatically.</p>
13115  * <p>Here is the list of valid resize handles:</p>
13116  * <pre>
13117 Value   Description
13118 ------  -------------------
13119  'n'     north
13120  's'     south
13121  'e'     east
13122  'w'     west
13123  'nw'    northwest
13124  'sw'    southwest
13125  'se'    southeast
13126  'ne'    northeast
13127  'hd'    horizontal drag
13128  'all'   all
13129 </pre>
13130  * <p>Here's an example showing the creation of a typical Resizable:</p>
13131  * <pre><code>
13132 var resizer = new Roo.Resizable("element-id", {
13133     handles: 'all',
13134     minWidth: 200,
13135     minHeight: 100,
13136     maxWidth: 500,
13137     maxHeight: 400,
13138     pinned: true
13139 });
13140 resizer.on("resize", myHandler);
13141 </code></pre>
13142  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13143  * resizer.east.setDisplayed(false);</p>
13144  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13145  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13146  * resize operation's new size (defaults to [0, 0])
13147  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13148  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13149  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13150  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13151  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13152  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13153  * @cfg {Number} width The width of the element in pixels (defaults to null)
13154  * @cfg {Number} height The height of the element in pixels (defaults to null)
13155  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13156  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13157  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13158  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13159  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13160  * in favor of the handles config option (defaults to false)
13161  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13162  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13163  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13164  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13165  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13166  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13167  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13168  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13169  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13170  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13171  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13172  * @constructor
13173  * Create a new resizable component
13174  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13175  * @param {Object} config configuration options
13176   */
13177 Roo.Resizable = function(el, config)
13178 {
13179     this.el = Roo.get(el);
13180
13181     if(config && config.wrap){
13182         config.resizeChild = this.el;
13183         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13184         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13185         this.el.setStyle("overflow", "hidden");
13186         this.el.setPositioning(config.resizeChild.getPositioning());
13187         config.resizeChild.clearPositioning();
13188         if(!config.width || !config.height){
13189             var csize = config.resizeChild.getSize();
13190             this.el.setSize(csize.width, csize.height);
13191         }
13192         if(config.pinned && !config.adjustments){
13193             config.adjustments = "auto";
13194         }
13195     }
13196
13197     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13198     this.proxy.unselectable();
13199     this.proxy.enableDisplayMode('block');
13200
13201     Roo.apply(this, config);
13202
13203     if(this.pinned){
13204         this.disableTrackOver = true;
13205         this.el.addClass("x-resizable-pinned");
13206     }
13207     // if the element isn't positioned, make it relative
13208     var position = this.el.getStyle("position");
13209     if(position != "absolute" && position != "fixed"){
13210         this.el.setStyle("position", "relative");
13211     }
13212     if(!this.handles){ // no handles passed, must be legacy style
13213         this.handles = 's,e,se';
13214         if(this.multiDirectional){
13215             this.handles += ',n,w';
13216         }
13217     }
13218     if(this.handles == "all"){
13219         this.handles = "n s e w ne nw se sw";
13220     }
13221     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13222     var ps = Roo.Resizable.positions;
13223     for(var i = 0, len = hs.length; i < len; i++){
13224         if(hs[i] && ps[hs[i]]){
13225             var pos = ps[hs[i]];
13226             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13227         }
13228     }
13229     // legacy
13230     this.corner = this.southeast;
13231     
13232     // updateBox = the box can move..
13233     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13234         this.updateBox = true;
13235     }
13236
13237     this.activeHandle = null;
13238
13239     if(this.resizeChild){
13240         if(typeof this.resizeChild == "boolean"){
13241             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13242         }else{
13243             this.resizeChild = Roo.get(this.resizeChild, true);
13244         }
13245     }
13246     
13247     if(this.adjustments == "auto"){
13248         var rc = this.resizeChild;
13249         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13250         if(rc && (hw || hn)){
13251             rc.position("relative");
13252             rc.setLeft(hw ? hw.el.getWidth() : 0);
13253             rc.setTop(hn ? hn.el.getHeight() : 0);
13254         }
13255         this.adjustments = [
13256             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13257             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13258         ];
13259     }
13260
13261     if(this.draggable){
13262         this.dd = this.dynamic ?
13263             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13264         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13265     }
13266
13267     // public events
13268     this.addEvents({
13269         /**
13270          * @event beforeresize
13271          * Fired before resize is allowed. Set enabled to false to cancel resize.
13272          * @param {Roo.Resizable} this
13273          * @param {Roo.EventObject} e The mousedown event
13274          */
13275         "beforeresize" : true,
13276         /**
13277          * @event resize
13278          * Fired after a resize.
13279          * @param {Roo.Resizable} this
13280          * @param {Number} width The new width
13281          * @param {Number} height The new height
13282          * @param {Roo.EventObject} e The mouseup event
13283          */
13284         "resize" : true
13285     });
13286
13287     if(this.width !== null && this.height !== null){
13288         this.resizeTo(this.width, this.height);
13289     }else{
13290         this.updateChildSize();
13291     }
13292     if(Roo.isIE){
13293         this.el.dom.style.zoom = 1;
13294     }
13295     Roo.Resizable.superclass.constructor.call(this);
13296 };
13297
13298 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13299         resizeChild : false,
13300         adjustments : [0, 0],
13301         minWidth : 5,
13302         minHeight : 5,
13303         maxWidth : 10000,
13304         maxHeight : 10000,
13305         enabled : true,
13306         animate : false,
13307         duration : .35,
13308         dynamic : false,
13309         handles : false,
13310         multiDirectional : false,
13311         disableTrackOver : false,
13312         easing : 'easeOutStrong',
13313         widthIncrement : 0,
13314         heightIncrement : 0,
13315         pinned : false,
13316         width : null,
13317         height : null,
13318         preserveRatio : false,
13319         transparent: false,
13320         minX: 0,
13321         minY: 0,
13322         draggable: false,
13323
13324         /**
13325          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13326          */
13327         constrainTo: undefined,
13328         /**
13329          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13330          */
13331         resizeRegion: undefined,
13332
13333
13334     /**
13335      * Perform a manual resize
13336      * @param {Number} width
13337      * @param {Number} height
13338      */
13339     resizeTo : function(width, height){
13340         this.el.setSize(width, height);
13341         this.updateChildSize();
13342         this.fireEvent("resize", this, width, height, null);
13343     },
13344
13345     // private
13346     startSizing : function(e, handle){
13347         this.fireEvent("beforeresize", this, e);
13348         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13349
13350             if(!this.overlay){
13351                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13352                 this.overlay.unselectable();
13353                 this.overlay.enableDisplayMode("block");
13354                 this.overlay.on("mousemove", this.onMouseMove, this);
13355                 this.overlay.on("mouseup", this.onMouseUp, this);
13356             }
13357             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13358
13359             this.resizing = true;
13360             this.startBox = this.el.getBox();
13361             this.startPoint = e.getXY();
13362             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13363                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13364
13365             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13366             this.overlay.show();
13367
13368             if(this.constrainTo) {
13369                 var ct = Roo.get(this.constrainTo);
13370                 this.resizeRegion = ct.getRegion().adjust(
13371                     ct.getFrameWidth('t'),
13372                     ct.getFrameWidth('l'),
13373                     -ct.getFrameWidth('b'),
13374                     -ct.getFrameWidth('r')
13375                 );
13376             }
13377
13378             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13379             this.proxy.show();
13380             this.proxy.setBox(this.startBox);
13381             if(!this.dynamic){
13382                 this.proxy.setStyle('visibility', 'visible');
13383             }
13384         }
13385     },
13386
13387     // private
13388     onMouseDown : function(handle, e){
13389         if(this.enabled){
13390             e.stopEvent();
13391             this.activeHandle = handle;
13392             this.startSizing(e, handle);
13393         }
13394     },
13395
13396     // private
13397     onMouseUp : function(e){
13398         var size = this.resizeElement();
13399         this.resizing = false;
13400         this.handleOut();
13401         this.overlay.hide();
13402         this.proxy.hide();
13403         this.fireEvent("resize", this, size.width, size.height, e);
13404     },
13405
13406     // private
13407     updateChildSize : function(){
13408         if(this.resizeChild){
13409             var el = this.el;
13410             var child = this.resizeChild;
13411             var adj = this.adjustments;
13412             if(el.dom.offsetWidth){
13413                 var b = el.getSize(true);
13414                 child.setSize(b.width+adj[0], b.height+adj[1]);
13415             }
13416             // Second call here for IE
13417             // The first call enables instant resizing and
13418             // the second call corrects scroll bars if they
13419             // exist
13420             if(Roo.isIE){
13421                 setTimeout(function(){
13422                     if(el.dom.offsetWidth){
13423                         var b = el.getSize(true);
13424                         child.setSize(b.width+adj[0], b.height+adj[1]);
13425                     }
13426                 }, 10);
13427             }
13428         }
13429     },
13430
13431     // private
13432     snap : function(value, inc, min){
13433         if(!inc || !value) return value;
13434         var newValue = value;
13435         var m = value % inc;
13436         if(m > 0){
13437             if(m > (inc/2)){
13438                 newValue = value + (inc-m);
13439             }else{
13440                 newValue = value - m;
13441             }
13442         }
13443         return Math.max(min, newValue);
13444     },
13445
13446     // private
13447     resizeElement : function(){
13448         var box = this.proxy.getBox();
13449         if(this.updateBox){
13450             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13451         }else{
13452             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13453         }
13454         this.updateChildSize();
13455         if(!this.dynamic){
13456             this.proxy.hide();
13457         }
13458         return box;
13459     },
13460
13461     // private
13462     constrain : function(v, diff, m, mx){
13463         if(v - diff < m){
13464             diff = v - m;
13465         }else if(v - diff > mx){
13466             diff = mx - v;
13467         }
13468         return diff;
13469     },
13470
13471     // private
13472     onMouseMove : function(e){
13473         if(this.enabled){
13474             try{// try catch so if something goes wrong the user doesn't get hung
13475
13476             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13477                 return;
13478             }
13479
13480             //var curXY = this.startPoint;
13481             var curSize = this.curSize || this.startBox;
13482             var x = this.startBox.x, y = this.startBox.y;
13483             var ox = x, oy = y;
13484             var w = curSize.width, h = curSize.height;
13485             var ow = w, oh = h;
13486             var mw = this.minWidth, mh = this.minHeight;
13487             var mxw = this.maxWidth, mxh = this.maxHeight;
13488             var wi = this.widthIncrement;
13489             var hi = this.heightIncrement;
13490
13491             var eventXY = e.getXY();
13492             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13493             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13494
13495             var pos = this.activeHandle.position;
13496
13497             switch(pos){
13498                 case "east":
13499                     w += diffX;
13500                     w = Math.min(Math.max(mw, w), mxw);
13501                     break;
13502              
13503                 case "south":
13504                     h += diffY;
13505                     h = Math.min(Math.max(mh, h), mxh);
13506                     break;
13507                 case "southeast":
13508                     w += diffX;
13509                     h += diffY;
13510                     w = Math.min(Math.max(mw, w), mxw);
13511                     h = Math.min(Math.max(mh, h), mxh);
13512                     break;
13513                 case "north":
13514                     diffY = this.constrain(h, diffY, mh, mxh);
13515                     y += diffY;
13516                     h -= diffY;
13517                     break;
13518                 case "hdrag":
13519                     
13520                     if (wi) {
13521                         var adiffX = Math.abs(diffX);
13522                         var sub = (adiffX % wi); // how much 
13523                         if (sub > (wi/2)) { // far enough to snap
13524                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13525                         } else {
13526                             // remove difference.. 
13527                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13528                         }
13529                     }
13530                     x += diffX;
13531                     x = Math.max(this.minX, x);
13532                     break;
13533                 case "west":
13534                     diffX = this.constrain(w, diffX, mw, mxw);
13535                     x += diffX;
13536                     w -= diffX;
13537                     break;
13538                 case "northeast":
13539                     w += diffX;
13540                     w = Math.min(Math.max(mw, w), mxw);
13541                     diffY = this.constrain(h, diffY, mh, mxh);
13542                     y += diffY;
13543                     h -= diffY;
13544                     break;
13545                 case "northwest":
13546                     diffX = this.constrain(w, diffX, mw, mxw);
13547                     diffY = this.constrain(h, diffY, mh, mxh);
13548                     y += diffY;
13549                     h -= diffY;
13550                     x += diffX;
13551                     w -= diffX;
13552                     break;
13553                case "southwest":
13554                     diffX = this.constrain(w, diffX, mw, mxw);
13555                     h += diffY;
13556                     h = Math.min(Math.max(mh, h), mxh);
13557                     x += diffX;
13558                     w -= diffX;
13559                     break;
13560             }
13561
13562             var sw = this.snap(w, wi, mw);
13563             var sh = this.snap(h, hi, mh);
13564             if(sw != w || sh != h){
13565                 switch(pos){
13566                     case "northeast":
13567                         y -= sh - h;
13568                     break;
13569                     case "north":
13570                         y -= sh - h;
13571                         break;
13572                     case "southwest":
13573                         x -= sw - w;
13574                     break;
13575                     case "west":
13576                         x -= sw - w;
13577                         break;
13578                     case "northwest":
13579                         x -= sw - w;
13580                         y -= sh - h;
13581                     break;
13582                 }
13583                 w = sw;
13584                 h = sh;
13585             }
13586
13587             if(this.preserveRatio){
13588                 switch(pos){
13589                     case "southeast":
13590                     case "east":
13591                         h = oh * (w/ow);
13592                         h = Math.min(Math.max(mh, h), mxh);
13593                         w = ow * (h/oh);
13594                        break;
13595                     case "south":
13596                         w = ow * (h/oh);
13597                         w = Math.min(Math.max(mw, w), mxw);
13598                         h = oh * (w/ow);
13599                         break;
13600                     case "northeast":
13601                         w = ow * (h/oh);
13602                         w = Math.min(Math.max(mw, w), mxw);
13603                         h = oh * (w/ow);
13604                     break;
13605                     case "north":
13606                         var tw = w;
13607                         w = ow * (h/oh);
13608                         w = Math.min(Math.max(mw, w), mxw);
13609                         h = oh * (w/ow);
13610                         x += (tw - w) / 2;
13611                         break;
13612                     case "southwest":
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         x += tw - w;
13618                         break;
13619                     case "west":
13620                         var th = h;
13621                         h = oh * (w/ow);
13622                         h = Math.min(Math.max(mh, h), mxh);
13623                         y += (th - h) / 2;
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                        break;
13628                     case "northwest":
13629                         var tw = w;
13630                         var th = h;
13631                         h = oh * (w/ow);
13632                         h = Math.min(Math.max(mh, h), mxh);
13633                         w = ow * (h/oh);
13634                         y += th - h;
13635                         x += tw - w;
13636                        break;
13637
13638                 }
13639             }
13640             if (pos == 'hdrag') {
13641                 w = ow;
13642             }
13643             this.proxy.setBounds(x, y, w, h);
13644             if(this.dynamic){
13645                 this.resizeElement();
13646             }
13647             }catch(e){}
13648         }
13649     },
13650
13651     // private
13652     handleOver : function(){
13653         if(this.enabled){
13654             this.el.addClass("x-resizable-over");
13655         }
13656     },
13657
13658     // private
13659     handleOut : function(){
13660         if(!this.resizing){
13661             this.el.removeClass("x-resizable-over");
13662         }
13663     },
13664
13665     /**
13666      * Returns the element this component is bound to.
13667      * @return {Roo.Element}
13668      */
13669     getEl : function(){
13670         return this.el;
13671     },
13672
13673     /**
13674      * Returns the resizeChild element (or null).
13675      * @return {Roo.Element}
13676      */
13677     getResizeChild : function(){
13678         return this.resizeChild;
13679     },
13680
13681     /**
13682      * Destroys this resizable. If the element was wrapped and
13683      * removeEl is not true then the element remains.
13684      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13685      */
13686     destroy : function(removeEl){
13687         this.proxy.remove();
13688         if(this.overlay){
13689             this.overlay.removeAllListeners();
13690             this.overlay.remove();
13691         }
13692         var ps = Roo.Resizable.positions;
13693         for(var k in ps){
13694             if(typeof ps[k] != "function" && this[ps[k]]){
13695                 var h = this[ps[k]];
13696                 h.el.removeAllListeners();
13697                 h.el.remove();
13698             }
13699         }
13700         if(removeEl){
13701             this.el.update("");
13702             this.el.remove();
13703         }
13704     }
13705 });
13706
13707 // private
13708 // hash to map config positions to true positions
13709 Roo.Resizable.positions = {
13710     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13711     hd: "hdrag"
13712 };
13713
13714 // private
13715 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13716     if(!this.tpl){
13717         // only initialize the template if resizable is used
13718         var tpl = Roo.DomHelper.createTemplate(
13719             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13720         );
13721         tpl.compile();
13722         Roo.Resizable.Handle.prototype.tpl = tpl;
13723     }
13724     this.position = pos;
13725     this.rz = rz;
13726     // show north drag fro topdra
13727     var handlepos = pos == 'hdrag' ? 'north' : pos;
13728     
13729     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13730     if (pos == 'hdrag') {
13731         this.el.setStyle('cursor', 'pointer');
13732     }
13733     this.el.unselectable();
13734     if(transparent){
13735         this.el.setOpacity(0);
13736     }
13737     this.el.on("mousedown", this.onMouseDown, this);
13738     if(!disableTrackOver){
13739         this.el.on("mouseover", this.onMouseOver, this);
13740         this.el.on("mouseout", this.onMouseOut, this);
13741     }
13742 };
13743
13744 // private
13745 Roo.Resizable.Handle.prototype = {
13746     afterResize : function(rz){
13747         // do nothing
13748     },
13749     // private
13750     onMouseDown : function(e){
13751         this.rz.onMouseDown(this, e);
13752     },
13753     // private
13754     onMouseOver : function(e){
13755         this.rz.handleOver(this, e);
13756     },
13757     // private
13758     onMouseOut : function(e){
13759         this.rz.handleOut(this, e);
13760     }
13761 };/*
13762  * Based on:
13763  * Ext JS Library 1.1.1
13764  * Copyright(c) 2006-2007, Ext JS, LLC.
13765  *
13766  * Originally Released Under LGPL - original licence link has changed is not relivant.
13767  *
13768  * Fork - LGPL
13769  * <script type="text/javascript">
13770  */
13771
13772 /**
13773  * @class Roo.Editor
13774  * @extends Roo.Component
13775  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13776  * @constructor
13777  * Create a new Editor
13778  * @param {Roo.form.Field} field The Field object (or descendant)
13779  * @param {Object} config The config object
13780  */
13781 Roo.Editor = function(field, config){
13782     Roo.Editor.superclass.constructor.call(this, config);
13783     this.field = field;
13784     this.addEvents({
13785         /**
13786              * @event beforestartedit
13787              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13788              * false from the handler of this event.
13789              * @param {Editor} this
13790              * @param {Roo.Element} boundEl The underlying element bound to this editor
13791              * @param {Mixed} value The field value being set
13792              */
13793         "beforestartedit" : true,
13794         /**
13795              * @event startedit
13796              * Fires when this editor is displayed
13797              * @param {Roo.Element} boundEl The underlying element bound to this editor
13798              * @param {Mixed} value The starting field value
13799              */
13800         "startedit" : true,
13801         /**
13802              * @event beforecomplete
13803              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13804              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13805              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13806              * event will not fire since no edit actually occurred.
13807              * @param {Editor} this
13808              * @param {Mixed} value The current field value
13809              * @param {Mixed} startValue The original field value
13810              */
13811         "beforecomplete" : true,
13812         /**
13813              * @event complete
13814              * Fires after editing is complete and any changed value has been written to the underlying field.
13815              * @param {Editor} this
13816              * @param {Mixed} value The current field value
13817              * @param {Mixed} startValue The original field value
13818              */
13819         "complete" : true,
13820         /**
13821          * @event specialkey
13822          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13823          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13824          * @param {Roo.form.Field} this
13825          * @param {Roo.EventObject} e The event object
13826          */
13827         "specialkey" : true
13828     });
13829 };
13830
13831 Roo.extend(Roo.Editor, Roo.Component, {
13832     /**
13833      * @cfg {Boolean/String} autosize
13834      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13835      * or "height" to adopt the height only (defaults to false)
13836      */
13837     /**
13838      * @cfg {Boolean} revertInvalid
13839      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13840      * validation fails (defaults to true)
13841      */
13842     /**
13843      * @cfg {Boolean} ignoreNoChange
13844      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13845      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13846      * will never be ignored.
13847      */
13848     /**
13849      * @cfg {Boolean} hideEl
13850      * False to keep the bound element visible while the editor is displayed (defaults to true)
13851      */
13852     /**
13853      * @cfg {Mixed} value
13854      * The data value of the underlying field (defaults to "")
13855      */
13856     value : "",
13857     /**
13858      * @cfg {String} alignment
13859      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13860      */
13861     alignment: "c-c?",
13862     /**
13863      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13864      * for bottom-right shadow (defaults to "frame")
13865      */
13866     shadow : "frame",
13867     /**
13868      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13869      */
13870     constrain : false,
13871     /**
13872      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13873      */
13874     completeOnEnter : false,
13875     /**
13876      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13877      */
13878     cancelOnEsc : false,
13879     /**
13880      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13881      */
13882     updateEl : false,
13883
13884     // private
13885     onRender : function(ct, position){
13886         this.el = new Roo.Layer({
13887             shadow: this.shadow,
13888             cls: "x-editor",
13889             parentEl : ct,
13890             shim : this.shim,
13891             shadowOffset:4,
13892             id: this.id,
13893             constrain: this.constrain
13894         });
13895         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13896         if(this.field.msgTarget != 'title'){
13897             this.field.msgTarget = 'qtip';
13898         }
13899         this.field.render(this.el);
13900         if(Roo.isGecko){
13901             this.field.el.dom.setAttribute('autocomplete', 'off');
13902         }
13903         this.field.on("specialkey", this.onSpecialKey, this);
13904         if(this.swallowKeys){
13905             this.field.el.swallowEvent(['keydown','keypress']);
13906         }
13907         this.field.show();
13908         this.field.on("blur", this.onBlur, this);
13909         if(this.field.grow){
13910             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13911         }
13912     },
13913
13914     onSpecialKey : function(field, e){
13915         //Roo.log('editor onSpecialKey');
13916         if(this.completeOnEnter && e.getKey() == e.ENTER){
13917             e.stopEvent();
13918             this.completeEdit();
13919         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13920             this.cancelEdit();
13921         }else{
13922             this.fireEvent('specialkey', field, e);
13923         }
13924     },
13925
13926     /**
13927      * Starts the editing process and shows the editor.
13928      * @param {String/HTMLElement/Element} el The element to edit
13929      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13930       * to the innerHTML of el.
13931      */
13932     startEdit : function(el, value){
13933         if(this.editing){
13934             this.completeEdit();
13935         }
13936         this.boundEl = Roo.get(el);
13937         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13938         if(!this.rendered){
13939             this.render(this.parentEl || document.body);
13940         }
13941         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13942             return;
13943         }
13944         this.startValue = v;
13945         this.field.setValue(v);
13946         if(this.autoSize){
13947             var sz = this.boundEl.getSize();
13948             switch(this.autoSize){
13949                 case "width":
13950                 this.setSize(sz.width,  "");
13951                 break;
13952                 case "height":
13953                 this.setSize("",  sz.height);
13954                 break;
13955                 default:
13956                 this.setSize(sz.width,  sz.height);
13957             }
13958         }
13959         this.el.alignTo(this.boundEl, this.alignment);
13960         this.editing = true;
13961         if(Roo.QuickTips){
13962             Roo.QuickTips.disable();
13963         }
13964         this.show();
13965     },
13966
13967     /**
13968      * Sets the height and width of this editor.
13969      * @param {Number} width The new width
13970      * @param {Number} height The new height
13971      */
13972     setSize : function(w, h){
13973         this.field.setSize(w, h);
13974         if(this.el){
13975             this.el.sync();
13976         }
13977     },
13978
13979     /**
13980      * Realigns the editor to the bound field based on the current alignment config value.
13981      */
13982     realign : function(){
13983         this.el.alignTo(this.boundEl, this.alignment);
13984     },
13985
13986     /**
13987      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13988      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13989      */
13990     completeEdit : function(remainVisible){
13991         if(!this.editing){
13992             return;
13993         }
13994         var v = this.getValue();
13995         if(this.revertInvalid !== false && !this.field.isValid()){
13996             v = this.startValue;
13997             this.cancelEdit(true);
13998         }
13999         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14000             this.editing = false;
14001             this.hide();
14002             return;
14003         }
14004         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14005             this.editing = false;
14006             if(this.updateEl && this.boundEl){
14007                 this.boundEl.update(v);
14008             }
14009             if(remainVisible !== true){
14010                 this.hide();
14011             }
14012             this.fireEvent("complete", this, v, this.startValue);
14013         }
14014     },
14015
14016     // private
14017     onShow : function(){
14018         this.el.show();
14019         if(this.hideEl !== false){
14020             this.boundEl.hide();
14021         }
14022         this.field.show();
14023         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14024             this.fixIEFocus = true;
14025             this.deferredFocus.defer(50, this);
14026         }else{
14027             this.field.focus();
14028         }
14029         this.fireEvent("startedit", this.boundEl, this.startValue);
14030     },
14031
14032     deferredFocus : function(){
14033         if(this.editing){
14034             this.field.focus();
14035         }
14036     },
14037
14038     /**
14039      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14040      * reverted to the original starting value.
14041      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14042      * cancel (defaults to false)
14043      */
14044     cancelEdit : function(remainVisible){
14045         if(this.editing){
14046             this.setValue(this.startValue);
14047             if(remainVisible !== true){
14048                 this.hide();
14049             }
14050         }
14051     },
14052
14053     // private
14054     onBlur : function(){
14055         if(this.allowBlur !== true && this.editing){
14056             this.completeEdit();
14057         }
14058     },
14059
14060     // private
14061     onHide : function(){
14062         if(this.editing){
14063             this.completeEdit();
14064             return;
14065         }
14066         this.field.blur();
14067         if(this.field.collapse){
14068             this.field.collapse();
14069         }
14070         this.el.hide();
14071         if(this.hideEl !== false){
14072             this.boundEl.show();
14073         }
14074         if(Roo.QuickTips){
14075             Roo.QuickTips.enable();
14076         }
14077     },
14078
14079     /**
14080      * Sets the data value of the editor
14081      * @param {Mixed} value Any valid value supported by the underlying field
14082      */
14083     setValue : function(v){
14084         this.field.setValue(v);
14085     },
14086
14087     /**
14088      * Gets the data value of the editor
14089      * @return {Mixed} The data value
14090      */
14091     getValue : function(){
14092         return this.field.getValue();
14093     }
14094 });/*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104  
14105 /**
14106  * @class Roo.BasicDialog
14107  * @extends Roo.util.Observable
14108  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14109  * <pre><code>
14110 var dlg = new Roo.BasicDialog("my-dlg", {
14111     height: 200,
14112     width: 300,
14113     minHeight: 100,
14114     minWidth: 150,
14115     modal: true,
14116     proxyDrag: true,
14117     shadow: true
14118 });
14119 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14120 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14121 dlg.addButton('Cancel', dlg.hide, dlg);
14122 dlg.show();
14123 </code></pre>
14124   <b>A Dialog should always be a direct child of the body element.</b>
14125  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14126  * @cfg {String} title Default text to display in the title bar (defaults to null)
14127  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14128  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14129  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14130  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14131  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14132  * (defaults to null with no animation)
14133  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14134  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14135  * property for valid values (defaults to 'all')
14136  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14137  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14138  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14139  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14140  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14141  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14142  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14143  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14144  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14145  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14146  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14147  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14148  * draggable = true (defaults to false)
14149  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14150  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14151  * shadow (defaults to false)
14152  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14153  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14154  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14155  * @cfg {Array} buttons Array of buttons
14156  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14157  * @constructor
14158  * Create a new BasicDialog.
14159  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14160  * @param {Object} config Configuration options
14161  */
14162 Roo.BasicDialog = function(el, config){
14163     this.el = Roo.get(el);
14164     var dh = Roo.DomHelper;
14165     if(!this.el && config && config.autoCreate){
14166         if(typeof config.autoCreate == "object"){
14167             if(!config.autoCreate.id){
14168                 config.autoCreate.id = el;
14169             }
14170             this.el = dh.append(document.body,
14171                         config.autoCreate, true);
14172         }else{
14173             this.el = dh.append(document.body,
14174                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14175         }
14176     }
14177     el = this.el;
14178     el.setDisplayed(true);
14179     el.hide = this.hideAction;
14180     this.id = el.id;
14181     el.addClass("x-dlg");
14182
14183     Roo.apply(this, config);
14184
14185     this.proxy = el.createProxy("x-dlg-proxy");
14186     this.proxy.hide = this.hideAction;
14187     this.proxy.setOpacity(.5);
14188     this.proxy.hide();
14189
14190     if(config.width){
14191         el.setWidth(config.width);
14192     }
14193     if(config.height){
14194         el.setHeight(config.height);
14195     }
14196     this.size = el.getSize();
14197     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14198         this.xy = [config.x,config.y];
14199     }else{
14200         this.xy = el.getCenterXY(true);
14201     }
14202     /** The header element @type Roo.Element */
14203     this.header = el.child("> .x-dlg-hd");
14204     /** The body element @type Roo.Element */
14205     this.body = el.child("> .x-dlg-bd");
14206     /** The footer element @type Roo.Element */
14207     this.footer = el.child("> .x-dlg-ft");
14208
14209     if(!this.header){
14210         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14211     }
14212     if(!this.body){
14213         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14214     }
14215
14216     this.header.unselectable();
14217     if(this.title){
14218         this.header.update(this.title);
14219     }
14220     // this element allows the dialog to be focused for keyboard event
14221     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14222     this.focusEl.swallowEvent("click", true);
14223
14224     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14225
14226     // wrap the body and footer for special rendering
14227     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14228     if(this.footer){
14229         this.bwrap.dom.appendChild(this.footer.dom);
14230     }
14231
14232     this.bg = this.el.createChild({
14233         tag: "div", cls:"x-dlg-bg",
14234         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14235     });
14236     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14237
14238
14239     if(this.autoScroll !== false && !this.autoTabs){
14240         this.body.setStyle("overflow", "auto");
14241     }
14242
14243     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14244
14245     if(this.closable !== false){
14246         this.el.addClass("x-dlg-closable");
14247         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14248         this.close.on("click", this.closeClick, this);
14249         this.close.addClassOnOver("x-dlg-close-over");
14250     }
14251     if(this.collapsible !== false){
14252         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14253         this.collapseBtn.on("click", this.collapseClick, this);
14254         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14255         this.header.on("dblclick", this.collapseClick, this);
14256     }
14257     if(this.resizable !== false){
14258         this.el.addClass("x-dlg-resizable");
14259         this.resizer = new Roo.Resizable(el, {
14260             minWidth: this.minWidth || 80,
14261             minHeight:this.minHeight || 80,
14262             handles: this.resizeHandles || "all",
14263             pinned: true
14264         });
14265         this.resizer.on("beforeresize", this.beforeResize, this);
14266         this.resizer.on("resize", this.onResize, this);
14267     }
14268     if(this.draggable !== false){
14269         el.addClass("x-dlg-draggable");
14270         if (!this.proxyDrag) {
14271             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14272         }
14273         else {
14274             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14275         }
14276         dd.setHandleElId(this.header.id);
14277         dd.endDrag = this.endMove.createDelegate(this);
14278         dd.startDrag = this.startMove.createDelegate(this);
14279         dd.onDrag = this.onDrag.createDelegate(this);
14280         dd.scroll = false;
14281         this.dd = dd;
14282     }
14283     if(this.modal){
14284         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14285         this.mask.enableDisplayMode("block");
14286         this.mask.hide();
14287         this.el.addClass("x-dlg-modal");
14288     }
14289     if(this.shadow){
14290         this.shadow = new Roo.Shadow({
14291             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14292             offset : this.shadowOffset
14293         });
14294     }else{
14295         this.shadowOffset = 0;
14296     }
14297     if(Roo.useShims && this.shim !== false){
14298         this.shim = this.el.createShim();
14299         this.shim.hide = this.hideAction;
14300         this.shim.hide();
14301     }else{
14302         this.shim = false;
14303     }
14304     if(this.autoTabs){
14305         this.initTabs();
14306     }
14307     if (this.buttons) { 
14308         var bts= this.buttons;
14309         this.buttons = [];
14310         Roo.each(bts, function(b) {
14311             this.addButton(b);
14312         }, this);
14313     }
14314     
14315     
14316     this.addEvents({
14317         /**
14318          * @event keydown
14319          * Fires when a key is pressed
14320          * @param {Roo.BasicDialog} this
14321          * @param {Roo.EventObject} e
14322          */
14323         "keydown" : true,
14324         /**
14325          * @event move
14326          * Fires when this dialog is moved by the user.
14327          * @param {Roo.BasicDialog} this
14328          * @param {Number} x The new page X
14329          * @param {Number} y The new page Y
14330          */
14331         "move" : true,
14332         /**
14333          * @event resize
14334          * Fires when this dialog is resized by the user.
14335          * @param {Roo.BasicDialog} this
14336          * @param {Number} width The new width
14337          * @param {Number} height The new height
14338          */
14339         "resize" : true,
14340         /**
14341          * @event beforehide
14342          * Fires before this dialog is hidden.
14343          * @param {Roo.BasicDialog} this
14344          */
14345         "beforehide" : true,
14346         /**
14347          * @event hide
14348          * Fires when this dialog is hidden.
14349          * @param {Roo.BasicDialog} this
14350          */
14351         "hide" : true,
14352         /**
14353          * @event beforeshow
14354          * Fires before this dialog is shown.
14355          * @param {Roo.BasicDialog} this
14356          */
14357         "beforeshow" : true,
14358         /**
14359          * @event show
14360          * Fires when this dialog is shown.
14361          * @param {Roo.BasicDialog} this
14362          */
14363         "show" : true
14364     });
14365     el.on("keydown", this.onKeyDown, this);
14366     el.on("mousedown", this.toFront, this);
14367     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14368     this.el.hide();
14369     Roo.DialogManager.register(this);
14370     Roo.BasicDialog.superclass.constructor.call(this);
14371 };
14372
14373 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14374     shadowOffset: Roo.isIE ? 6 : 5,
14375     minHeight: 80,
14376     minWidth: 200,
14377     minButtonWidth: 75,
14378     defaultButton: null,
14379     buttonAlign: "right",
14380     tabTag: 'div',
14381     firstShow: true,
14382
14383     /**
14384      * Sets the dialog title text
14385      * @param {String} text The title text to display
14386      * @return {Roo.BasicDialog} this
14387      */
14388     setTitle : function(text){
14389         this.header.update(text);
14390         return this;
14391     },
14392
14393     // private
14394     closeClick : function(){
14395         this.hide();
14396     },
14397
14398     // private
14399     collapseClick : function(){
14400         this[this.collapsed ? "expand" : "collapse"]();
14401     },
14402
14403     /**
14404      * Collapses the dialog to its minimized state (only the title bar is visible).
14405      * Equivalent to the user clicking the collapse dialog button.
14406      */
14407     collapse : function(){
14408         if(!this.collapsed){
14409             this.collapsed = true;
14410             this.el.addClass("x-dlg-collapsed");
14411             this.restoreHeight = this.el.getHeight();
14412             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14413         }
14414     },
14415
14416     /**
14417      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14418      * clicking the expand dialog button.
14419      */
14420     expand : function(){
14421         if(this.collapsed){
14422             this.collapsed = false;
14423             this.el.removeClass("x-dlg-collapsed");
14424             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14425         }
14426     },
14427
14428     /**
14429      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14430      * @return {Roo.TabPanel} The tabs component
14431      */
14432     initTabs : function(){
14433         var tabs = this.getTabs();
14434         while(tabs.getTab(0)){
14435             tabs.removeTab(0);
14436         }
14437         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14438             var dom = el.dom;
14439             tabs.addTab(Roo.id(dom), dom.title);
14440             dom.title = "";
14441         });
14442         tabs.activate(0);
14443         return tabs;
14444     },
14445
14446     // private
14447     beforeResize : function(){
14448         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14449     },
14450
14451     // private
14452     onResize : function(){
14453         this.refreshSize();
14454         this.syncBodyHeight();
14455         this.adjustAssets();
14456         this.focus();
14457         this.fireEvent("resize", this, this.size.width, this.size.height);
14458     },
14459
14460     // private
14461     onKeyDown : function(e){
14462         if(this.isVisible()){
14463             this.fireEvent("keydown", this, e);
14464         }
14465     },
14466
14467     /**
14468      * Resizes the dialog.
14469      * @param {Number} width
14470      * @param {Number} height
14471      * @return {Roo.BasicDialog} this
14472      */
14473     resizeTo : function(width, height){
14474         this.el.setSize(width, height);
14475         this.size = {width: width, height: height};
14476         this.syncBodyHeight();
14477         if(this.fixedcenter){
14478             this.center();
14479         }
14480         if(this.isVisible()){
14481             this.constrainXY();
14482             this.adjustAssets();
14483         }
14484         this.fireEvent("resize", this, width, height);
14485         return this;
14486     },
14487
14488
14489     /**
14490      * Resizes the dialog to fit the specified content size.
14491      * @param {Number} width
14492      * @param {Number} height
14493      * @return {Roo.BasicDialog} this
14494      */
14495     setContentSize : function(w, h){
14496         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14497         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14498         //if(!this.el.isBorderBox()){
14499             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14500             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14501         //}
14502         if(this.tabs){
14503             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14504             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14505         }
14506         this.resizeTo(w, h);
14507         return this;
14508     },
14509
14510     /**
14511      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14512      * executed in response to a particular key being pressed while the dialog is active.
14513      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14514      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      * @return {Roo.BasicDialog} this
14518      */
14519     addKeyListener : function(key, fn, scope){
14520         var keyCode, shift, ctrl, alt;
14521         if(typeof key == "object" && !(key instanceof Array)){
14522             keyCode = key["key"];
14523             shift = key["shift"];
14524             ctrl = key["ctrl"];
14525             alt = key["alt"];
14526         }else{
14527             keyCode = key;
14528         }
14529         var handler = function(dlg, e){
14530             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14531                 var k = e.getKey();
14532                 if(keyCode instanceof Array){
14533                     for(var i = 0, len = keyCode.length; i < len; i++){
14534                         if(keyCode[i] == k){
14535                           fn.call(scope || window, dlg, k, e);
14536                           return;
14537                         }
14538                     }
14539                 }else{
14540                     if(k == keyCode){
14541                         fn.call(scope || window, dlg, k, e);
14542                     }
14543                 }
14544             }
14545         };
14546         this.on("keydown", handler);
14547         return this;
14548     },
14549
14550     /**
14551      * Returns the TabPanel component (creates it if it doesn't exist).
14552      * Note: If you wish to simply check for the existence of tabs without creating them,
14553      * check for a null 'tabs' property.
14554      * @return {Roo.TabPanel} The tabs component
14555      */
14556     getTabs : function(){
14557         if(!this.tabs){
14558             this.el.addClass("x-dlg-auto-tabs");
14559             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14560             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14561         }
14562         return this.tabs;
14563     },
14564
14565     /**
14566      * Adds a button to the footer section of the dialog.
14567      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14568      * object or a valid Roo.DomHelper element config
14569      * @param {Function} handler The function called when the button is clicked
14570      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14571      * @return {Roo.Button} The new button
14572      */
14573     addButton : function(config, handler, scope){
14574         var dh = Roo.DomHelper;
14575         if(!this.footer){
14576             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14577         }
14578         if(!this.btnContainer){
14579             var tb = this.footer.createChild({
14580
14581                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14582                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14583             }, null, true);
14584             this.btnContainer = tb.firstChild.firstChild.firstChild;
14585         }
14586         var bconfig = {
14587             handler: handler,
14588             scope: scope,
14589             minWidth: this.minButtonWidth,
14590             hideParent:true
14591         };
14592         if(typeof config == "string"){
14593             bconfig.text = config;
14594         }else{
14595             if(config.tag){
14596                 bconfig.dhconfig = config;
14597             }else{
14598                 Roo.apply(bconfig, config);
14599             }
14600         }
14601         var fc = false;
14602         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14603             bconfig.position = Math.max(0, bconfig.position);
14604             fc = this.btnContainer.childNodes[bconfig.position];
14605         }
14606          
14607         var btn = new Roo.Button(
14608             fc ? 
14609                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14610                 : this.btnContainer.appendChild(document.createElement("td")),
14611             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14612             bconfig
14613         );
14614         this.syncBodyHeight();
14615         if(!this.buttons){
14616             /**
14617              * Array of all the buttons that have been added to this dialog via addButton
14618              * @type Array
14619              */
14620             this.buttons = [];
14621         }
14622         this.buttons.push(btn);
14623         return btn;
14624     },
14625
14626     /**
14627      * Sets the default button to be focused when the dialog is displayed.
14628      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14629      * @return {Roo.BasicDialog} this
14630      */
14631     setDefaultButton : function(btn){
14632         this.defaultButton = btn;
14633         return this;
14634     },
14635
14636     // private
14637     getHeaderFooterHeight : function(safe){
14638         var height = 0;
14639         if(this.header){
14640            height += this.header.getHeight();
14641         }
14642         if(this.footer){
14643            var fm = this.footer.getMargins();
14644             height += (this.footer.getHeight()+fm.top+fm.bottom);
14645         }
14646         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14647         height += this.centerBg.getPadding("tb");
14648         return height;
14649     },
14650
14651     // private
14652     syncBodyHeight : function(){
14653         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14654         var height = this.size.height - this.getHeaderFooterHeight(false);
14655         bd.setHeight(height-bd.getMargins("tb"));
14656         var hh = this.header.getHeight();
14657         var h = this.size.height-hh;
14658         cb.setHeight(h);
14659         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14660         bw.setHeight(h-cb.getPadding("tb"));
14661         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14662         bd.setWidth(bw.getWidth(true));
14663         if(this.tabs){
14664             this.tabs.syncHeight();
14665             if(Roo.isIE){
14666                 this.tabs.el.repaint();
14667             }
14668         }
14669     },
14670
14671     /**
14672      * Restores the previous state of the dialog if Roo.state is configured.
14673      * @return {Roo.BasicDialog} this
14674      */
14675     restoreState : function(){
14676         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14677         if(box && box.width){
14678             this.xy = [box.x, box.y];
14679             this.resizeTo(box.width, box.height);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     beforeShow : function(){
14686         this.expand();
14687         if(this.fixedcenter){
14688             this.xy = this.el.getCenterXY(true);
14689         }
14690         if(this.modal){
14691             Roo.get(document.body).addClass("x-body-masked");
14692             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14693             this.mask.show();
14694         }
14695         this.constrainXY();
14696     },
14697
14698     // private
14699     animShow : function(){
14700         var b = Roo.get(this.animateTarget).getBox();
14701         this.proxy.setSize(b.width, b.height);
14702         this.proxy.setLocation(b.x, b.y);
14703         this.proxy.show();
14704         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14705                     true, .35, this.showEl.createDelegate(this));
14706     },
14707
14708     /**
14709      * Shows the dialog.
14710      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14711      * @return {Roo.BasicDialog} this
14712      */
14713     show : function(animateTarget){
14714         if (this.fireEvent("beforeshow", this) === false){
14715             return;
14716         }
14717         if(this.syncHeightBeforeShow){
14718             this.syncBodyHeight();
14719         }else if(this.firstShow){
14720             this.firstShow = false;
14721             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14722         }
14723         this.animateTarget = animateTarget || this.animateTarget;
14724         if(!this.el.isVisible()){
14725             this.beforeShow();
14726             if(this.animateTarget && Roo.get(this.animateTarget)){
14727                 this.animShow();
14728             }else{
14729                 this.showEl();
14730             }
14731         }
14732         return this;
14733     },
14734
14735     // private
14736     showEl : function(){
14737         this.proxy.hide();
14738         this.el.setXY(this.xy);
14739         this.el.show();
14740         this.adjustAssets(true);
14741         this.toFront();
14742         this.focus();
14743         // IE peekaboo bug - fix found by Dave Fenwick
14744         if(Roo.isIE){
14745             this.el.repaint();
14746         }
14747         this.fireEvent("show", this);
14748     },
14749
14750     /**
14751      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14752      * dialog itself will receive focus.
14753      */
14754     focus : function(){
14755         if(this.defaultButton){
14756             this.defaultButton.focus();
14757         }else{
14758             this.focusEl.focus();
14759         }
14760     },
14761
14762     // private
14763     constrainXY : function(){
14764         if(this.constraintoviewport !== false){
14765             if(!this.viewSize){
14766                 if(this.container){
14767                     var s = this.container.getSize();
14768                     this.viewSize = [s.width, s.height];
14769                 }else{
14770                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14771                 }
14772             }
14773             var s = Roo.get(this.container||document).getScroll();
14774
14775             var x = this.xy[0], y = this.xy[1];
14776             var w = this.size.width, h = this.size.height;
14777             var vw = this.viewSize[0], vh = this.viewSize[1];
14778             // only move it if it needs it
14779             var moved = false;
14780             // first validate right/bottom
14781             if(x + w > vw+s.left){
14782                 x = vw - w;
14783                 moved = true;
14784             }
14785             if(y + h > vh+s.top){
14786                 y = vh - h;
14787                 moved = true;
14788             }
14789             // then make sure top/left isn't negative
14790             if(x < s.left){
14791                 x = s.left;
14792                 moved = true;
14793             }
14794             if(y < s.top){
14795                 y = s.top;
14796                 moved = true;
14797             }
14798             if(moved){
14799                 // cache xy
14800                 this.xy = [x, y];
14801                 if(this.isVisible()){
14802                     this.el.setLocation(x, y);
14803                     this.adjustAssets();
14804                 }
14805             }
14806         }
14807     },
14808
14809     // private
14810     onDrag : function(){
14811         if(!this.proxyDrag){
14812             this.xy = this.el.getXY();
14813             this.adjustAssets();
14814         }
14815     },
14816
14817     // private
14818     adjustAssets : function(doShow){
14819         var x = this.xy[0], y = this.xy[1];
14820         var w = this.size.width, h = this.size.height;
14821         if(doShow === true){
14822             if(this.shadow){
14823                 this.shadow.show(this.el);
14824             }
14825             if(this.shim){
14826                 this.shim.show();
14827             }
14828         }
14829         if(this.shadow && this.shadow.isVisible()){
14830             this.shadow.show(this.el);
14831         }
14832         if(this.shim && this.shim.isVisible()){
14833             this.shim.setBounds(x, y, w, h);
14834         }
14835     },
14836
14837     // private
14838     adjustViewport : function(w, h){
14839         if(!w || !h){
14840             w = Roo.lib.Dom.getViewWidth();
14841             h = Roo.lib.Dom.getViewHeight();
14842         }
14843         // cache the size
14844         this.viewSize = [w, h];
14845         if(this.modal && this.mask.isVisible()){
14846             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14847             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14848         }
14849         if(this.isVisible()){
14850             this.constrainXY();
14851         }
14852     },
14853
14854     /**
14855      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14856      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14857      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14858      */
14859     destroy : function(removeEl){
14860         if(this.isVisible()){
14861             this.animateTarget = null;
14862             this.hide();
14863         }
14864         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14865         if(this.tabs){
14866             this.tabs.destroy(removeEl);
14867         }
14868         Roo.destroy(
14869              this.shim,
14870              this.proxy,
14871              this.resizer,
14872              this.close,
14873              this.mask
14874         );
14875         if(this.dd){
14876             this.dd.unreg();
14877         }
14878         if(this.buttons){
14879            for(var i = 0, len = this.buttons.length; i < len; i++){
14880                this.buttons[i].destroy();
14881            }
14882         }
14883         this.el.removeAllListeners();
14884         if(removeEl === true){
14885             this.el.update("");
14886             this.el.remove();
14887         }
14888         Roo.DialogManager.unregister(this);
14889     },
14890
14891     // private
14892     startMove : function(){
14893         if(this.proxyDrag){
14894             this.proxy.show();
14895         }
14896         if(this.constraintoviewport !== false){
14897             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14898         }
14899     },
14900
14901     // private
14902     endMove : function(){
14903         if(!this.proxyDrag){
14904             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14905         }else{
14906             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14907             this.proxy.hide();
14908         }
14909         this.refreshSize();
14910         this.adjustAssets();
14911         this.focus();
14912         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14913     },
14914
14915     /**
14916      * Brings this dialog to the front of any other visible dialogs
14917      * @return {Roo.BasicDialog} this
14918      */
14919     toFront : function(){
14920         Roo.DialogManager.bringToFront(this);
14921         return this;
14922     },
14923
14924     /**
14925      * Sends this dialog to the back (under) of any other visible dialogs
14926      * @return {Roo.BasicDialog} this
14927      */
14928     toBack : function(){
14929         Roo.DialogManager.sendToBack(this);
14930         return this;
14931     },
14932
14933     /**
14934      * Centers this dialog in the viewport
14935      * @return {Roo.BasicDialog} this
14936      */
14937     center : function(){
14938         var xy = this.el.getCenterXY(true);
14939         this.moveTo(xy[0], xy[1]);
14940         return this;
14941     },
14942
14943     /**
14944      * Moves the dialog's top-left corner to the specified point
14945      * @param {Number} x
14946      * @param {Number} y
14947      * @return {Roo.BasicDialog} this
14948      */
14949     moveTo : function(x, y){
14950         this.xy = [x,y];
14951         if(this.isVisible()){
14952             this.el.setXY(this.xy);
14953             this.adjustAssets();
14954         }
14955         return this;
14956     },
14957
14958     /**
14959      * Aligns the dialog to the specified element
14960      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14961      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14962      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14963      * @return {Roo.BasicDialog} this
14964      */
14965     alignTo : function(element, position, offsets){
14966         this.xy = this.el.getAlignToXY(element, position, offsets);
14967         if(this.isVisible()){
14968             this.el.setXY(this.xy);
14969             this.adjustAssets();
14970         }
14971         return this;
14972     },
14973
14974     /**
14975      * Anchors an element to another element and realigns it when the window is resized.
14976      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14977      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14978      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14979      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14980      * is a number, it is used as the buffer delay (defaults to 50ms).
14981      * @return {Roo.BasicDialog} this
14982      */
14983     anchorTo : function(el, alignment, offsets, monitorScroll){
14984         var action = function(){
14985             this.alignTo(el, alignment, offsets);
14986         };
14987         Roo.EventManager.onWindowResize(action, this);
14988         var tm = typeof monitorScroll;
14989         if(tm != 'undefined'){
14990             Roo.EventManager.on(window, 'scroll', action, this,
14991                 {buffer: tm == 'number' ? monitorScroll : 50});
14992         }
14993         action.call(this);
14994         return this;
14995     },
14996
14997     /**
14998      * Returns true if the dialog is visible
14999      * @return {Boolean}
15000      */
15001     isVisible : function(){
15002         return this.el.isVisible();
15003     },
15004
15005     // private
15006     animHide : function(callback){
15007         var b = Roo.get(this.animateTarget).getBox();
15008         this.proxy.show();
15009         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15010         this.el.hide();
15011         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15012                     this.hideEl.createDelegate(this, [callback]));
15013     },
15014
15015     /**
15016      * Hides the dialog.
15017      * @param {Function} callback (optional) Function to call when the dialog is hidden
15018      * @return {Roo.BasicDialog} this
15019      */
15020     hide : function(callback){
15021         if (this.fireEvent("beforehide", this) === false){
15022             return;
15023         }
15024         if(this.shadow){
15025             this.shadow.hide();
15026         }
15027         if(this.shim) {
15028           this.shim.hide();
15029         }
15030         // sometimes animateTarget seems to get set.. causing problems...
15031         // this just double checks..
15032         if(this.animateTarget && Roo.get(this.animateTarget)) {
15033            this.animHide(callback);
15034         }else{
15035             this.el.hide();
15036             this.hideEl(callback);
15037         }
15038         return this;
15039     },
15040
15041     // private
15042     hideEl : function(callback){
15043         this.proxy.hide();
15044         if(this.modal){
15045             this.mask.hide();
15046             Roo.get(document.body).removeClass("x-body-masked");
15047         }
15048         this.fireEvent("hide", this);
15049         if(typeof callback == "function"){
15050             callback();
15051         }
15052     },
15053
15054     // private
15055     hideAction : function(){
15056         this.setLeft("-10000px");
15057         this.setTop("-10000px");
15058         this.setStyle("visibility", "hidden");
15059     },
15060
15061     // private
15062     refreshSize : function(){
15063         this.size = this.el.getSize();
15064         this.xy = this.el.getXY();
15065         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15066     },
15067
15068     // private
15069     // z-index is managed by the DialogManager and may be overwritten at any time
15070     setZIndex : function(index){
15071         if(this.modal){
15072             this.mask.setStyle("z-index", index);
15073         }
15074         if(this.shim){
15075             this.shim.setStyle("z-index", ++index);
15076         }
15077         if(this.shadow){
15078             this.shadow.setZIndex(++index);
15079         }
15080         this.el.setStyle("z-index", ++index);
15081         if(this.proxy){
15082             this.proxy.setStyle("z-index", ++index);
15083         }
15084         if(this.resizer){
15085             this.resizer.proxy.setStyle("z-index", ++index);
15086         }
15087
15088         this.lastZIndex = index;
15089     },
15090
15091     /**
15092      * Returns the element for this dialog
15093      * @return {Roo.Element} The underlying dialog Element
15094      */
15095     getEl : function(){
15096         return this.el;
15097     }
15098 });
15099
15100 /**
15101  * @class Roo.DialogManager
15102  * Provides global access to BasicDialogs that have been created and
15103  * support for z-indexing (layering) multiple open dialogs.
15104  */
15105 Roo.DialogManager = function(){
15106     var list = {};
15107     var accessList = [];
15108     var front = null;
15109
15110     // private
15111     var sortDialogs = function(d1, d2){
15112         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15113     };
15114
15115     // private
15116     var orderDialogs = function(){
15117         accessList.sort(sortDialogs);
15118         var seed = Roo.DialogManager.zseed;
15119         for(var i = 0, len = accessList.length; i < len; i++){
15120             var dlg = accessList[i];
15121             if(dlg){
15122                 dlg.setZIndex(seed + (i*10));
15123             }
15124         }
15125     };
15126
15127     return {
15128         /**
15129          * The starting z-index for BasicDialogs (defaults to 9000)
15130          * @type Number The z-index value
15131          */
15132         zseed : 9000,
15133
15134         // private
15135         register : function(dlg){
15136             list[dlg.id] = dlg;
15137             accessList.push(dlg);
15138         },
15139
15140         // private
15141         unregister : function(dlg){
15142             delete list[dlg.id];
15143             var i=0;
15144             var len=0;
15145             if(!accessList.indexOf){
15146                 for(  i = 0, len = accessList.length; i < len; i++){
15147                     if(accessList[i] == dlg){
15148                         accessList.splice(i, 1);
15149                         return;
15150                     }
15151                 }
15152             }else{
15153                  i = accessList.indexOf(dlg);
15154                 if(i != -1){
15155                     accessList.splice(i, 1);
15156                 }
15157             }
15158         },
15159
15160         /**
15161          * Gets a registered dialog by id
15162          * @param {String/Object} id The id of the dialog or a dialog
15163          * @return {Roo.BasicDialog} this
15164          */
15165         get : function(id){
15166             return typeof id == "object" ? id : list[id];
15167         },
15168
15169         /**
15170          * Brings the specified dialog to the front
15171          * @param {String/Object} dlg The id of the dialog or a dialog
15172          * @return {Roo.BasicDialog} this
15173          */
15174         bringToFront : function(dlg){
15175             dlg = this.get(dlg);
15176             if(dlg != front){
15177                 front = dlg;
15178                 dlg._lastAccess = new Date().getTime();
15179                 orderDialogs();
15180             }
15181             return dlg;
15182         },
15183
15184         /**
15185          * Sends the specified dialog to the back
15186          * @param {String/Object} dlg The id of the dialog or a dialog
15187          * @return {Roo.BasicDialog} this
15188          */
15189         sendToBack : function(dlg){
15190             dlg = this.get(dlg);
15191             dlg._lastAccess = -(new Date().getTime());
15192             orderDialogs();
15193             return dlg;
15194         },
15195
15196         /**
15197          * Hides all dialogs
15198          */
15199         hideAll : function(){
15200             for(var id in list){
15201                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15202                     list[id].hide();
15203                 }
15204             }
15205         }
15206     };
15207 }();
15208
15209 /**
15210  * @class Roo.LayoutDialog
15211  * @extends Roo.BasicDialog
15212  * Dialog which provides adjustments for working with a layout in a Dialog.
15213  * Add your necessary layout config options to the dialog's config.<br>
15214  * Example usage (including a nested layout):
15215  * <pre><code>
15216 if(!dialog){
15217     dialog = new Roo.LayoutDialog("download-dlg", {
15218         modal: true,
15219         width:600,
15220         height:450,
15221         shadow:true,
15222         minWidth:500,
15223         minHeight:350,
15224         autoTabs:true,
15225         proxyDrag:true,
15226         // layout config merges with the dialog config
15227         center:{
15228             tabPosition: "top",
15229             alwaysShowTabs: true
15230         }
15231     });
15232     dialog.addKeyListener(27, dialog.hide, dialog);
15233     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15234     dialog.addButton("Build It!", this.getDownload, this);
15235
15236     // we can even add nested layouts
15237     var innerLayout = new Roo.BorderLayout("dl-inner", {
15238         east: {
15239             initialSize: 200,
15240             autoScroll:true,
15241             split:true
15242         },
15243         center: {
15244             autoScroll:true
15245         }
15246     });
15247     innerLayout.beginUpdate();
15248     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15249     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15250     innerLayout.endUpdate(true);
15251
15252     var layout = dialog.getLayout();
15253     layout.beginUpdate();
15254     layout.add("center", new Roo.ContentPanel("standard-panel",
15255                         {title: "Download the Source", fitToFrame:true}));
15256     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15257                {title: "Build your own roo.js"}));
15258     layout.getRegion("center").showPanel(sp);
15259     layout.endUpdate();
15260 }
15261 </code></pre>
15262     * @constructor
15263     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15264     * @param {Object} config configuration options
15265   */
15266 Roo.LayoutDialog = function(el, cfg){
15267     
15268     var config=  cfg;
15269     if (typeof(cfg) == 'undefined') {
15270         config = Roo.apply({}, el);
15271         // not sure why we use documentElement here.. - it should always be body.
15272         // IE7 borks horribly if we use documentElement.
15273         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15274         //config.autoCreate = true;
15275     }
15276     
15277     
15278     config.autoTabs = false;
15279     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15280     this.body.setStyle({overflow:"hidden", position:"relative"});
15281     this.layout = new Roo.BorderLayout(this.body.dom, config);
15282     this.layout.monitorWindowResize = false;
15283     this.el.addClass("x-dlg-auto-layout");
15284     // fix case when center region overwrites center function
15285     this.center = Roo.BasicDialog.prototype.center;
15286     this.on("show", this.layout.layout, this.layout, true);
15287     if (config.items) {
15288         var xitems = config.items;
15289         delete config.items;
15290         Roo.each(xitems, this.addxtype, this);
15291     }
15292     
15293     
15294 };
15295 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15296     /**
15297      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15298      * @deprecated
15299      */
15300     endUpdate : function(){
15301         this.layout.endUpdate();
15302     },
15303
15304     /**
15305      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15306      *  @deprecated
15307      */
15308     beginUpdate : function(){
15309         this.layout.beginUpdate();
15310     },
15311
15312     /**
15313      * Get the BorderLayout for this dialog
15314      * @return {Roo.BorderLayout}
15315      */
15316     getLayout : function(){
15317         return this.layout;
15318     },
15319
15320     showEl : function(){
15321         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15322         if(Roo.isIE7){
15323             this.layout.layout();
15324         }
15325     },
15326
15327     // private
15328     // Use the syncHeightBeforeShow config option to control this automatically
15329     syncBodyHeight : function(){
15330         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15331         if(this.layout){this.layout.layout();}
15332     },
15333     
15334       /**
15335      * Add an xtype element (actually adds to the layout.)
15336      * @return {Object} xdata xtype object data.
15337      */
15338     
15339     addxtype : function(c) {
15340         return this.layout.addxtype(c);
15341     }
15342 });/*
15343  * Based on:
15344  * Ext JS Library 1.1.1
15345  * Copyright(c) 2006-2007, Ext JS, LLC.
15346  *
15347  * Originally Released Under LGPL - original licence link has changed is not relivant.
15348  *
15349  * Fork - LGPL
15350  * <script type="text/javascript">
15351  */
15352  
15353 /**
15354  * @class Roo.MessageBox
15355  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15356  * Example usage:
15357  *<pre><code>
15358 // Basic alert:
15359 Roo.Msg.alert('Status', 'Changes saved successfully.');
15360
15361 // Prompt for user data:
15362 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15363     if (btn == 'ok'){
15364         // process text value...
15365     }
15366 });
15367
15368 // Show a dialog using config options:
15369 Roo.Msg.show({
15370    title:'Save Changes?',
15371    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15372    buttons: Roo.Msg.YESNOCANCEL,
15373    fn: processResult,
15374    animEl: 'elId'
15375 });
15376 </code></pre>
15377  * @singleton
15378  */
15379 Roo.MessageBox = function(){
15380     var dlg, opt, mask, waitTimer;
15381     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15382     var buttons, activeTextEl, bwidth;
15383
15384     // private
15385     var handleButton = function(button){
15386         dlg.hide();
15387         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15388     };
15389
15390     // private
15391     var handleHide = function(){
15392         if(opt && opt.cls){
15393             dlg.el.removeClass(opt.cls);
15394         }
15395         if(waitTimer){
15396             Roo.TaskMgr.stop(waitTimer);
15397             waitTimer = null;
15398         }
15399     };
15400
15401     // private
15402     var updateButtons = function(b){
15403         var width = 0;
15404         if(!b){
15405             buttons["ok"].hide();
15406             buttons["cancel"].hide();
15407             buttons["yes"].hide();
15408             buttons["no"].hide();
15409             dlg.footer.dom.style.display = 'none';
15410             return width;
15411         }
15412         dlg.footer.dom.style.display = '';
15413         for(var k in buttons){
15414             if(typeof buttons[k] != "function"){
15415                 if(b[k]){
15416                     buttons[k].show();
15417                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15418                     width += buttons[k].el.getWidth()+15;
15419                 }else{
15420                     buttons[k].hide();
15421                 }
15422             }
15423         }
15424         return width;
15425     };
15426
15427     // private
15428     var handleEsc = function(d, k, e){
15429         if(opt && opt.closable !== false){
15430             dlg.hide();
15431         }
15432         if(e){
15433             e.stopEvent();
15434         }
15435     };
15436
15437     return {
15438         /**
15439          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15440          * @return {Roo.BasicDialog} The BasicDialog element
15441          */
15442         getDialog : function(){
15443            if(!dlg){
15444                 dlg = new Roo.BasicDialog("x-msg-box", {
15445                     autoCreate : true,
15446                     shadow: true,
15447                     draggable: true,
15448                     resizable:false,
15449                     constraintoviewport:false,
15450                     fixedcenter:true,
15451                     collapsible : false,
15452                     shim:true,
15453                     modal: true,
15454                     width:400, height:100,
15455                     buttonAlign:"center",
15456                     closeClick : function(){
15457                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15458                             handleButton("no");
15459                         }else{
15460                             handleButton("cancel");
15461                         }
15462                     }
15463                 });
15464                 dlg.on("hide", handleHide);
15465                 mask = dlg.mask;
15466                 dlg.addKeyListener(27, handleEsc);
15467                 buttons = {};
15468                 var bt = this.buttonText;
15469                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15470                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15471                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15472                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15473                 bodyEl = dlg.body.createChild({
15474
15475                     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>'
15476                 });
15477                 msgEl = bodyEl.dom.firstChild;
15478                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15479                 textboxEl.enableDisplayMode();
15480                 textboxEl.addKeyListener([10,13], function(){
15481                     if(dlg.isVisible() && opt && opt.buttons){
15482                         if(opt.buttons.ok){
15483                             handleButton("ok");
15484                         }else if(opt.buttons.yes){
15485                             handleButton("yes");
15486                         }
15487                     }
15488                 });
15489                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15490                 textareaEl.enableDisplayMode();
15491                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15492                 progressEl.enableDisplayMode();
15493                 var pf = progressEl.dom.firstChild;
15494                 if (pf) {
15495                     pp = Roo.get(pf.firstChild);
15496                     pp.setHeight(pf.offsetHeight);
15497                 }
15498                 
15499             }
15500             return dlg;
15501         },
15502
15503         /**
15504          * Updates the message box body text
15505          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15506          * the XHTML-compliant non-breaking space character '&amp;#160;')
15507          * @return {Roo.MessageBox} This message box
15508          */
15509         updateText : function(text){
15510             if(!dlg.isVisible() && !opt.width){
15511                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15512             }
15513             msgEl.innerHTML = text || '&#160;';
15514             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15515                         Math.max(opt.minWidth || this.minWidth, bwidth));
15516             if(opt.prompt){
15517                 activeTextEl.setWidth(w);
15518             }
15519             if(dlg.isVisible()){
15520                 dlg.fixedcenter = false;
15521             }
15522             dlg.setContentSize(w, bodyEl.getHeight());
15523             if(dlg.isVisible()){
15524                 dlg.fixedcenter = true;
15525             }
15526             return this;
15527         },
15528
15529         /**
15530          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15531          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15532          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15533          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15534          * @return {Roo.MessageBox} This message box
15535          */
15536         updateProgress : function(value, text){
15537             if(text){
15538                 this.updateText(text);
15539             }
15540             if (pp) { // weird bug on my firefox - for some reason this is not defined
15541                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15542             }
15543             return this;
15544         },        
15545
15546         /**
15547          * Returns true if the message box is currently displayed
15548          * @return {Boolean} True if the message box is visible, else false
15549          */
15550         isVisible : function(){
15551             return dlg && dlg.isVisible();  
15552         },
15553
15554         /**
15555          * Hides the message box if it is displayed
15556          */
15557         hide : function(){
15558             if(this.isVisible()){
15559                 dlg.hide();
15560             }  
15561         },
15562
15563         /**
15564          * Displays a new message box, or reinitializes an existing message box, based on the config options
15565          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15566          * The following config object properties are supported:
15567          * <pre>
15568 Property    Type             Description
15569 ----------  ---------------  ------------------------------------------------------------------------------------
15570 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15571                                    closes (defaults to undefined)
15572 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15573                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15574 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15575                                    progress and wait dialogs will ignore this property and always hide the
15576                                    close button as they can only be closed programmatically.
15577 cls               String           A custom CSS class to apply to the message box element
15578 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15579                                    displayed (defaults to 75)
15580 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15581                                    function will be btn (the name of the button that was clicked, if applicable,
15582                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15583                                    Progress and wait dialogs will ignore this option since they do not respond to
15584                                    user actions and can only be closed programmatically, so any required function
15585                                    should be called by the same code after it closes the dialog.
15586 icon              String           A CSS class that provides a background image to be used as an icon for
15587                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15588 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15589 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15590 modal             Boolean          False to allow user interaction with the page while the message box is
15591                                    displayed (defaults to true)
15592 msg               String           A string that will replace the existing message box body text (defaults
15593                                    to the XHTML-compliant non-breaking space character '&#160;')
15594 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15595 progress          Boolean          True to display a progress bar (defaults to false)
15596 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15597 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15598 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15599 title             String           The title text
15600 value             String           The string value to set into the active textbox element if displayed
15601 wait              Boolean          True to display a progress bar (defaults to false)
15602 width             Number           The width of the dialog in pixels
15603 </pre>
15604          *
15605          * Example usage:
15606          * <pre><code>
15607 Roo.Msg.show({
15608    title: 'Address',
15609    msg: 'Please enter your address:',
15610    width: 300,
15611    buttons: Roo.MessageBox.OKCANCEL,
15612    multiline: true,
15613    fn: saveAddress,
15614    animEl: 'addAddressBtn'
15615 });
15616 </code></pre>
15617          * @param {Object} config Configuration options
15618          * @return {Roo.MessageBox} This message box
15619          */
15620         show : function(options){
15621             if(this.isVisible()){
15622                 this.hide();
15623             }
15624             var d = this.getDialog();
15625             opt = options;
15626             d.setTitle(opt.title || "&#160;");
15627             d.close.setDisplayed(opt.closable !== false);
15628             activeTextEl = textboxEl;
15629             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15630             if(opt.prompt){
15631                 if(opt.multiline){
15632                     textboxEl.hide();
15633                     textareaEl.show();
15634                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15635                         opt.multiline : this.defaultTextHeight);
15636                     activeTextEl = textareaEl;
15637                 }else{
15638                     textboxEl.show();
15639                     textareaEl.hide();
15640                 }
15641             }else{
15642                 textboxEl.hide();
15643                 textareaEl.hide();
15644             }
15645             progressEl.setDisplayed(opt.progress === true);
15646             this.updateProgress(0);
15647             activeTextEl.dom.value = opt.value || "";
15648             if(opt.prompt){
15649                 dlg.setDefaultButton(activeTextEl);
15650             }else{
15651                 var bs = opt.buttons;
15652                 var db = null;
15653                 if(bs && bs.ok){
15654                     db = buttons["ok"];
15655                 }else if(bs && bs.yes){
15656                     db = buttons["yes"];
15657                 }
15658                 dlg.setDefaultButton(db);
15659             }
15660             bwidth = updateButtons(opt.buttons);
15661             this.updateText(opt.msg);
15662             if(opt.cls){
15663                 d.el.addClass(opt.cls);
15664             }
15665             d.proxyDrag = opt.proxyDrag === true;
15666             d.modal = opt.modal !== false;
15667             d.mask = opt.modal !== false ? mask : false;
15668             if(!d.isVisible()){
15669                 // force it to the end of the z-index stack so it gets a cursor in FF
15670                 document.body.appendChild(dlg.el.dom);
15671                 d.animateTarget = null;
15672                 d.show(options.animEl);
15673             }
15674             return this;
15675         },
15676
15677         /**
15678          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15679          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15680          * and closing the message box when the process is complete.
15681          * @param {String} title The title bar text
15682          * @param {String} msg The message box body text
15683          * @return {Roo.MessageBox} This message box
15684          */
15685         progress : function(title, msg){
15686             this.show({
15687                 title : title,
15688                 msg : msg,
15689                 buttons: false,
15690                 progress:true,
15691                 closable:false,
15692                 minWidth: this.minProgressWidth,
15693                 modal : true
15694             });
15695             return this;
15696         },
15697
15698         /**
15699          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15700          * If a callback function is passed it will be called after the user clicks the button, and the
15701          * id of the button that was clicked will be passed as the only parameter to the callback
15702          * (could also be the top-right close button).
15703          * @param {String} title The title bar text
15704          * @param {String} msg The message box body text
15705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15706          * @param {Object} scope (optional) The scope of the callback function
15707          * @return {Roo.MessageBox} This message box
15708          */
15709         alert : function(title, msg, fn, scope){
15710             this.show({
15711                 title : title,
15712                 msg : msg,
15713                 buttons: this.OK,
15714                 fn: fn,
15715                 scope : scope,
15716                 modal : true
15717             });
15718             return this;
15719         },
15720
15721         /**
15722          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15723          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15724          * You are responsible for closing the message box when the process is complete.
15725          * @param {String} msg The message box body text
15726          * @param {String} title (optional) The title bar text
15727          * @return {Roo.MessageBox} This message box
15728          */
15729         wait : function(msg, title){
15730             this.show({
15731                 title : title,
15732                 msg : msg,
15733                 buttons: false,
15734                 closable:false,
15735                 progress:true,
15736                 modal:true,
15737                 width:300,
15738                 wait:true
15739             });
15740             waitTimer = Roo.TaskMgr.start({
15741                 run: function(i){
15742                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15743                 },
15744                 interval: 1000
15745             });
15746             return this;
15747         },
15748
15749         /**
15750          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15751          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15752          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15753          * @param {String} title The title bar text
15754          * @param {String} msg The message box body text
15755          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15756          * @param {Object} scope (optional) The scope of the callback function
15757          * @return {Roo.MessageBox} This message box
15758          */
15759         confirm : function(title, msg, fn, scope){
15760             this.show({
15761                 title : title,
15762                 msg : msg,
15763                 buttons: this.YESNO,
15764                 fn: fn,
15765                 scope : scope,
15766                 modal : true
15767             });
15768             return this;
15769         },
15770
15771         /**
15772          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15773          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15774          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15775          * (could also be the top-right close button) and the text that was entered will be passed as the two
15776          * parameters to the callback.
15777          * @param {String} title The title bar text
15778          * @param {String} msg The message box body text
15779          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15780          * @param {Object} scope (optional) The scope of the callback function
15781          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15782          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15783          * @return {Roo.MessageBox} This message box
15784          */
15785         prompt : function(title, msg, fn, scope, multiline){
15786             this.show({
15787                 title : title,
15788                 msg : msg,
15789                 buttons: this.OKCANCEL,
15790                 fn: fn,
15791                 minWidth:250,
15792                 scope : scope,
15793                 prompt:true,
15794                 multiline: multiline,
15795                 modal : true
15796             });
15797             return this;
15798         },
15799
15800         /**
15801          * Button config that displays a single OK button
15802          * @type Object
15803          */
15804         OK : {ok:true},
15805         /**
15806          * Button config that displays Yes and No buttons
15807          * @type Object
15808          */
15809         YESNO : {yes:true, no:true},
15810         /**
15811          * Button config that displays OK and Cancel buttons
15812          * @type Object
15813          */
15814         OKCANCEL : {ok:true, cancel:true},
15815         /**
15816          * Button config that displays Yes, No and Cancel buttons
15817          * @type Object
15818          */
15819         YESNOCANCEL : {yes:true, no:true, cancel:true},
15820
15821         /**
15822          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15823          * @type Number
15824          */
15825         defaultTextHeight : 75,
15826         /**
15827          * The maximum width in pixels of the message box (defaults to 600)
15828          * @type Number
15829          */
15830         maxWidth : 600,
15831         /**
15832          * The minimum width in pixels of the message box (defaults to 100)
15833          * @type Number
15834          */
15835         minWidth : 100,
15836         /**
15837          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15838          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15839          * @type Number
15840          */
15841         minProgressWidth : 250,
15842         /**
15843          * An object containing the default button text strings that can be overriden for localized language support.
15844          * Supported properties are: ok, cancel, yes and no.
15845          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15846          * @type Object
15847          */
15848         buttonText : {
15849             ok : "OK",
15850             cancel : "Cancel",
15851             yes : "Yes",
15852             no : "No"
15853         }
15854     };
15855 }();
15856
15857 /**
15858  * Shorthand for {@link Roo.MessageBox}
15859  */
15860 Roo.Msg = Roo.MessageBox;/*
15861  * Based on:
15862  * Ext JS Library 1.1.1
15863  * Copyright(c) 2006-2007, Ext JS, LLC.
15864  *
15865  * Originally Released Under LGPL - original licence link has changed is not relivant.
15866  *
15867  * Fork - LGPL
15868  * <script type="text/javascript">
15869  */
15870 /**
15871  * @class Roo.QuickTips
15872  * Provides attractive and customizable tooltips for any element.
15873  * @singleton
15874  */
15875 Roo.QuickTips = function(){
15876     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15877     var ce, bd, xy, dd;
15878     var visible = false, disabled = true, inited = false;
15879     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15880     
15881     var onOver = function(e){
15882         if(disabled){
15883             return;
15884         }
15885         var t = e.getTarget();
15886         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15887             return;
15888         }
15889         if(ce && t == ce.el){
15890             clearTimeout(hideProc);
15891             return;
15892         }
15893         if(t && tagEls[t.id]){
15894             tagEls[t.id].el = t;
15895             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15896             return;
15897         }
15898         var ttp, et = Roo.fly(t);
15899         var ns = cfg.namespace;
15900         if(tm.interceptTitles && t.title){
15901             ttp = t.title;
15902             t.qtip = ttp;
15903             t.removeAttribute("title");
15904             e.preventDefault();
15905         }else{
15906             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15907         }
15908         if(ttp){
15909             showProc = show.defer(tm.showDelay, tm, [{
15910                 el: t, 
15911                 text: ttp, 
15912                 width: et.getAttributeNS(ns, cfg.width),
15913                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15914                 title: et.getAttributeNS(ns, cfg.title),
15915                     cls: et.getAttributeNS(ns, cfg.cls)
15916             }]);
15917         }
15918     };
15919     
15920     var onOut = function(e){
15921         clearTimeout(showProc);
15922         var t = e.getTarget();
15923         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15924             hideProc = setTimeout(hide, tm.hideDelay);
15925         }
15926     };
15927     
15928     var onMove = function(e){
15929         if(disabled){
15930             return;
15931         }
15932         xy = e.getXY();
15933         xy[1] += 18;
15934         if(tm.trackMouse && ce){
15935             el.setXY(xy);
15936         }
15937     };
15938     
15939     var onDown = function(e){
15940         clearTimeout(showProc);
15941         clearTimeout(hideProc);
15942         if(!e.within(el)){
15943             if(tm.hideOnClick){
15944                 hide();
15945                 tm.disable();
15946                 tm.enable.defer(100, tm);
15947             }
15948         }
15949     };
15950     
15951     var getPad = function(){
15952         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15953     };
15954
15955     var show = function(o){
15956         if(disabled){
15957             return;
15958         }
15959         clearTimeout(dismissProc);
15960         ce = o;
15961         if(removeCls){ // in case manually hidden
15962             el.removeClass(removeCls);
15963             removeCls = null;
15964         }
15965         if(ce.cls){
15966             el.addClass(ce.cls);
15967             removeCls = ce.cls;
15968         }
15969         if(ce.title){
15970             tipTitle.update(ce.title);
15971             tipTitle.show();
15972         }else{
15973             tipTitle.update('');
15974             tipTitle.hide();
15975         }
15976         el.dom.style.width  = tm.maxWidth+'px';
15977         //tipBody.dom.style.width = '';
15978         tipBodyText.update(o.text);
15979         var p = getPad(), w = ce.width;
15980         if(!w){
15981             var td = tipBodyText.dom;
15982             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15983             if(aw > tm.maxWidth){
15984                 w = tm.maxWidth;
15985             }else if(aw < tm.minWidth){
15986                 w = tm.minWidth;
15987             }else{
15988                 w = aw;
15989             }
15990         }
15991         //tipBody.setWidth(w);
15992         el.setWidth(parseInt(w, 10) + p);
15993         if(ce.autoHide === false){
15994             close.setDisplayed(true);
15995             if(dd){
15996                 dd.unlock();
15997             }
15998         }else{
15999             close.setDisplayed(false);
16000             if(dd){
16001                 dd.lock();
16002             }
16003         }
16004         if(xy){
16005             el.avoidY = xy[1]-18;
16006             el.setXY(xy);
16007         }
16008         if(tm.animate){
16009             el.setOpacity(.1);
16010             el.setStyle("visibility", "visible");
16011             el.fadeIn({callback: afterShow});
16012         }else{
16013             afterShow();
16014         }
16015     };
16016     
16017     var afterShow = function(){
16018         if(ce){
16019             el.show();
16020             esc.enable();
16021             if(tm.autoDismiss && ce.autoHide !== false){
16022                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16023             }
16024         }
16025     };
16026     
16027     var hide = function(noanim){
16028         clearTimeout(dismissProc);
16029         clearTimeout(hideProc);
16030         ce = null;
16031         if(el.isVisible()){
16032             esc.disable();
16033             if(noanim !== true && tm.animate){
16034                 el.fadeOut({callback: afterHide});
16035             }else{
16036                 afterHide();
16037             } 
16038         }
16039     };
16040     
16041     var afterHide = function(){
16042         el.hide();
16043         if(removeCls){
16044             el.removeClass(removeCls);
16045             removeCls = null;
16046         }
16047     };
16048     
16049     return {
16050         /**
16051         * @cfg {Number} minWidth
16052         * The minimum width of the quick tip (defaults to 40)
16053         */
16054        minWidth : 40,
16055         /**
16056         * @cfg {Number} maxWidth
16057         * The maximum width of the quick tip (defaults to 300)
16058         */
16059        maxWidth : 300,
16060         /**
16061         * @cfg {Boolean} interceptTitles
16062         * True to automatically use the element's DOM title value if available (defaults to false)
16063         */
16064        interceptTitles : false,
16065         /**
16066         * @cfg {Boolean} trackMouse
16067         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16068         */
16069        trackMouse : false,
16070         /**
16071         * @cfg {Boolean} hideOnClick
16072         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16073         */
16074        hideOnClick : true,
16075         /**
16076         * @cfg {Number} showDelay
16077         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16078         */
16079        showDelay : 500,
16080         /**
16081         * @cfg {Number} hideDelay
16082         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16083         */
16084        hideDelay : 200,
16085         /**
16086         * @cfg {Boolean} autoHide
16087         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16088         * Used in conjunction with hideDelay.
16089         */
16090        autoHide : true,
16091         /**
16092         * @cfg {Boolean}
16093         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16094         * (defaults to true).  Used in conjunction with autoDismissDelay.
16095         */
16096        autoDismiss : true,
16097         /**
16098         * @cfg {Number}
16099         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16100         */
16101        autoDismissDelay : 5000,
16102        /**
16103         * @cfg {Boolean} animate
16104         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16105         */
16106        animate : false,
16107
16108        /**
16109         * @cfg {String} title
16110         * Title text to display (defaults to '').  This can be any valid HTML markup.
16111         */
16112         title: '',
16113        /**
16114         * @cfg {String} text
16115         * Body text to display (defaults to '').  This can be any valid HTML markup.
16116         */
16117         text : '',
16118        /**
16119         * @cfg {String} cls
16120         * A CSS class to apply to the base quick tip element (defaults to '').
16121         */
16122         cls : '',
16123        /**
16124         * @cfg {Number} width
16125         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16126         * minWidth or maxWidth.
16127         */
16128         width : null,
16129
16130     /**
16131      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16132      * or display QuickTips in a page.
16133      */
16134        init : function(){
16135           tm = Roo.QuickTips;
16136           cfg = tm.tagConfig;
16137           if(!inited){
16138               if(!Roo.isReady){ // allow calling of init() before onReady
16139                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16140                   return;
16141               }
16142               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16143               el.fxDefaults = {stopFx: true};
16144               // maximum custom styling
16145               //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>');
16146               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>');              
16147               tipTitle = el.child('h3');
16148               tipTitle.enableDisplayMode("block");
16149               tipBody = el.child('div.x-tip-bd');
16150               tipBodyText = el.child('div.x-tip-bd-inner');
16151               //bdLeft = el.child('div.x-tip-bd-left');
16152               //bdRight = el.child('div.x-tip-bd-right');
16153               close = el.child('div.x-tip-close');
16154               close.enableDisplayMode("block");
16155               close.on("click", hide);
16156               var d = Roo.get(document);
16157               d.on("mousedown", onDown);
16158               d.on("mouseover", onOver);
16159               d.on("mouseout", onOut);
16160               d.on("mousemove", onMove);
16161               esc = d.addKeyListener(27, hide);
16162               esc.disable();
16163               if(Roo.dd.DD){
16164                   dd = el.initDD("default", null, {
16165                       onDrag : function(){
16166                           el.sync();  
16167                       }
16168                   });
16169                   dd.setHandleElId(tipTitle.id);
16170                   dd.lock();
16171               }
16172               inited = true;
16173           }
16174           this.enable(); 
16175        },
16176
16177     /**
16178      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16179      * are supported:
16180      * <pre>
16181 Property    Type                   Description
16182 ----------  ---------------------  ------------------------------------------------------------------------
16183 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16184      * </ul>
16185      * @param {Object} config The config object
16186      */
16187        register : function(config){
16188            var cs = config instanceof Array ? config : arguments;
16189            for(var i = 0, len = cs.length; i < len; i++) {
16190                var c = cs[i];
16191                var target = c.target;
16192                if(target){
16193                    if(target instanceof Array){
16194                        for(var j = 0, jlen = target.length; j < jlen; j++){
16195                            tagEls[target[j]] = c;
16196                        }
16197                    }else{
16198                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16199                    }
16200                }
16201            }
16202        },
16203
16204     /**
16205      * Removes this quick tip from its element and destroys it.
16206      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16207      */
16208        unregister : function(el){
16209            delete tagEls[Roo.id(el)];
16210        },
16211
16212     /**
16213      * Enable this quick tip.
16214      */
16215        enable : function(){
16216            if(inited && disabled){
16217                locks.pop();
16218                if(locks.length < 1){
16219                    disabled = false;
16220                }
16221            }
16222        },
16223
16224     /**
16225      * Disable this quick tip.
16226      */
16227        disable : function(){
16228           disabled = true;
16229           clearTimeout(showProc);
16230           clearTimeout(hideProc);
16231           clearTimeout(dismissProc);
16232           if(ce){
16233               hide(true);
16234           }
16235           locks.push(1);
16236        },
16237
16238     /**
16239      * Returns true if the quick tip is enabled, else false.
16240      */
16241        isEnabled : function(){
16242             return !disabled;
16243        },
16244
16245         // private
16246        tagConfig : {
16247            namespace : "ext",
16248            attribute : "qtip",
16249            width : "width",
16250            target : "target",
16251            title : "qtitle",
16252            hide : "hide",
16253            cls : "qclass"
16254        }
16255    };
16256 }();
16257
16258 // backwards compat
16259 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.tree.TreePanel
16273  * @extends Roo.data.Tree
16274
16275  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16276  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16277  * @cfg {Boolean} enableDD true to enable drag and drop
16278  * @cfg {Boolean} enableDrag true to enable just drag
16279  * @cfg {Boolean} enableDrop true to enable just drop
16280  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16281  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16282  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16283  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16284  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16285  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16286  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16287  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16288  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16289  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16290  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16291  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16292  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16293  * @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>
16294  * @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>
16295  * 
16296  * @constructor
16297  * @param {String/HTMLElement/Element} el The container element
16298  * @param {Object} config
16299  */
16300 Roo.tree.TreePanel = function(el, config){
16301     var root = false;
16302     var loader = false;
16303     if (config.root) {
16304         root = config.root;
16305         delete config.root;
16306     }
16307     if (config.loader) {
16308         loader = config.loader;
16309         delete config.loader;
16310     }
16311     
16312     Roo.apply(this, config);
16313     Roo.tree.TreePanel.superclass.constructor.call(this);
16314     this.el = Roo.get(el);
16315     this.el.addClass('x-tree');
16316     //console.log(root);
16317     if (root) {
16318         this.setRootNode( Roo.factory(root, Roo.tree));
16319     }
16320     if (loader) {
16321         this.loader = Roo.factory(loader, Roo.tree);
16322     }
16323    /**
16324     * Read-only. The id of the container element becomes this TreePanel's id.
16325     */
16326    this.id = this.el.id;
16327    this.addEvents({
16328         /**
16329         * @event beforeload
16330         * Fires before a node is loaded, return false to cancel
16331         * @param {Node} node The node being loaded
16332         */
16333         "beforeload" : true,
16334         /**
16335         * @event load
16336         * Fires when a node is loaded
16337         * @param {Node} node The node that was loaded
16338         */
16339         "load" : true,
16340         /**
16341         * @event textchange
16342         * Fires when the text for a node is changed
16343         * @param {Node} node The node
16344         * @param {String} text The new text
16345         * @param {String} oldText The old text
16346         */
16347         "textchange" : true,
16348         /**
16349         * @event beforeexpand
16350         * Fires before a node is expanded, return false to cancel.
16351         * @param {Node} node The node
16352         * @param {Boolean} deep
16353         * @param {Boolean} anim
16354         */
16355         "beforeexpand" : true,
16356         /**
16357         * @event beforecollapse
16358         * Fires before a node is collapsed, return false to cancel.
16359         * @param {Node} node The node
16360         * @param {Boolean} deep
16361         * @param {Boolean} anim
16362         */
16363         "beforecollapse" : true,
16364         /**
16365         * @event expand
16366         * Fires when a node is expanded
16367         * @param {Node} node The node
16368         */
16369         "expand" : true,
16370         /**
16371         * @event disabledchange
16372         * Fires when the disabled status of a node changes
16373         * @param {Node} node The node
16374         * @param {Boolean} disabled
16375         */
16376         "disabledchange" : true,
16377         /**
16378         * @event collapse
16379         * Fires when a node is collapsed
16380         * @param {Node} node The node
16381         */
16382         "collapse" : true,
16383         /**
16384         * @event beforeclick
16385         * Fires before click processing on a node. Return false to cancel the default action.
16386         * @param {Node} node The node
16387         * @param {Roo.EventObject} e The event object
16388         */
16389         "beforeclick":true,
16390         /**
16391         * @event checkchange
16392         * Fires when a node with a checkbox's checked property changes
16393         * @param {Node} this This node
16394         * @param {Boolean} checked
16395         */
16396         "checkchange":true,
16397         /**
16398         * @event click
16399         * Fires when a node is clicked
16400         * @param {Node} node The node
16401         * @param {Roo.EventObject} e The event object
16402         */
16403         "click":true,
16404         /**
16405         * @event dblclick
16406         * Fires when a node is double clicked
16407         * @param {Node} node The node
16408         * @param {Roo.EventObject} e The event object
16409         */
16410         "dblclick":true,
16411         /**
16412         * @event contextmenu
16413         * Fires when a node is right clicked
16414         * @param {Node} node The node
16415         * @param {Roo.EventObject} e The event object
16416         */
16417         "contextmenu":true,
16418         /**
16419         * @event beforechildrenrendered
16420         * Fires right before the child nodes for a node are rendered
16421         * @param {Node} node The node
16422         */
16423         "beforechildrenrendered":true,
16424        /**
16425              * @event startdrag
16426              * Fires when a node starts being dragged
16427              * @param {Roo.tree.TreePanel} this
16428              * @param {Roo.tree.TreeNode} node
16429              * @param {event} e The raw browser event
16430              */ 
16431             "startdrag" : true,
16432             /**
16433              * @event enddrag
16434              * Fires when a drag operation is complete
16435              * @param {Roo.tree.TreePanel} this
16436              * @param {Roo.tree.TreeNode} node
16437              * @param {event} e The raw browser event
16438              */
16439             "enddrag" : true,
16440             /**
16441              * @event dragdrop
16442              * Fires when a dragged node is dropped on a valid DD target
16443              * @param {Roo.tree.TreePanel} this
16444              * @param {Roo.tree.TreeNode} node
16445              * @param {DD} dd The dd it was dropped on
16446              * @param {event} e The raw browser event
16447              */
16448             "dragdrop" : true,
16449             /**
16450              * @event beforenodedrop
16451              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16452              * passed to handlers has the following properties:<br />
16453              * <ul style="padding:5px;padding-left:16px;">
16454              * <li>tree - The TreePanel</li>
16455              * <li>target - The node being targeted for the drop</li>
16456              * <li>data - The drag data from the drag source</li>
16457              * <li>point - The point of the drop - append, above or below</li>
16458              * <li>source - The drag source</li>
16459              * <li>rawEvent - Raw mouse event</li>
16460              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16461              * to be inserted by setting them on this object.</li>
16462              * <li>cancel - Set this to true to cancel the drop.</li>
16463              * </ul>
16464              * @param {Object} dropEvent
16465              */
16466             "beforenodedrop" : true,
16467             /**
16468              * @event nodedrop
16469              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16470              * passed to handlers has the following properties:<br />
16471              * <ul style="padding:5px;padding-left:16px;">
16472              * <li>tree - The TreePanel</li>
16473              * <li>target - The node being targeted for the drop</li>
16474              * <li>data - The drag data from the drag source</li>
16475              * <li>point - The point of the drop - append, above or below</li>
16476              * <li>source - The drag source</li>
16477              * <li>rawEvent - Raw mouse event</li>
16478              * <li>dropNode - Dropped node(s).</li>
16479              * </ul>
16480              * @param {Object} dropEvent
16481              */
16482             "nodedrop" : true,
16483              /**
16484              * @event nodedragover
16485              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16486              * passed to handlers has the following properties:<br />
16487              * <ul style="padding:5px;padding-left:16px;">
16488              * <li>tree - The TreePanel</li>
16489              * <li>target - The node being targeted for the drop</li>
16490              * <li>data - The drag data from the drag source</li>
16491              * <li>point - The point of the drop - append, above or below</li>
16492              * <li>source - The drag source</li>
16493              * <li>rawEvent - Raw mouse event</li>
16494              * <li>dropNode - Drop node(s) provided by the source.</li>
16495              * <li>cancel - Set this to true to signal drop not allowed.</li>
16496              * </ul>
16497              * @param {Object} dragOverEvent
16498              */
16499             "nodedragover" : true
16500         
16501    });
16502    if(this.singleExpand){
16503        this.on("beforeexpand", this.restrictExpand, this);
16504    }
16505 };
16506 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16507     rootVisible : true,
16508     animate: Roo.enableFx,
16509     lines : true,
16510     enableDD : false,
16511     hlDrop : Roo.enableFx,
16512   
16513     renderer: false,
16514     
16515     rendererTip: false,
16516     // private
16517     restrictExpand : function(node){
16518         var p = node.parentNode;
16519         if(p){
16520             if(p.expandedChild && p.expandedChild.parentNode == p){
16521                 p.expandedChild.collapse();
16522             }
16523             p.expandedChild = node;
16524         }
16525     },
16526
16527     // private override
16528     setRootNode : function(node){
16529         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16530         if(!this.rootVisible){
16531             node.ui = new Roo.tree.RootTreeNodeUI(node);
16532         }
16533         return node;
16534     },
16535
16536     /**
16537      * Returns the container element for this TreePanel
16538      */
16539     getEl : function(){
16540         return this.el;
16541     },
16542
16543     /**
16544      * Returns the default TreeLoader for this TreePanel
16545      */
16546     getLoader : function(){
16547         return this.loader;
16548     },
16549
16550     /**
16551      * Expand all nodes
16552      */
16553     expandAll : function(){
16554         this.root.expand(true);
16555     },
16556
16557     /**
16558      * Collapse all nodes
16559      */
16560     collapseAll : function(){
16561         this.root.collapse(true);
16562     },
16563
16564     /**
16565      * Returns the selection model used by this TreePanel
16566      */
16567     getSelectionModel : function(){
16568         if(!this.selModel){
16569             this.selModel = new Roo.tree.DefaultSelectionModel();
16570         }
16571         return this.selModel;
16572     },
16573
16574     /**
16575      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16576      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16577      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16578      * @return {Array}
16579      */
16580     getChecked : function(a, startNode){
16581         startNode = startNode || this.root;
16582         var r = [];
16583         var f = function(){
16584             if(this.attributes.checked){
16585                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16586             }
16587         }
16588         startNode.cascade(f);
16589         return r;
16590     },
16591
16592     /**
16593      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16594      * @param {String} path
16595      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16596      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16597      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16598      */
16599     expandPath : function(path, attr, callback){
16600         attr = attr || "id";
16601         var keys = path.split(this.pathSeparator);
16602         var curNode = this.root;
16603         if(curNode.attributes[attr] != keys[1]){ // invalid root
16604             if(callback){
16605                 callback(false, null);
16606             }
16607             return;
16608         }
16609         var index = 1;
16610         var f = function(){
16611             if(++index == keys.length){
16612                 if(callback){
16613                     callback(true, curNode);
16614                 }
16615                 return;
16616             }
16617             var c = curNode.findChild(attr, keys[index]);
16618             if(!c){
16619                 if(callback){
16620                     callback(false, curNode);
16621                 }
16622                 return;
16623             }
16624             curNode = c;
16625             c.expand(false, false, f);
16626         };
16627         curNode.expand(false, false, f);
16628     },
16629
16630     /**
16631      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16632      * @param {String} path
16633      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16634      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16635      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16636      */
16637     selectPath : function(path, attr, callback){
16638         attr = attr || "id";
16639         var keys = path.split(this.pathSeparator);
16640         var v = keys.pop();
16641         if(keys.length > 0){
16642             var f = function(success, node){
16643                 if(success && node){
16644                     var n = node.findChild(attr, v);
16645                     if(n){
16646                         n.select();
16647                         if(callback){
16648                             callback(true, n);
16649                         }
16650                     }else if(callback){
16651                         callback(false, n);
16652                     }
16653                 }else{
16654                     if(callback){
16655                         callback(false, n);
16656                     }
16657                 }
16658             };
16659             this.expandPath(keys.join(this.pathSeparator), attr, f);
16660         }else{
16661             this.root.select();
16662             if(callback){
16663                 callback(true, this.root);
16664             }
16665         }
16666     },
16667
16668     getTreeEl : function(){
16669         return this.el;
16670     },
16671
16672     /**
16673      * Trigger rendering of this TreePanel
16674      */
16675     render : function(){
16676         if (this.innerCt) {
16677             return this; // stop it rendering more than once!!
16678         }
16679         
16680         this.innerCt = this.el.createChild({tag:"ul",
16681                cls:"x-tree-root-ct " +
16682                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16683
16684         if(this.containerScroll){
16685             Roo.dd.ScrollManager.register(this.el);
16686         }
16687         if((this.enableDD || this.enableDrop) && !this.dropZone){
16688            /**
16689             * The dropZone used by this tree if drop is enabled
16690             * @type Roo.tree.TreeDropZone
16691             */
16692              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16693                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16694            });
16695         }
16696         if((this.enableDD || this.enableDrag) && !this.dragZone){
16697            /**
16698             * The dragZone used by this tree if drag is enabled
16699             * @type Roo.tree.TreeDragZone
16700             */
16701             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16702                ddGroup: this.ddGroup || "TreeDD",
16703                scroll: this.ddScroll
16704            });
16705         }
16706         this.getSelectionModel().init(this);
16707         if (!this.root) {
16708             console.log("ROOT not set in tree");
16709             return;
16710         }
16711         this.root.render();
16712         if(!this.rootVisible){
16713             this.root.renderChildren();
16714         }
16715         return this;
16716     }
16717 });/*
16718  * Based on:
16719  * Ext JS Library 1.1.1
16720  * Copyright(c) 2006-2007, Ext JS, LLC.
16721  *
16722  * Originally Released Under LGPL - original licence link has changed is not relivant.
16723  *
16724  * Fork - LGPL
16725  * <script type="text/javascript">
16726  */
16727  
16728
16729 /**
16730  * @class Roo.tree.DefaultSelectionModel
16731  * @extends Roo.util.Observable
16732  * The default single selection for a TreePanel.
16733  */
16734 Roo.tree.DefaultSelectionModel = function(){
16735    this.selNode = null;
16736    
16737    this.addEvents({
16738        /**
16739         * @event selectionchange
16740         * Fires when the selected node changes
16741         * @param {DefaultSelectionModel} this
16742         * @param {TreeNode} node the new selection
16743         */
16744        "selectionchange" : true,
16745
16746        /**
16747         * @event beforeselect
16748         * Fires before the selected node changes, return false to cancel the change
16749         * @param {DefaultSelectionModel} this
16750         * @param {TreeNode} node the new selection
16751         * @param {TreeNode} node the old selection
16752         */
16753        "beforeselect" : true
16754    });
16755 };
16756
16757 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16758     init : function(tree){
16759         this.tree = tree;
16760         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16761         tree.on("click", this.onNodeClick, this);
16762     },
16763     
16764     onNodeClick : function(node, e){
16765         if (e.ctrlKey && this.selNode == node)  {
16766             this.unselect(node);
16767             return;
16768         }
16769         this.select(node);
16770     },
16771     
16772     /**
16773      * Select a node.
16774      * @param {TreeNode} node The node to select
16775      * @return {TreeNode} The selected node
16776      */
16777     select : function(node){
16778         var last = this.selNode;
16779         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16780             if(last){
16781                 last.ui.onSelectedChange(false);
16782             }
16783             this.selNode = node;
16784             node.ui.onSelectedChange(true);
16785             this.fireEvent("selectionchange", this, node, last);
16786         }
16787         return node;
16788     },
16789     
16790     /**
16791      * Deselect a node.
16792      * @param {TreeNode} node The node to unselect
16793      */
16794     unselect : function(node){
16795         if(this.selNode == node){
16796             this.clearSelections();
16797         }    
16798     },
16799     
16800     /**
16801      * Clear all selections
16802      */
16803     clearSelections : function(){
16804         var n = this.selNode;
16805         if(n){
16806             n.ui.onSelectedChange(false);
16807             this.selNode = null;
16808             this.fireEvent("selectionchange", this, null);
16809         }
16810         return n;
16811     },
16812     
16813     /**
16814      * Get the selected node
16815      * @return {TreeNode} The selected node
16816      */
16817     getSelectedNode : function(){
16818         return this.selNode;    
16819     },
16820     
16821     /**
16822      * Returns true if the node is selected
16823      * @param {TreeNode} node The node to check
16824      * @return {Boolean}
16825      */
16826     isSelected : function(node){
16827         return this.selNode == node;  
16828     },
16829
16830     /**
16831      * Selects the node above the selected node in the tree, intelligently walking the nodes
16832      * @return TreeNode The new selection
16833      */
16834     selectPrevious : function(){
16835         var s = this.selNode || this.lastSelNode;
16836         if(!s){
16837             return null;
16838         }
16839         var ps = s.previousSibling;
16840         if(ps){
16841             if(!ps.isExpanded() || ps.childNodes.length < 1){
16842                 return this.select(ps);
16843             } else{
16844                 var lc = ps.lastChild;
16845                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16846                     lc = lc.lastChild;
16847                 }
16848                 return this.select(lc);
16849             }
16850         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16851             return this.select(s.parentNode);
16852         }
16853         return null;
16854     },
16855
16856     /**
16857      * Selects the node above the selected node in the tree, intelligently walking the nodes
16858      * @return TreeNode The new selection
16859      */
16860     selectNext : function(){
16861         var s = this.selNode || this.lastSelNode;
16862         if(!s){
16863             return null;
16864         }
16865         if(s.firstChild && s.isExpanded()){
16866              return this.select(s.firstChild);
16867          }else if(s.nextSibling){
16868              return this.select(s.nextSibling);
16869          }else if(s.parentNode){
16870             var newS = null;
16871             s.parentNode.bubble(function(){
16872                 if(this.nextSibling){
16873                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16874                     return false;
16875                 }
16876             });
16877             return newS;
16878          }
16879         return null;
16880     },
16881
16882     onKeyDown : function(e){
16883         var s = this.selNode || this.lastSelNode;
16884         // undesirable, but required
16885         var sm = this;
16886         if(!s){
16887             return;
16888         }
16889         var k = e.getKey();
16890         switch(k){
16891              case e.DOWN:
16892                  e.stopEvent();
16893                  this.selectNext();
16894              break;
16895              case e.UP:
16896                  e.stopEvent();
16897                  this.selectPrevious();
16898              break;
16899              case e.RIGHT:
16900                  e.preventDefault();
16901                  if(s.hasChildNodes()){
16902                      if(!s.isExpanded()){
16903                          s.expand();
16904                      }else if(s.firstChild){
16905                          this.select(s.firstChild, e);
16906                      }
16907                  }
16908              break;
16909              case e.LEFT:
16910                  e.preventDefault();
16911                  if(s.hasChildNodes() && s.isExpanded()){
16912                      s.collapse();
16913                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16914                      this.select(s.parentNode, e);
16915                  }
16916              break;
16917         };
16918     }
16919 });
16920
16921 /**
16922  * @class Roo.tree.MultiSelectionModel
16923  * @extends Roo.util.Observable
16924  * Multi selection for a TreePanel.
16925  */
16926 Roo.tree.MultiSelectionModel = function(){
16927    this.selNodes = [];
16928    this.selMap = {};
16929    this.addEvents({
16930        /**
16931         * @event selectionchange
16932         * Fires when the selected nodes change
16933         * @param {MultiSelectionModel} this
16934         * @param {Array} nodes Array of the selected nodes
16935         */
16936        "selectionchange" : true
16937    });
16938 };
16939
16940 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16941     init : function(tree){
16942         this.tree = tree;
16943         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16944         tree.on("click", this.onNodeClick, this);
16945     },
16946     
16947     onNodeClick : function(node, e){
16948         this.select(node, e, e.ctrlKey);
16949     },
16950     
16951     /**
16952      * Select a node.
16953      * @param {TreeNode} node The node to select
16954      * @param {EventObject} e (optional) An event associated with the selection
16955      * @param {Boolean} keepExisting True to retain existing selections
16956      * @return {TreeNode} The selected node
16957      */
16958     select : function(node, e, keepExisting){
16959         if(keepExisting !== true){
16960             this.clearSelections(true);
16961         }
16962         if(this.isSelected(node)){
16963             this.lastSelNode = node;
16964             return node;
16965         }
16966         this.selNodes.push(node);
16967         this.selMap[node.id] = node;
16968         this.lastSelNode = node;
16969         node.ui.onSelectedChange(true);
16970         this.fireEvent("selectionchange", this, this.selNodes);
16971         return node;
16972     },
16973     
16974     /**
16975      * Deselect a node.
16976      * @param {TreeNode} node The node to unselect
16977      */
16978     unselect : function(node){
16979         if(this.selMap[node.id]){
16980             node.ui.onSelectedChange(false);
16981             var sn = this.selNodes;
16982             var index = -1;
16983             if(sn.indexOf){
16984                 index = sn.indexOf(node);
16985             }else{
16986                 for(var i = 0, len = sn.length; i < len; i++){
16987                     if(sn[i] == node){
16988                         index = i;
16989                         break;
16990                     }
16991                 }
16992             }
16993             if(index != -1){
16994                 this.selNodes.splice(index, 1);
16995             }
16996             delete this.selMap[node.id];
16997             this.fireEvent("selectionchange", this, this.selNodes);
16998         }
16999     },
17000     
17001     /**
17002      * Clear all selections
17003      */
17004     clearSelections : function(suppressEvent){
17005         var sn = this.selNodes;
17006         if(sn.length > 0){
17007             for(var i = 0, len = sn.length; i < len; i++){
17008                 sn[i].ui.onSelectedChange(false);
17009             }
17010             this.selNodes = [];
17011             this.selMap = {};
17012             if(suppressEvent !== true){
17013                 this.fireEvent("selectionchange", this, this.selNodes);
17014             }
17015         }
17016     },
17017     
17018     /**
17019      * Returns true if the node is selected
17020      * @param {TreeNode} node The node to check
17021      * @return {Boolean}
17022      */
17023     isSelected : function(node){
17024         return this.selMap[node.id] ? true : false;  
17025     },
17026     
17027     /**
17028      * Returns an array of the selected nodes
17029      * @return {Array}
17030      */
17031     getSelectedNodes : function(){
17032         return this.selNodes;    
17033     },
17034
17035     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17036
17037     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17038
17039     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17040 });/*
17041  * Based on:
17042  * Ext JS Library 1.1.1
17043  * Copyright(c) 2006-2007, Ext JS, LLC.
17044  *
17045  * Originally Released Under LGPL - original licence link has changed is not relivant.
17046  *
17047  * Fork - LGPL
17048  * <script type="text/javascript">
17049  */
17050  
17051 /**
17052  * @class Roo.tree.TreeNode
17053  * @extends Roo.data.Node
17054  * @cfg {String} text The text for this node
17055  * @cfg {Boolean} expanded true to start the node expanded
17056  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17057  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17058  * @cfg {Boolean} disabled true to start the node disabled
17059  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17060  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17061  * @cfg {String} cls A css class to be added to the node
17062  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17063  * @cfg {String} href URL of the link used for the node (defaults to #)
17064  * @cfg {String} hrefTarget target frame for the link
17065  * @cfg {String} qtip An Ext QuickTip for the node
17066  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17067  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17068  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17069  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17070  * (defaults to undefined with no checkbox rendered)
17071  * @constructor
17072  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17073  */
17074 Roo.tree.TreeNode = function(attributes){
17075     attributes = attributes || {};
17076     if(typeof attributes == "string"){
17077         attributes = {text: attributes};
17078     }
17079     this.childrenRendered = false;
17080     this.rendered = false;
17081     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17082     this.expanded = attributes.expanded === true;
17083     this.isTarget = attributes.isTarget !== false;
17084     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17085     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17086
17087     /**
17088      * Read-only. The text for this node. To change it use setText().
17089      * @type String
17090      */
17091     this.text = attributes.text;
17092     /**
17093      * True if this node is disabled.
17094      * @type Boolean
17095      */
17096     this.disabled = attributes.disabled === true;
17097
17098     this.addEvents({
17099         /**
17100         * @event textchange
17101         * Fires when the text for this node is changed
17102         * @param {Node} this This node
17103         * @param {String} text The new text
17104         * @param {String} oldText The old text
17105         */
17106         "textchange" : true,
17107         /**
17108         * @event beforeexpand
17109         * Fires before this node is expanded, return false to cancel.
17110         * @param {Node} this This node
17111         * @param {Boolean} deep
17112         * @param {Boolean} anim
17113         */
17114         "beforeexpand" : true,
17115         /**
17116         * @event beforecollapse
17117         * Fires before this node is collapsed, return false to cancel.
17118         * @param {Node} this This node
17119         * @param {Boolean} deep
17120         * @param {Boolean} anim
17121         */
17122         "beforecollapse" : true,
17123         /**
17124         * @event expand
17125         * Fires when this node is expanded
17126         * @param {Node} this This node
17127         */
17128         "expand" : true,
17129         /**
17130         * @event disabledchange
17131         * Fires when the disabled status of this node changes
17132         * @param {Node} this This node
17133         * @param {Boolean} disabled
17134         */
17135         "disabledchange" : true,
17136         /**
17137         * @event collapse
17138         * Fires when this node is collapsed
17139         * @param {Node} this This node
17140         */
17141         "collapse" : true,
17142         /**
17143         * @event beforeclick
17144         * Fires before click processing. Return false to cancel the default action.
17145         * @param {Node} this This node
17146         * @param {Roo.EventObject} e The event object
17147         */
17148         "beforeclick":true,
17149         /**
17150         * @event checkchange
17151         * Fires when a node with a checkbox's checked property changes
17152         * @param {Node} this This node
17153         * @param {Boolean} checked
17154         */
17155         "checkchange":true,
17156         /**
17157         * @event click
17158         * Fires when this node is clicked
17159         * @param {Node} this This node
17160         * @param {Roo.EventObject} e The event object
17161         */
17162         "click":true,
17163         /**
17164         * @event dblclick
17165         * Fires when this node is double clicked
17166         * @param {Node} this This node
17167         * @param {Roo.EventObject} e The event object
17168         */
17169         "dblclick":true,
17170         /**
17171         * @event contextmenu
17172         * Fires when this node is right clicked
17173         * @param {Node} this This node
17174         * @param {Roo.EventObject} e The event object
17175         */
17176         "contextmenu":true,
17177         /**
17178         * @event beforechildrenrendered
17179         * Fires right before the child nodes for this node are rendered
17180         * @param {Node} this This node
17181         */
17182         "beforechildrenrendered":true
17183     });
17184
17185     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17186
17187     /**
17188      * Read-only. The UI for this node
17189      * @type TreeNodeUI
17190      */
17191     this.ui = new uiClass(this);
17192 };
17193 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17194     preventHScroll: true,
17195     /**
17196      * Returns true if this node is expanded
17197      * @return {Boolean}
17198      */
17199     isExpanded : function(){
17200         return this.expanded;
17201     },
17202
17203     /**
17204      * Returns the UI object for this node
17205      * @return {TreeNodeUI}
17206      */
17207     getUI : function(){
17208         return this.ui;
17209     },
17210
17211     // private override
17212     setFirstChild : function(node){
17213         var of = this.firstChild;
17214         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17215         if(this.childrenRendered && of && node != of){
17216             of.renderIndent(true, true);
17217         }
17218         if(this.rendered){
17219             this.renderIndent(true, true);
17220         }
17221     },
17222
17223     // private override
17224     setLastChild : function(node){
17225         var ol = this.lastChild;
17226         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17227         if(this.childrenRendered && ol && node != ol){
17228             ol.renderIndent(true, true);
17229         }
17230         if(this.rendered){
17231             this.renderIndent(true, true);
17232         }
17233     },
17234
17235     // these methods are overridden to provide lazy rendering support
17236     // private override
17237     appendChild : function(){
17238         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17239         if(node && this.childrenRendered){
17240             node.render();
17241         }
17242         this.ui.updateExpandIcon();
17243         return node;
17244     },
17245
17246     // private override
17247     removeChild : function(node){
17248         this.ownerTree.getSelectionModel().unselect(node);
17249         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17250         // if it's been rendered remove dom node
17251         if(this.childrenRendered){
17252             node.ui.remove();
17253         }
17254         if(this.childNodes.length < 1){
17255             this.collapse(false, false);
17256         }else{
17257             this.ui.updateExpandIcon();
17258         }
17259         if(!this.firstChild) {
17260             this.childrenRendered = false;
17261         }
17262         return node;
17263     },
17264
17265     // private override
17266     insertBefore : function(node, refNode){
17267         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17268         if(newNode && refNode && this.childrenRendered){
17269             node.render();
17270         }
17271         this.ui.updateExpandIcon();
17272         return newNode;
17273     },
17274
17275     /**
17276      * Sets the text for this node
17277      * @param {String} text
17278      */
17279     setText : function(text){
17280         var oldText = this.text;
17281         this.text = text;
17282         this.attributes.text = text;
17283         if(this.rendered){ // event without subscribing
17284             this.ui.onTextChange(this, text, oldText);
17285         }
17286         this.fireEvent("textchange", this, text, oldText);
17287     },
17288
17289     /**
17290      * Triggers selection of this node
17291      */
17292     select : function(){
17293         this.getOwnerTree().getSelectionModel().select(this);
17294     },
17295
17296     /**
17297      * Triggers deselection of this node
17298      */
17299     unselect : function(){
17300         this.getOwnerTree().getSelectionModel().unselect(this);
17301     },
17302
17303     /**
17304      * Returns true if this node is selected
17305      * @return {Boolean}
17306      */
17307     isSelected : function(){
17308         return this.getOwnerTree().getSelectionModel().isSelected(this);
17309     },
17310
17311     /**
17312      * Expand this node.
17313      * @param {Boolean} deep (optional) True to expand all children as well
17314      * @param {Boolean} anim (optional) false to cancel the default animation
17315      * @param {Function} callback (optional) A callback to be called when
17316      * expanding this node completes (does not wait for deep expand to complete).
17317      * Called with 1 parameter, this node.
17318      */
17319     expand : function(deep, anim, callback){
17320         if(!this.expanded){
17321             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17322                 return;
17323             }
17324             if(!this.childrenRendered){
17325                 this.renderChildren();
17326             }
17327             this.expanded = true;
17328             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17329                 this.ui.animExpand(function(){
17330                     this.fireEvent("expand", this);
17331                     if(typeof callback == "function"){
17332                         callback(this);
17333                     }
17334                     if(deep === true){
17335                         this.expandChildNodes(true);
17336                     }
17337                 }.createDelegate(this));
17338                 return;
17339             }else{
17340                 this.ui.expand();
17341                 this.fireEvent("expand", this);
17342                 if(typeof callback == "function"){
17343                     callback(this);
17344                 }
17345             }
17346         }else{
17347            if(typeof callback == "function"){
17348                callback(this);
17349            }
17350         }
17351         if(deep === true){
17352             this.expandChildNodes(true);
17353         }
17354     },
17355
17356     isHiddenRoot : function(){
17357         return this.isRoot && !this.getOwnerTree().rootVisible;
17358     },
17359
17360     /**
17361      * Collapse this node.
17362      * @param {Boolean} deep (optional) True to collapse all children as well
17363      * @param {Boolean} anim (optional) false to cancel the default animation
17364      */
17365     collapse : function(deep, anim){
17366         if(this.expanded && !this.isHiddenRoot()){
17367             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17368                 return;
17369             }
17370             this.expanded = false;
17371             if((this.getOwnerTree().animate && anim !== false) || anim){
17372                 this.ui.animCollapse(function(){
17373                     this.fireEvent("collapse", this);
17374                     if(deep === true){
17375                         this.collapseChildNodes(true);
17376                     }
17377                 }.createDelegate(this));
17378                 return;
17379             }else{
17380                 this.ui.collapse();
17381                 this.fireEvent("collapse", this);
17382             }
17383         }
17384         if(deep === true){
17385             var cs = this.childNodes;
17386             for(var i = 0, len = cs.length; i < len; i++) {
17387                 cs[i].collapse(true, false);
17388             }
17389         }
17390     },
17391
17392     // private
17393     delayedExpand : function(delay){
17394         if(!this.expandProcId){
17395             this.expandProcId = this.expand.defer(delay, this);
17396         }
17397     },
17398
17399     // private
17400     cancelExpand : function(){
17401         if(this.expandProcId){
17402             clearTimeout(this.expandProcId);
17403         }
17404         this.expandProcId = false;
17405     },
17406
17407     /**
17408      * Toggles expanded/collapsed state of the node
17409      */
17410     toggle : function(){
17411         if(this.expanded){
17412             this.collapse();
17413         }else{
17414             this.expand();
17415         }
17416     },
17417
17418     /**
17419      * Ensures all parent nodes are expanded
17420      */
17421     ensureVisible : function(callback){
17422         var tree = this.getOwnerTree();
17423         tree.expandPath(this.parentNode.getPath(), false, function(){
17424             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17425             Roo.callback(callback);
17426         }.createDelegate(this));
17427     },
17428
17429     /**
17430      * Expand all child nodes
17431      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17432      */
17433     expandChildNodes : function(deep){
17434         var cs = this.childNodes;
17435         for(var i = 0, len = cs.length; i < len; i++) {
17436                 cs[i].expand(deep);
17437         }
17438     },
17439
17440     /**
17441      * Collapse all child nodes
17442      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17443      */
17444     collapseChildNodes : function(deep){
17445         var cs = this.childNodes;
17446         for(var i = 0, len = cs.length; i < len; i++) {
17447                 cs[i].collapse(deep);
17448         }
17449     },
17450
17451     /**
17452      * Disables this node
17453      */
17454     disable : function(){
17455         this.disabled = true;
17456         this.unselect();
17457         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17458             this.ui.onDisableChange(this, true);
17459         }
17460         this.fireEvent("disabledchange", this, true);
17461     },
17462
17463     /**
17464      * Enables this node
17465      */
17466     enable : function(){
17467         this.disabled = false;
17468         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17469             this.ui.onDisableChange(this, false);
17470         }
17471         this.fireEvent("disabledchange", this, false);
17472     },
17473
17474     // private
17475     renderChildren : function(suppressEvent){
17476         if(suppressEvent !== false){
17477             this.fireEvent("beforechildrenrendered", this);
17478         }
17479         var cs = this.childNodes;
17480         for(var i = 0, len = cs.length; i < len; i++){
17481             cs[i].render(true);
17482         }
17483         this.childrenRendered = true;
17484     },
17485
17486     // private
17487     sort : function(fn, scope){
17488         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17489         if(this.childrenRendered){
17490             var cs = this.childNodes;
17491             for(var i = 0, len = cs.length; i < len; i++){
17492                 cs[i].render(true);
17493             }
17494         }
17495     },
17496
17497     // private
17498     render : function(bulkRender){
17499         this.ui.render(bulkRender);
17500         if(!this.rendered){
17501             this.rendered = true;
17502             if(this.expanded){
17503                 this.expanded = false;
17504                 this.expand(false, false);
17505             }
17506         }
17507     },
17508
17509     // private
17510     renderIndent : function(deep, refresh){
17511         if(refresh){
17512             this.ui.childIndent = null;
17513         }
17514         this.ui.renderIndent();
17515         if(deep === true && this.childrenRendered){
17516             var cs = this.childNodes;
17517             for(var i = 0, len = cs.length; i < len; i++){
17518                 cs[i].renderIndent(true, refresh);
17519             }
17520         }
17521     }
17522 });/*
17523  * Based on:
17524  * Ext JS Library 1.1.1
17525  * Copyright(c) 2006-2007, Ext JS, LLC.
17526  *
17527  * Originally Released Under LGPL - original licence link has changed is not relivant.
17528  *
17529  * Fork - LGPL
17530  * <script type="text/javascript">
17531  */
17532  
17533 /**
17534  * @class Roo.tree.AsyncTreeNode
17535  * @extends Roo.tree.TreeNode
17536  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17537  * @constructor
17538  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17539  */
17540  Roo.tree.AsyncTreeNode = function(config){
17541     this.loaded = false;
17542     this.loading = false;
17543     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17544     /**
17545     * @event beforeload
17546     * Fires before this node is loaded, return false to cancel
17547     * @param {Node} this This node
17548     */
17549     this.addEvents({'beforeload':true, 'load': true});
17550     /**
17551     * @event load
17552     * Fires when this node is loaded
17553     * @param {Node} this This node
17554     */
17555     /**
17556      * The loader used by this node (defaults to using the tree's defined loader)
17557      * @type TreeLoader
17558      * @property loader
17559      */
17560 };
17561 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17562     expand : function(deep, anim, callback){
17563         if(this.loading){ // if an async load is already running, waiting til it's done
17564             var timer;
17565             var f = function(){
17566                 if(!this.loading){ // done loading
17567                     clearInterval(timer);
17568                     this.expand(deep, anim, callback);
17569                 }
17570             }.createDelegate(this);
17571             timer = setInterval(f, 200);
17572             return;
17573         }
17574         if(!this.loaded){
17575             if(this.fireEvent("beforeload", this) === false){
17576                 return;
17577             }
17578             this.loading = true;
17579             this.ui.beforeLoad(this);
17580             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17581             if(loader){
17582                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17583                 return;
17584             }
17585         }
17586         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17587     },
17588     
17589     /**
17590      * Returns true if this node is currently loading
17591      * @return {Boolean}
17592      */
17593     isLoading : function(){
17594         return this.loading;  
17595     },
17596     
17597     loadComplete : function(deep, anim, callback){
17598         this.loading = false;
17599         this.loaded = true;
17600         this.ui.afterLoad(this);
17601         this.fireEvent("load", this);
17602         this.expand(deep, anim, callback);
17603     },
17604     
17605     /**
17606      * Returns true if this node has been loaded
17607      * @return {Boolean}
17608      */
17609     isLoaded : function(){
17610         return this.loaded;
17611     },
17612     
17613     hasChildNodes : function(){
17614         if(!this.isLeaf() && !this.loaded){
17615             return true;
17616         }else{
17617             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17618         }
17619     },
17620
17621     /**
17622      * Trigger a reload for this node
17623      * @param {Function} callback
17624      */
17625     reload : function(callback){
17626         this.collapse(false, false);
17627         while(this.firstChild){
17628             this.removeChild(this.firstChild);
17629         }
17630         this.childrenRendered = false;
17631         this.loaded = false;
17632         if(this.isHiddenRoot()){
17633             this.expanded = false;
17634         }
17635         this.expand(false, false, callback);
17636     }
17637 });/*
17638  * Based on:
17639  * Ext JS Library 1.1.1
17640  * Copyright(c) 2006-2007, Ext JS, LLC.
17641  *
17642  * Originally Released Under LGPL - original licence link has changed is not relivant.
17643  *
17644  * Fork - LGPL
17645  * <script type="text/javascript">
17646  */
17647  
17648 /**
17649  * @class Roo.tree.TreeNodeUI
17650  * @constructor
17651  * @param {Object} node The node to render
17652  * The TreeNode UI implementation is separate from the
17653  * tree implementation. Unless you are customizing the tree UI,
17654  * you should never have to use this directly.
17655  */
17656 Roo.tree.TreeNodeUI = function(node){
17657     this.node = node;
17658     this.rendered = false;
17659     this.animating = false;
17660     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17661 };
17662
17663 Roo.tree.TreeNodeUI.prototype = {
17664     removeChild : function(node){
17665         if(this.rendered){
17666             this.ctNode.removeChild(node.ui.getEl());
17667         }
17668     },
17669
17670     beforeLoad : function(){
17671          this.addClass("x-tree-node-loading");
17672     },
17673
17674     afterLoad : function(){
17675          this.removeClass("x-tree-node-loading");
17676     },
17677
17678     onTextChange : function(node, text, oldText){
17679         if(this.rendered){
17680             this.textNode.innerHTML = text;
17681         }
17682     },
17683
17684     onDisableChange : function(node, state){
17685         this.disabled = state;
17686         if(state){
17687             this.addClass("x-tree-node-disabled");
17688         }else{
17689             this.removeClass("x-tree-node-disabled");
17690         }
17691     },
17692
17693     onSelectedChange : function(state){
17694         if(state){
17695             this.focus();
17696             this.addClass("x-tree-selected");
17697         }else{
17698             //this.blur();
17699             this.removeClass("x-tree-selected");
17700         }
17701     },
17702
17703     onMove : function(tree, node, oldParent, newParent, index, refNode){
17704         this.childIndent = null;
17705         if(this.rendered){
17706             var targetNode = newParent.ui.getContainer();
17707             if(!targetNode){//target not rendered
17708                 this.holder = document.createElement("div");
17709                 this.holder.appendChild(this.wrap);
17710                 return;
17711             }
17712             var insertBefore = refNode ? refNode.ui.getEl() : null;
17713             if(insertBefore){
17714                 targetNode.insertBefore(this.wrap, insertBefore);
17715             }else{
17716                 targetNode.appendChild(this.wrap);
17717             }
17718             this.node.renderIndent(true);
17719         }
17720     },
17721
17722     addClass : function(cls){
17723         if(this.elNode){
17724             Roo.fly(this.elNode).addClass(cls);
17725         }
17726     },
17727
17728     removeClass : function(cls){
17729         if(this.elNode){
17730             Roo.fly(this.elNode).removeClass(cls);
17731         }
17732     },
17733
17734     remove : function(){
17735         if(this.rendered){
17736             this.holder = document.createElement("div");
17737             this.holder.appendChild(this.wrap);
17738         }
17739     },
17740
17741     fireEvent : function(){
17742         return this.node.fireEvent.apply(this.node, arguments);
17743     },
17744
17745     initEvents : function(){
17746         this.node.on("move", this.onMove, this);
17747         var E = Roo.EventManager;
17748         var a = this.anchor;
17749
17750         var el = Roo.fly(a, '_treeui');
17751
17752         if(Roo.isOpera){ // opera render bug ignores the CSS
17753             el.setStyle("text-decoration", "none");
17754         }
17755
17756         el.on("click", this.onClick, this);
17757         el.on("dblclick", this.onDblClick, this);
17758
17759         if(this.checkbox){
17760             Roo.EventManager.on(this.checkbox,
17761                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17762         }
17763
17764         el.on("contextmenu", this.onContextMenu, this);
17765
17766         var icon = Roo.fly(this.iconNode);
17767         icon.on("click", this.onClick, this);
17768         icon.on("dblclick", this.onDblClick, this);
17769         icon.on("contextmenu", this.onContextMenu, this);
17770         E.on(this.ecNode, "click", this.ecClick, this, true);
17771
17772         if(this.node.disabled){
17773             this.addClass("x-tree-node-disabled");
17774         }
17775         if(this.node.hidden){
17776             this.addClass("x-tree-node-disabled");
17777         }
17778         var ot = this.node.getOwnerTree();
17779         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17780         if(dd && (!this.node.isRoot || ot.rootVisible)){
17781             Roo.dd.Registry.register(this.elNode, {
17782                 node: this.node,
17783                 handles: this.getDDHandles(),
17784                 isHandle: false
17785             });
17786         }
17787     },
17788
17789     getDDHandles : function(){
17790         return [this.iconNode, this.textNode];
17791     },
17792
17793     hide : function(){
17794         if(this.rendered){
17795             this.wrap.style.display = "none";
17796         }
17797     },
17798
17799     show : function(){
17800         if(this.rendered){
17801             this.wrap.style.display = "";
17802         }
17803     },
17804
17805     onContextMenu : function(e){
17806         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17807             e.preventDefault();
17808             this.focus();
17809             this.fireEvent("contextmenu", this.node, e);
17810         }
17811     },
17812
17813     onClick : function(e){
17814         if(this.dropping){
17815             e.stopEvent();
17816             return;
17817         }
17818         if(this.fireEvent("beforeclick", this.node, e) !== false){
17819             if(!this.disabled && this.node.attributes.href){
17820                 this.fireEvent("click", this.node, e);
17821                 return;
17822             }
17823             e.preventDefault();
17824             if(this.disabled){
17825                 return;
17826             }
17827
17828             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17829                 this.node.toggle();
17830             }
17831
17832             this.fireEvent("click", this.node, e);
17833         }else{
17834             e.stopEvent();
17835         }
17836     },
17837
17838     onDblClick : function(e){
17839         e.preventDefault();
17840         if(this.disabled){
17841             return;
17842         }
17843         if(this.checkbox){
17844             this.toggleCheck();
17845         }
17846         if(!this.animating && this.node.hasChildNodes()){
17847             this.node.toggle();
17848         }
17849         this.fireEvent("dblclick", this.node, e);
17850     },
17851
17852     onCheckChange : function(){
17853         var checked = this.checkbox.checked;
17854         this.node.attributes.checked = checked;
17855         this.fireEvent('checkchange', this.node, checked);
17856     },
17857
17858     ecClick : function(e){
17859         if(!this.animating && this.node.hasChildNodes()){
17860             this.node.toggle();
17861         }
17862     },
17863
17864     startDrop : function(){
17865         this.dropping = true;
17866     },
17867
17868     // delayed drop so the click event doesn't get fired on a drop
17869     endDrop : function(){
17870        setTimeout(function(){
17871            this.dropping = false;
17872        }.createDelegate(this), 50);
17873     },
17874
17875     expand : function(){
17876         this.updateExpandIcon();
17877         this.ctNode.style.display = "";
17878     },
17879
17880     focus : function(){
17881         if(!this.node.preventHScroll){
17882             try{this.anchor.focus();
17883             }catch(e){}
17884         }else if(!Roo.isIE){
17885             try{
17886                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17887                 var l = noscroll.scrollLeft;
17888                 this.anchor.focus();
17889                 noscroll.scrollLeft = l;
17890             }catch(e){}
17891         }
17892     },
17893
17894     toggleCheck : function(value){
17895         var cb = this.checkbox;
17896         if(cb){
17897             cb.checked = (value === undefined ? !cb.checked : value);
17898         }
17899     },
17900
17901     blur : function(){
17902         try{
17903             this.anchor.blur();
17904         }catch(e){}
17905     },
17906
17907     animExpand : function(callback){
17908         var ct = Roo.get(this.ctNode);
17909         ct.stopFx();
17910         if(!this.node.hasChildNodes()){
17911             this.updateExpandIcon();
17912             this.ctNode.style.display = "";
17913             Roo.callback(callback);
17914             return;
17915         }
17916         this.animating = true;
17917         this.updateExpandIcon();
17918
17919         ct.slideIn('t', {
17920            callback : function(){
17921                this.animating = false;
17922                Roo.callback(callback);
17923             },
17924             scope: this,
17925             duration: this.node.ownerTree.duration || .25
17926         });
17927     },
17928
17929     highlight : function(){
17930         var tree = this.node.getOwnerTree();
17931         Roo.fly(this.wrap).highlight(
17932             tree.hlColor || "C3DAF9",
17933             {endColor: tree.hlBaseColor}
17934         );
17935     },
17936
17937     collapse : function(){
17938         this.updateExpandIcon();
17939         this.ctNode.style.display = "none";
17940     },
17941
17942     animCollapse : function(callback){
17943         var ct = Roo.get(this.ctNode);
17944         ct.enableDisplayMode('block');
17945         ct.stopFx();
17946
17947         this.animating = true;
17948         this.updateExpandIcon();
17949
17950         ct.slideOut('t', {
17951             callback : function(){
17952                this.animating = false;
17953                Roo.callback(callback);
17954             },
17955             scope: this,
17956             duration: this.node.ownerTree.duration || .25
17957         });
17958     },
17959
17960     getContainer : function(){
17961         return this.ctNode;
17962     },
17963
17964     getEl : function(){
17965         return this.wrap;
17966     },
17967
17968     appendDDGhost : function(ghostNode){
17969         ghostNode.appendChild(this.elNode.cloneNode(true));
17970     },
17971
17972     getDDRepairXY : function(){
17973         return Roo.lib.Dom.getXY(this.iconNode);
17974     },
17975
17976     onRender : function(){
17977         this.render();
17978     },
17979
17980     render : function(bulkRender){
17981         var n = this.node, a = n.attributes;
17982         var targetNode = n.parentNode ?
17983               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17984
17985         if(!this.rendered){
17986             this.rendered = true;
17987
17988             this.renderElements(n, a, targetNode, bulkRender);
17989
17990             if(a.qtip){
17991                if(this.textNode.setAttributeNS){
17992                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17993                    if(a.qtipTitle){
17994                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17995                    }
17996                }else{
17997                    this.textNode.setAttribute("ext:qtip", a.qtip);
17998                    if(a.qtipTitle){
17999                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18000                    }
18001                }
18002             }else if(a.qtipCfg){
18003                 a.qtipCfg.target = Roo.id(this.textNode);
18004                 Roo.QuickTips.register(a.qtipCfg);
18005             }
18006             this.initEvents();
18007             if(!this.node.expanded){
18008                 this.updateExpandIcon();
18009             }
18010         }else{
18011             if(bulkRender === true) {
18012                 targetNode.appendChild(this.wrap);
18013             }
18014         }
18015     },
18016
18017     renderElements : function(n, a, targetNode, bulkRender){
18018         // add some indent caching, this helps performance when rendering a large tree
18019         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18020         var t = n.getOwnerTree();
18021         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18022         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18023         var cb = typeof a.checked == 'boolean';
18024         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18025         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18026             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18027             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18028             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18029             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18030             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18031              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18032                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18033             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18034             "</li>"];
18035
18036         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18037             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18038                                 n.nextSibling.ui.getEl(), buf.join(""));
18039         }else{
18040             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18041         }
18042
18043         this.elNode = this.wrap.childNodes[0];
18044         this.ctNode = this.wrap.childNodes[1];
18045         var cs = this.elNode.childNodes;
18046         this.indentNode = cs[0];
18047         this.ecNode = cs[1];
18048         this.iconNode = cs[2];
18049         var index = 3;
18050         if(cb){
18051             this.checkbox = cs[3];
18052             index++;
18053         }
18054         this.anchor = cs[index];
18055         this.textNode = cs[index].firstChild;
18056     },
18057
18058     getAnchor : function(){
18059         return this.anchor;
18060     },
18061
18062     getTextEl : function(){
18063         return this.textNode;
18064     },
18065
18066     getIconEl : function(){
18067         return this.iconNode;
18068     },
18069
18070     isChecked : function(){
18071         return this.checkbox ? this.checkbox.checked : false;
18072     },
18073
18074     updateExpandIcon : function(){
18075         if(this.rendered){
18076             var n = this.node, c1, c2;
18077             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18078             var hasChild = n.hasChildNodes();
18079             if(hasChild){
18080                 if(n.expanded){
18081                     cls += "-minus";
18082                     c1 = "x-tree-node-collapsed";
18083                     c2 = "x-tree-node-expanded";
18084                 }else{
18085                     cls += "-plus";
18086                     c1 = "x-tree-node-expanded";
18087                     c2 = "x-tree-node-collapsed";
18088                 }
18089                 if(this.wasLeaf){
18090                     this.removeClass("x-tree-node-leaf");
18091                     this.wasLeaf = false;
18092                 }
18093                 if(this.c1 != c1 || this.c2 != c2){
18094                     Roo.fly(this.elNode).replaceClass(c1, c2);
18095                     this.c1 = c1; this.c2 = c2;
18096                 }
18097             }else{
18098                 if(!this.wasLeaf){
18099                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18100                     delete this.c1;
18101                     delete this.c2;
18102                     this.wasLeaf = true;
18103                 }
18104             }
18105             var ecc = "x-tree-ec-icon "+cls;
18106             if(this.ecc != ecc){
18107                 this.ecNode.className = ecc;
18108                 this.ecc = ecc;
18109             }
18110         }
18111     },
18112
18113     getChildIndent : function(){
18114         if(!this.childIndent){
18115             var buf = [];
18116             var p = this.node;
18117             while(p){
18118                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18119                     if(!p.isLast()) {
18120                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18121                     } else {
18122                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18123                     }
18124                 }
18125                 p = p.parentNode;
18126             }
18127             this.childIndent = buf.join("");
18128         }
18129         return this.childIndent;
18130     },
18131
18132     renderIndent : function(){
18133         if(this.rendered){
18134             var indent = "";
18135             var p = this.node.parentNode;
18136             if(p){
18137                 indent = p.ui.getChildIndent();
18138             }
18139             if(this.indentMarkup != indent){ // don't rerender if not required
18140                 this.indentNode.innerHTML = indent;
18141                 this.indentMarkup = indent;
18142             }
18143             this.updateExpandIcon();
18144         }
18145     }
18146 };
18147
18148 Roo.tree.RootTreeNodeUI = function(){
18149     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18150 };
18151 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18152     render : function(){
18153         if(!this.rendered){
18154             var targetNode = this.node.ownerTree.innerCt.dom;
18155             this.node.expanded = true;
18156             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18157             this.wrap = this.ctNode = targetNode.firstChild;
18158         }
18159     },
18160     collapse : function(){
18161     },
18162     expand : function(){
18163     }
18164 });/*
18165  * Based on:
18166  * Ext JS Library 1.1.1
18167  * Copyright(c) 2006-2007, Ext JS, LLC.
18168  *
18169  * Originally Released Under LGPL - original licence link has changed is not relivant.
18170  *
18171  * Fork - LGPL
18172  * <script type="text/javascript">
18173  */
18174 /**
18175  * @class Roo.tree.TreeLoader
18176  * @extends Roo.util.Observable
18177  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18178  * nodes from a specified URL. The response must be a javascript Array definition
18179  * who's elements are node definition objects. eg:
18180  * <pre><code>
18181    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18182     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18183 </code></pre>
18184  * <br><br>
18185  * A server request is sent, and child nodes are loaded only when a node is expanded.
18186  * The loading node's id is passed to the server under the parameter name "node" to
18187  * enable the server to produce the correct child nodes.
18188  * <br><br>
18189  * To pass extra parameters, an event handler may be attached to the "beforeload"
18190  * event, and the parameters specified in the TreeLoader's baseParams property:
18191  * <pre><code>
18192     myTreeLoader.on("beforeload", function(treeLoader, node) {
18193         this.baseParams.category = node.attributes.category;
18194     }, this);
18195 </code></pre><
18196  * This would pass an HTTP parameter called "category" to the server containing
18197  * the value of the Node's "category" attribute.
18198  * @constructor
18199  * Creates a new Treeloader.
18200  * @param {Object} config A config object containing config properties.
18201  */
18202 Roo.tree.TreeLoader = function(config){
18203     this.baseParams = {};
18204     this.requestMethod = "POST";
18205     Roo.apply(this, config);
18206
18207     this.addEvents({
18208     
18209         /**
18210          * @event beforeload
18211          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18212          * @param {Object} This TreeLoader object.
18213          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18214          * @param {Object} callback The callback function specified in the {@link #load} call.
18215          */
18216         beforeload : true,
18217         /**
18218          * @event load
18219          * Fires when the node has been successfuly loaded.
18220          * @param {Object} This TreeLoader object.
18221          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18222          * @param {Object} response The response object containing the data from the server.
18223          */
18224         load : true,
18225         /**
18226          * @event loadexception
18227          * Fires if the network request failed.
18228          * @param {Object} This TreeLoader object.
18229          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18230          * @param {Object} response The response object containing the data from the server.
18231          */
18232         loadexception : true,
18233         /**
18234          * @event create
18235          * Fires before a node is created, enabling you to return custom Node types 
18236          * @param {Object} This TreeLoader object.
18237          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18238          */
18239         create : true
18240     });
18241
18242     Roo.tree.TreeLoader.superclass.constructor.call(this);
18243 };
18244
18245 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18246     /**
18247     * @cfg {String} dataUrl The URL from which to request a Json string which
18248     * specifies an array of node definition object representing the child nodes
18249     * to be loaded.
18250     */
18251     /**
18252     * @cfg {Object} baseParams (optional) An object containing properties which
18253     * specify HTTP parameters to be passed to each request for child nodes.
18254     */
18255     /**
18256     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18257     * created by this loader. If the attributes sent by the server have an attribute in this object,
18258     * they take priority.
18259     */
18260     /**
18261     * @cfg {Object} uiProviders (optional) An object containing properties which
18262     * 
18263     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18264     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18265     * <i>uiProvider</i> attribute of a returned child node is a string rather
18266     * than a reference to a TreeNodeUI implementation, this that string value
18267     * is used as a property name in the uiProviders object. You can define the provider named
18268     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18269     */
18270     uiProviders : {},
18271
18272     /**
18273     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18274     * child nodes before loading.
18275     */
18276     clearOnLoad : true,
18277
18278     /**
18279     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18280     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18281     * Grid query { data : [ .....] }
18282     */
18283     
18284     root : false,
18285      /**
18286     * @cfg {String} queryParam (optional) 
18287     * Name of the query as it will be passed on the querystring (defaults to 'node')
18288     * eg. the request will be ?node=[id]
18289     */
18290     
18291     
18292     queryParam: false,
18293     
18294     /**
18295      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18296      * This is called automatically when a node is expanded, but may be used to reload
18297      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18298      * @param {Roo.tree.TreeNode} node
18299      * @param {Function} callback
18300      */
18301     load : function(node, callback){
18302         if(this.clearOnLoad){
18303             while(node.firstChild){
18304                 node.removeChild(node.firstChild);
18305             }
18306         }
18307         if(node.attributes.children){ // preloaded json children
18308             var cs = node.attributes.children;
18309             for(var i = 0, len = cs.length; i < len; i++){
18310                 node.appendChild(this.createNode(cs[i]));
18311             }
18312             if(typeof callback == "function"){
18313                 callback();
18314             }
18315         }else if(this.dataUrl){
18316             this.requestData(node, callback);
18317         }
18318     },
18319
18320     getParams: function(node){
18321         var buf = [], bp = this.baseParams;
18322         for(var key in bp){
18323             if(typeof bp[key] != "function"){
18324                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18325             }
18326         }
18327         var n = this.queryParam === false ? 'node' : this.queryParam;
18328         buf.push(n + "=", encodeURIComponent(node.id));
18329         return buf.join("");
18330     },
18331
18332     requestData : function(node, callback){
18333         if(this.fireEvent("beforeload", this, node, callback) !== false){
18334             this.transId = Roo.Ajax.request({
18335                 method:this.requestMethod,
18336                 url: this.dataUrl||this.url,
18337                 success: this.handleResponse,
18338                 failure: this.handleFailure,
18339                 scope: this,
18340                 argument: {callback: callback, node: node},
18341                 params: this.getParams(node)
18342             });
18343         }else{
18344             // if the load is cancelled, make sure we notify
18345             // the node that we are done
18346             if(typeof callback == "function"){
18347                 callback();
18348             }
18349         }
18350     },
18351
18352     isLoading : function(){
18353         return this.transId ? true : false;
18354     },
18355
18356     abort : function(){
18357         if(this.isLoading()){
18358             Roo.Ajax.abort(this.transId);
18359         }
18360     },
18361
18362     // private
18363     createNode : function(attr){
18364         // apply baseAttrs, nice idea Corey!
18365         if(this.baseAttrs){
18366             Roo.applyIf(attr, this.baseAttrs);
18367         }
18368         if(this.applyLoader !== false){
18369             attr.loader = this;
18370         }
18371         // uiProvider = depreciated..
18372         
18373         if(typeof(attr.uiProvider) == 'string'){
18374            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18375                 /**  eval:var:attr */ eval(attr.uiProvider);
18376         }
18377         if(typeof(this.uiProviders['default']) != 'undefined') {
18378             attr.uiProvider = this.uiProviders['default'];
18379         }
18380         
18381         this.fireEvent('create', this, attr);
18382         
18383         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18384         return(attr.leaf ?
18385                         new Roo.tree.TreeNode(attr) :
18386                         new Roo.tree.AsyncTreeNode(attr));
18387     },
18388
18389     processResponse : function(response, node, callback){
18390         var json = response.responseText;
18391         try {
18392             
18393             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18394             if (this.root !== false) {
18395                 o = o[this.root];
18396             }
18397             
18398             for(var i = 0, len = o.length; i < len; i++){
18399                 var n = this.createNode(o[i]);
18400                 if(n){
18401                     node.appendChild(n);
18402                 }
18403             }
18404             if(typeof callback == "function"){
18405                 callback(this, node);
18406             }
18407         }catch(e){
18408             this.handleFailure(response);
18409         }
18410     },
18411
18412     handleResponse : function(response){
18413         this.transId = false;
18414         var a = response.argument;
18415         this.processResponse(response, a.node, a.callback);
18416         this.fireEvent("load", this, a.node, response);
18417     },
18418
18419     handleFailure : function(response){
18420         this.transId = false;
18421         var a = response.argument;
18422         this.fireEvent("loadexception", this, a.node, response);
18423         if(typeof a.callback == "function"){
18424             a.callback(this, a.node);
18425         }
18426     }
18427 });/*
18428  * Based on:
18429  * Ext JS Library 1.1.1
18430  * Copyright(c) 2006-2007, Ext JS, LLC.
18431  *
18432  * Originally Released Under LGPL - original licence link has changed is not relivant.
18433  *
18434  * Fork - LGPL
18435  * <script type="text/javascript">
18436  */
18437
18438 /**
18439 * @class Roo.tree.TreeFilter
18440 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18441 * @param {TreePanel} tree
18442 * @param {Object} config (optional)
18443  */
18444 Roo.tree.TreeFilter = function(tree, config){
18445     this.tree = tree;
18446     this.filtered = {};
18447     Roo.apply(this, config);
18448 };
18449
18450 Roo.tree.TreeFilter.prototype = {
18451     clearBlank:false,
18452     reverse:false,
18453     autoClear:false,
18454     remove:false,
18455
18456      /**
18457      * Filter the data by a specific attribute.
18458      * @param {String/RegExp} value Either string that the attribute value
18459      * should start with or a RegExp to test against the attribute
18460      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18461      * @param {TreeNode} startNode (optional) The node to start the filter at.
18462      */
18463     filter : function(value, attr, startNode){
18464         attr = attr || "text";
18465         var f;
18466         if(typeof value == "string"){
18467             var vlen = value.length;
18468             // auto clear empty filter
18469             if(vlen == 0 && this.clearBlank){
18470                 this.clear();
18471                 return;
18472             }
18473             value = value.toLowerCase();
18474             f = function(n){
18475                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18476             };
18477         }else if(value.exec){ // regex?
18478             f = function(n){
18479                 return value.test(n.attributes[attr]);
18480             };
18481         }else{
18482             throw 'Illegal filter type, must be string or regex';
18483         }
18484         this.filterBy(f, null, startNode);
18485         },
18486
18487     /**
18488      * Filter by a function. The passed function will be called with each
18489      * node in the tree (or from the startNode). If the function returns true, the node is kept
18490      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18491      * @param {Function} fn The filter function
18492      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18493      */
18494     filterBy : function(fn, scope, startNode){
18495         startNode = startNode || this.tree.root;
18496         if(this.autoClear){
18497             this.clear();
18498         }
18499         var af = this.filtered, rv = this.reverse;
18500         var f = function(n){
18501             if(n == startNode){
18502                 return true;
18503             }
18504             if(af[n.id]){
18505                 return false;
18506             }
18507             var m = fn.call(scope || n, n);
18508             if(!m || rv){
18509                 af[n.id] = n;
18510                 n.ui.hide();
18511                 return false;
18512             }
18513             return true;
18514         };
18515         startNode.cascade(f);
18516         if(this.remove){
18517            for(var id in af){
18518                if(typeof id != "function"){
18519                    var n = af[id];
18520                    if(n && n.parentNode){
18521                        n.parentNode.removeChild(n);
18522                    }
18523                }
18524            }
18525         }
18526     },
18527
18528     /**
18529      * Clears the current filter. Note: with the "remove" option
18530      * set a filter cannot be cleared.
18531      */
18532     clear : function(){
18533         var t = this.tree;
18534         var af = this.filtered;
18535         for(var id in af){
18536             if(typeof id != "function"){
18537                 var n = af[id];
18538                 if(n){
18539                     n.ui.show();
18540                 }
18541             }
18542         }
18543         this.filtered = {};
18544     }
18545 };
18546 /*
18547  * Based on:
18548  * Ext JS Library 1.1.1
18549  * Copyright(c) 2006-2007, Ext JS, LLC.
18550  *
18551  * Originally Released Under LGPL - original licence link has changed is not relivant.
18552  *
18553  * Fork - LGPL
18554  * <script type="text/javascript">
18555  */
18556  
18557
18558 /**
18559  * @class Roo.tree.TreeSorter
18560  * Provides sorting of nodes in a TreePanel
18561  * 
18562  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18563  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18564  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18565  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18566  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18567  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18568  * @constructor
18569  * @param {TreePanel} tree
18570  * @param {Object} config
18571  */
18572 Roo.tree.TreeSorter = function(tree, config){
18573     Roo.apply(this, config);
18574     tree.on("beforechildrenrendered", this.doSort, this);
18575     tree.on("append", this.updateSort, this);
18576     tree.on("insert", this.updateSort, this);
18577     
18578     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18579     var p = this.property || "text";
18580     var sortType = this.sortType;
18581     var fs = this.folderSort;
18582     var cs = this.caseSensitive === true;
18583     var leafAttr = this.leafAttr || 'leaf';
18584
18585     this.sortFn = function(n1, n2){
18586         if(fs){
18587             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18588                 return 1;
18589             }
18590             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18591                 return -1;
18592             }
18593         }
18594         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18595         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18596         if(v1 < v2){
18597                         return dsc ? +1 : -1;
18598                 }else if(v1 > v2){
18599                         return dsc ? -1 : +1;
18600         }else{
18601                 return 0;
18602         }
18603     };
18604 };
18605
18606 Roo.tree.TreeSorter.prototype = {
18607     doSort : function(node){
18608         node.sort(this.sortFn);
18609     },
18610     
18611     compareNodes : function(n1, n2){
18612         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18613     },
18614     
18615     updateSort : function(tree, node){
18616         if(node.childrenRendered){
18617             this.doSort.defer(1, this, [node]);
18618         }
18619     }
18620 };/*
18621  * Based on:
18622  * Ext JS Library 1.1.1
18623  * Copyright(c) 2006-2007, Ext JS, LLC.
18624  *
18625  * Originally Released Under LGPL - original licence link has changed is not relivant.
18626  *
18627  * Fork - LGPL
18628  * <script type="text/javascript">
18629  */
18630
18631 if(Roo.dd.DropZone){
18632     
18633 Roo.tree.TreeDropZone = function(tree, config){
18634     this.allowParentInsert = false;
18635     this.allowContainerDrop = false;
18636     this.appendOnly = false;
18637     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18638     this.tree = tree;
18639     this.lastInsertClass = "x-tree-no-status";
18640     this.dragOverData = {};
18641 };
18642
18643 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18644     ddGroup : "TreeDD",
18645     
18646     expandDelay : 1000,
18647     
18648     expandNode : function(node){
18649         if(node.hasChildNodes() && !node.isExpanded()){
18650             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18651         }
18652     },
18653     
18654     queueExpand : function(node){
18655         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18656     },
18657     
18658     cancelExpand : function(){
18659         if(this.expandProcId){
18660             clearTimeout(this.expandProcId);
18661             this.expandProcId = false;
18662         }
18663     },
18664     
18665     isValidDropPoint : function(n, pt, dd, e, data){
18666         if(!n || !data){ return false; }
18667         var targetNode = n.node;
18668         var dropNode = data.node;
18669         // default drop rules
18670         if(!(targetNode && targetNode.isTarget && pt)){
18671             return false;
18672         }
18673         if(pt == "append" && targetNode.allowChildren === false){
18674             return false;
18675         }
18676         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18677             return false;
18678         }
18679         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18680             return false;
18681         }
18682         // reuse the object
18683         var overEvent = this.dragOverData;
18684         overEvent.tree = this.tree;
18685         overEvent.target = targetNode;
18686         overEvent.data = data;
18687         overEvent.point = pt;
18688         overEvent.source = dd;
18689         overEvent.rawEvent = e;
18690         overEvent.dropNode = dropNode;
18691         overEvent.cancel = false;  
18692         var result = this.tree.fireEvent("nodedragover", overEvent);
18693         return overEvent.cancel === false && result !== false;
18694     },
18695     
18696     getDropPoint : function(e, n, dd){
18697         var tn = n.node;
18698         if(tn.isRoot){
18699             return tn.allowChildren !== false ? "append" : false; // always append for root
18700         }
18701         var dragEl = n.ddel;
18702         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18703         var y = Roo.lib.Event.getPageY(e);
18704         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18705         
18706         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18707         var noAppend = tn.allowChildren === false;
18708         if(this.appendOnly || tn.parentNode.allowChildren === false){
18709             return noAppend ? false : "append";
18710         }
18711         var noBelow = false;
18712         if(!this.allowParentInsert){
18713             noBelow = tn.hasChildNodes() && tn.isExpanded();
18714         }
18715         var q = (b - t) / (noAppend ? 2 : 3);
18716         if(y >= t && y < (t + q)){
18717             return "above";
18718         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18719             return "below";
18720         }else{
18721             return "append";
18722         }
18723     },
18724     
18725     onNodeEnter : function(n, dd, e, data){
18726         this.cancelExpand();
18727     },
18728     
18729     onNodeOver : function(n, dd, e, data){
18730         var pt = this.getDropPoint(e, n, dd);
18731         var node = n.node;
18732         
18733         // auto node expand check
18734         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18735             this.queueExpand(node);
18736         }else if(pt != "append"){
18737             this.cancelExpand();
18738         }
18739         
18740         // set the insert point style on the target node
18741         var returnCls = this.dropNotAllowed;
18742         if(this.isValidDropPoint(n, pt, dd, e, data)){
18743            if(pt){
18744                var el = n.ddel;
18745                var cls;
18746                if(pt == "above"){
18747                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18748                    cls = "x-tree-drag-insert-above";
18749                }else if(pt == "below"){
18750                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18751                    cls = "x-tree-drag-insert-below";
18752                }else{
18753                    returnCls = "x-tree-drop-ok-append";
18754                    cls = "x-tree-drag-append";
18755                }
18756                if(this.lastInsertClass != cls){
18757                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18758                    this.lastInsertClass = cls;
18759                }
18760            }
18761        }
18762        return returnCls;
18763     },
18764     
18765     onNodeOut : function(n, dd, e, data){
18766         this.cancelExpand();
18767         this.removeDropIndicators(n);
18768     },
18769     
18770     onNodeDrop : function(n, dd, e, data){
18771         var point = this.getDropPoint(e, n, dd);
18772         var targetNode = n.node;
18773         targetNode.ui.startDrop();
18774         if(!this.isValidDropPoint(n, point, dd, e, data)){
18775             targetNode.ui.endDrop();
18776             return false;
18777         }
18778         // first try to find the drop node
18779         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18780         var dropEvent = {
18781             tree : this.tree,
18782             target: targetNode,
18783             data: data,
18784             point: point,
18785             source: dd,
18786             rawEvent: e,
18787             dropNode: dropNode,
18788             cancel: !dropNode   
18789         };
18790         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18791         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18792             targetNode.ui.endDrop();
18793             return false;
18794         }
18795         // allow target changing
18796         targetNode = dropEvent.target;
18797         if(point == "append" && !targetNode.isExpanded()){
18798             targetNode.expand(false, null, function(){
18799                 this.completeDrop(dropEvent);
18800             }.createDelegate(this));
18801         }else{
18802             this.completeDrop(dropEvent);
18803         }
18804         return true;
18805     },
18806     
18807     completeDrop : function(de){
18808         var ns = de.dropNode, p = de.point, t = de.target;
18809         if(!(ns instanceof Array)){
18810             ns = [ns];
18811         }
18812         var n;
18813         for(var i = 0, len = ns.length; i < len; i++){
18814             n = ns[i];
18815             if(p == "above"){
18816                 t.parentNode.insertBefore(n, t);
18817             }else if(p == "below"){
18818                 t.parentNode.insertBefore(n, t.nextSibling);
18819             }else{
18820                 t.appendChild(n);
18821             }
18822         }
18823         n.ui.focus();
18824         if(this.tree.hlDrop){
18825             n.ui.highlight();
18826         }
18827         t.ui.endDrop();
18828         this.tree.fireEvent("nodedrop", de);
18829     },
18830     
18831     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18832         if(this.tree.hlDrop){
18833             dropNode.ui.focus();
18834             dropNode.ui.highlight();
18835         }
18836         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18837     },
18838     
18839     getTree : function(){
18840         return this.tree;
18841     },
18842     
18843     removeDropIndicators : function(n){
18844         if(n && n.ddel){
18845             var el = n.ddel;
18846             Roo.fly(el).removeClass([
18847                     "x-tree-drag-insert-above",
18848                     "x-tree-drag-insert-below",
18849                     "x-tree-drag-append"]);
18850             this.lastInsertClass = "_noclass";
18851         }
18852     },
18853     
18854     beforeDragDrop : function(target, e, id){
18855         this.cancelExpand();
18856         return true;
18857     },
18858     
18859     afterRepair : function(data){
18860         if(data && Roo.enableFx){
18861             data.node.ui.highlight();
18862         }
18863         this.hideProxy();
18864     }    
18865 });
18866
18867 }
18868 /*
18869  * Based on:
18870  * Ext JS Library 1.1.1
18871  * Copyright(c) 2006-2007, Ext JS, LLC.
18872  *
18873  * Originally Released Under LGPL - original licence link has changed is not relivant.
18874  *
18875  * Fork - LGPL
18876  * <script type="text/javascript">
18877  */
18878  
18879
18880 if(Roo.dd.DragZone){
18881 Roo.tree.TreeDragZone = function(tree, config){
18882     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18883     this.tree = tree;
18884 };
18885
18886 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18887     ddGroup : "TreeDD",
18888     
18889     onBeforeDrag : function(data, e){
18890         var n = data.node;
18891         return n && n.draggable && !n.disabled;
18892     },
18893     
18894     onInitDrag : function(e){
18895         var data = this.dragData;
18896         this.tree.getSelectionModel().select(data.node);
18897         this.proxy.update("");
18898         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18899         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18900     },
18901     
18902     getRepairXY : function(e, data){
18903         return data.node.ui.getDDRepairXY();
18904     },
18905     
18906     onEndDrag : function(data, e){
18907         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18908     },
18909     
18910     onValidDrop : function(dd, e, id){
18911         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18912         this.hideProxy();
18913     },
18914     
18915     beforeInvalidDrop : function(e, id){
18916         // this scrolls the original position back into view
18917         var sm = this.tree.getSelectionModel();
18918         sm.clearSelections();
18919         sm.select(this.dragData.node);
18920     }
18921 });
18922 }/*
18923  * Based on:
18924  * Ext JS Library 1.1.1
18925  * Copyright(c) 2006-2007, Ext JS, LLC.
18926  *
18927  * Originally Released Under LGPL - original licence link has changed is not relivant.
18928  *
18929  * Fork - LGPL
18930  * <script type="text/javascript">
18931  */
18932 /**
18933  * @class Roo.tree.TreeEditor
18934  * @extends Roo.Editor
18935  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18936  * as the editor field.
18937  * @constructor
18938  * @param {TreePanel} tree
18939  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18940  */
18941 Roo.tree.TreeEditor = function(tree, config){
18942     config = config || {};
18943     var field = config.events ? config : new Roo.form.TextField(config);
18944     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18945
18946     this.tree = tree;
18947
18948     tree.on('beforeclick', this.beforeNodeClick, this);
18949     tree.getTreeEl().on('mousedown', this.hide, this);
18950     this.on('complete', this.updateNode, this);
18951     this.on('beforestartedit', this.fitToTree, this);
18952     this.on('startedit', this.bindScroll, this, {delay:10});
18953     this.on('specialkey', this.onSpecialKey, this);
18954 };
18955
18956 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18957     /**
18958      * @cfg {String} alignment
18959      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18960      */
18961     alignment: "l-l",
18962     // inherit
18963     autoSize: false,
18964     /**
18965      * @cfg {Boolean} hideEl
18966      * True to hide the bound element while the editor is displayed (defaults to false)
18967      */
18968     hideEl : false,
18969     /**
18970      * @cfg {String} cls
18971      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18972      */
18973     cls: "x-small-editor x-tree-editor",
18974     /**
18975      * @cfg {Boolean} shim
18976      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18977      */
18978     shim:false,
18979     // inherit
18980     shadow:"frame",
18981     /**
18982      * @cfg {Number} maxWidth
18983      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18984      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18985      * scroll and client offsets into account prior to each edit.
18986      */
18987     maxWidth: 250,
18988
18989     editDelay : 350,
18990
18991     // private
18992     fitToTree : function(ed, el){
18993         var td = this.tree.getTreeEl().dom, nd = el.dom;
18994         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18995             td.scrollLeft = nd.offsetLeft;
18996         }
18997         var w = Math.min(
18998                 this.maxWidth,
18999                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19000         this.setSize(w, '');
19001     },
19002
19003     // private
19004     triggerEdit : function(node){
19005         this.completeEdit();
19006         this.editNode = node;
19007         this.startEdit(node.ui.textNode, node.text);
19008     },
19009
19010     // private
19011     bindScroll : function(){
19012         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19013     },
19014
19015     // private
19016     beforeNodeClick : function(node, e){
19017         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19018         this.lastClick = new Date();
19019         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19020             e.stopEvent();
19021             this.triggerEdit(node);
19022             return false;
19023         }
19024     },
19025
19026     // private
19027     updateNode : function(ed, value){
19028         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19029         this.editNode.setText(value);
19030     },
19031
19032     // private
19033     onHide : function(){
19034         Roo.tree.TreeEditor.superclass.onHide.call(this);
19035         if(this.editNode){
19036             this.editNode.ui.focus();
19037         }
19038     },
19039
19040     // private
19041     onSpecialKey : function(field, e){
19042         var k = e.getKey();
19043         if(k == e.ESC){
19044             e.stopEvent();
19045             this.cancelEdit();
19046         }else if(k == e.ENTER && !e.hasModifier()){
19047             e.stopEvent();
19048             this.completeEdit();
19049         }
19050     }
19051 });//<Script type="text/javascript">
19052 /*
19053  * Based on:
19054  * Ext JS Library 1.1.1
19055  * Copyright(c) 2006-2007, Ext JS, LLC.
19056  *
19057  * Originally Released Under LGPL - original licence link has changed is not relivant.
19058  *
19059  * Fork - LGPL
19060  * <script type="text/javascript">
19061  */
19062  
19063 /**
19064  * Not documented??? - probably should be...
19065  */
19066
19067 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19068     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19069     
19070     renderElements : function(n, a, targetNode, bulkRender){
19071         //consel.log("renderElements?");
19072         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19073
19074         var t = n.getOwnerTree();
19075         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19076         
19077         var cols = t.columns;
19078         var bw = t.borderWidth;
19079         var c = cols[0];
19080         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19081          var cb = typeof a.checked == "boolean";
19082         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19083         var colcls = 'x-t-' + tid + '-c0';
19084         var buf = [
19085             '<li class="x-tree-node">',
19086             
19087                 
19088                 '<div class="x-tree-node-el ', a.cls,'">',
19089                     // extran...
19090                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19091                 
19092                 
19093                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19094                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19095                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19096                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19097                            (a.iconCls ? ' '+a.iconCls : ''),
19098                            '" unselectable="on" />',
19099                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19100                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19101                              
19102                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19103                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19104                             '<span unselectable="on" qtip="' + tx + '">',
19105                              tx,
19106                              '</span></a>' ,
19107                     '</div>',
19108                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19109                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19110                  ];
19111         for(var i = 1, len = cols.length; i < len; i++){
19112             c = cols[i];
19113             colcls = 'x-t-' + tid + '-c' +i;
19114             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19115             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19116                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19117                       "</div>");
19118          }
19119          
19120          buf.push(
19121             '</a>',
19122             '<div class="x-clear"></div></div>',
19123             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19124             "</li>");
19125         
19126         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19127             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19128                                 n.nextSibling.ui.getEl(), buf.join(""));
19129         }else{
19130             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19131         }
19132         var el = this.wrap.firstChild;
19133         this.elRow = el;
19134         this.elNode = el.firstChild;
19135         this.ranchor = el.childNodes[1];
19136         this.ctNode = this.wrap.childNodes[1];
19137         var cs = el.firstChild.childNodes;
19138         this.indentNode = cs[0];
19139         this.ecNode = cs[1];
19140         this.iconNode = cs[2];
19141         var index = 3;
19142         if(cb){
19143             this.checkbox = cs[3];
19144             index++;
19145         }
19146         this.anchor = cs[index];
19147         
19148         this.textNode = cs[index].firstChild;
19149         
19150         //el.on("click", this.onClick, this);
19151         //el.on("dblclick", this.onDblClick, this);
19152         
19153         
19154        // console.log(this);
19155     },
19156     initEvents : function(){
19157         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19158         
19159             
19160         var a = this.ranchor;
19161
19162         var el = Roo.get(a);
19163
19164         if(Roo.isOpera){ // opera render bug ignores the CSS
19165             el.setStyle("text-decoration", "none");
19166         }
19167
19168         el.on("click", this.onClick, this);
19169         el.on("dblclick", this.onDblClick, this);
19170         el.on("contextmenu", this.onContextMenu, this);
19171         
19172     },
19173     
19174     /*onSelectedChange : function(state){
19175         if(state){
19176             this.focus();
19177             this.addClass("x-tree-selected");
19178         }else{
19179             //this.blur();
19180             this.removeClass("x-tree-selected");
19181         }
19182     },*/
19183     addClass : function(cls){
19184         if(this.elRow){
19185             Roo.fly(this.elRow).addClass(cls);
19186         }
19187         
19188     },
19189     
19190     
19191     removeClass : function(cls){
19192         if(this.elRow){
19193             Roo.fly(this.elRow).removeClass(cls);
19194         }
19195     }
19196
19197     
19198     
19199 });//<Script type="text/javascript">
19200
19201 /*
19202  * Based on:
19203  * Ext JS Library 1.1.1
19204  * Copyright(c) 2006-2007, Ext JS, LLC.
19205  *
19206  * Originally Released Under LGPL - original licence link has changed is not relivant.
19207  *
19208  * Fork - LGPL
19209  * <script type="text/javascript">
19210  */
19211  
19212
19213 /**
19214  * @class Roo.tree.ColumnTree
19215  * @extends Roo.data.TreePanel
19216  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19217  * @cfg {int} borderWidth  compined right/left border allowance
19218  * @constructor
19219  * @param {String/HTMLElement/Element} el The container element
19220  * @param {Object} config
19221  */
19222 Roo.tree.ColumnTree =  function(el, config)
19223 {
19224    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19225    this.addEvents({
19226         /**
19227         * @event resize
19228         * Fire this event on a container when it resizes
19229         * @param {int} w Width
19230         * @param {int} h Height
19231         */
19232        "resize" : true
19233     });
19234     this.on('resize', this.onResize, this);
19235 };
19236
19237 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19238     //lines:false,
19239     
19240     
19241     borderWidth: Roo.isBorderBox ? 0 : 2, 
19242     headEls : false,
19243     
19244     render : function(){
19245         // add the header.....
19246        
19247         Roo.tree.ColumnTree.superclass.render.apply(this);
19248         
19249         this.el.addClass('x-column-tree');
19250         
19251         this.headers = this.el.createChild(
19252             {cls:'x-tree-headers'},this.innerCt.dom);
19253    
19254         var cols = this.columns, c;
19255         var totalWidth = 0;
19256         this.headEls = [];
19257         var  len = cols.length;
19258         for(var i = 0; i < len; i++){
19259              c = cols[i];
19260              totalWidth += c.width;
19261             this.headEls.push(this.headers.createChild({
19262                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19263                  cn: {
19264                      cls:'x-tree-hd-text',
19265                      html: c.header
19266                  },
19267                  style:'width:'+(c.width-this.borderWidth)+'px;'
19268              }));
19269         }
19270         this.headers.createChild({cls:'x-clear'});
19271         // prevent floats from wrapping when clipped
19272         this.headers.setWidth(totalWidth);
19273         //this.innerCt.setWidth(totalWidth);
19274         this.innerCt.setStyle({ overflow: 'auto' });
19275         this.onResize(this.width, this.height);
19276              
19277         
19278     },
19279     onResize : function(w,h)
19280     {
19281         this.height = h;
19282         this.width = w;
19283         // resize cols..
19284         this.innerCt.setWidth(this.width);
19285         this.innerCt.setHeight(this.height-20);
19286         
19287         // headers...
19288         var cols = this.columns, c;
19289         var totalWidth = 0;
19290         var expEl = false;
19291         var len = cols.length;
19292         for(var i = 0; i < len; i++){
19293             c = cols[i];
19294             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19295                 // it's the expander..
19296                 expEl  = this.headEls[i];
19297                 continue;
19298             }
19299             totalWidth += c.width;
19300             
19301         }
19302         if (expEl) {
19303             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19304         }
19305         this.headers.setWidth(w-20);
19306
19307         
19308         
19309         
19310     }
19311 });
19312 /*
19313  * Based on:
19314  * Ext JS Library 1.1.1
19315  * Copyright(c) 2006-2007, Ext JS, LLC.
19316  *
19317  * Originally Released Under LGPL - original licence link has changed is not relivant.
19318  *
19319  * Fork - LGPL
19320  * <script type="text/javascript">
19321  */
19322  
19323 /**
19324  * @class Roo.menu.Menu
19325  * @extends Roo.util.Observable
19326  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19327  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19328  * @constructor
19329  * Creates a new Menu
19330  * @param {Object} config Configuration options
19331  */
19332 Roo.menu.Menu = function(config){
19333     Roo.apply(this, config);
19334     this.id = this.id || Roo.id();
19335     this.addEvents({
19336         /**
19337          * @event beforeshow
19338          * Fires before this menu is displayed
19339          * @param {Roo.menu.Menu} this
19340          */
19341         beforeshow : true,
19342         /**
19343          * @event beforehide
19344          * Fires before this menu is hidden
19345          * @param {Roo.menu.Menu} this
19346          */
19347         beforehide : true,
19348         /**
19349          * @event show
19350          * Fires after this menu is displayed
19351          * @param {Roo.menu.Menu} this
19352          */
19353         show : true,
19354         /**
19355          * @event hide
19356          * Fires after this menu is hidden
19357          * @param {Roo.menu.Menu} this
19358          */
19359         hide : true,
19360         /**
19361          * @event click
19362          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19363          * @param {Roo.menu.Menu} this
19364          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19365          * @param {Roo.EventObject} e
19366          */
19367         click : true,
19368         /**
19369          * @event mouseover
19370          * Fires when the mouse is hovering over this menu
19371          * @param {Roo.menu.Menu} this
19372          * @param {Roo.EventObject} e
19373          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19374          */
19375         mouseover : true,
19376         /**
19377          * @event mouseout
19378          * Fires when the mouse exits this menu
19379          * @param {Roo.menu.Menu} this
19380          * @param {Roo.EventObject} e
19381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19382          */
19383         mouseout : true,
19384         /**
19385          * @event itemclick
19386          * Fires when a menu item contained in this menu is clicked
19387          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19388          * @param {Roo.EventObject} e
19389          */
19390         itemclick: true
19391     });
19392     if (this.registerMenu) {
19393         Roo.menu.MenuMgr.register(this);
19394     }
19395     
19396     var mis = this.items;
19397     this.items = new Roo.util.MixedCollection();
19398     if(mis){
19399         this.add.apply(this, mis);
19400     }
19401 };
19402
19403 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19404     /**
19405      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19406      */
19407     minWidth : 120,
19408     /**
19409      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19410      * for bottom-right shadow (defaults to "sides")
19411      */
19412     shadow : "sides",
19413     /**
19414      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19415      * this menu (defaults to "tl-tr?")
19416      */
19417     subMenuAlign : "tl-tr?",
19418     /**
19419      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19420      * relative to its element of origin (defaults to "tl-bl?")
19421      */
19422     defaultAlign : "tl-bl?",
19423     /**
19424      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19425      */
19426     allowOtherMenus : false,
19427     /**
19428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19429      */
19430     registerMenu : true,
19431
19432     hidden:true,
19433
19434     // private
19435     render : function(){
19436         if(this.el){
19437             return;
19438         }
19439         var el = this.el = new Roo.Layer({
19440             cls: "x-menu",
19441             shadow:this.shadow,
19442             constrain: false,
19443             parentEl: this.parentEl || document.body,
19444             zindex:15000
19445         });
19446
19447         this.keyNav = new Roo.menu.MenuNav(this);
19448
19449         if(this.plain){
19450             el.addClass("x-menu-plain");
19451         }
19452         if(this.cls){
19453             el.addClass(this.cls);
19454         }
19455         // generic focus element
19456         this.focusEl = el.createChild({
19457             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19458         });
19459         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19460         ul.on("click", this.onClick, this);
19461         ul.on("mouseover", this.onMouseOver, this);
19462         ul.on("mouseout", this.onMouseOut, this);
19463         this.items.each(function(item){
19464             var li = document.createElement("li");
19465             li.className = "x-menu-list-item";
19466             ul.dom.appendChild(li);
19467             item.render(li, this);
19468         }, this);
19469         this.ul = ul;
19470         this.autoWidth();
19471     },
19472
19473     // private
19474     autoWidth : function(){
19475         var el = this.el, ul = this.ul;
19476         if(!el){
19477             return;
19478         }
19479         var w = this.width;
19480         if(w){
19481             el.setWidth(w);
19482         }else if(Roo.isIE){
19483             el.setWidth(this.minWidth);
19484             var t = el.dom.offsetWidth; // force recalc
19485             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19486         }
19487     },
19488
19489     // private
19490     delayAutoWidth : function(){
19491         if(this.rendered){
19492             if(!this.awTask){
19493                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19494             }
19495             this.awTask.delay(20);
19496         }
19497     },
19498
19499     // private
19500     findTargetItem : function(e){
19501         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19502         if(t && t.menuItemId){
19503             return this.items.get(t.menuItemId);
19504         }
19505     },
19506
19507     // private
19508     onClick : function(e){
19509         var t;
19510         if(t = this.findTargetItem(e)){
19511             t.onClick(e);
19512             this.fireEvent("click", this, t, e);
19513         }
19514     },
19515
19516     // private
19517     setActiveItem : function(item, autoExpand){
19518         if(item != this.activeItem){
19519             if(this.activeItem){
19520                 this.activeItem.deactivate();
19521             }
19522             this.activeItem = item;
19523             item.activate(autoExpand);
19524         }else if(autoExpand){
19525             item.expandMenu();
19526         }
19527     },
19528
19529     // private
19530     tryActivate : function(start, step){
19531         var items = this.items;
19532         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19533             var item = items.get(i);
19534             if(!item.disabled && item.canActivate){
19535                 this.setActiveItem(item, false);
19536                 return item;
19537             }
19538         }
19539         return false;
19540     },
19541
19542     // private
19543     onMouseOver : function(e){
19544         var t;
19545         if(t = this.findTargetItem(e)){
19546             if(t.canActivate && !t.disabled){
19547                 this.setActiveItem(t, true);
19548             }
19549         }
19550         this.fireEvent("mouseover", this, e, t);
19551     },
19552
19553     // private
19554     onMouseOut : function(e){
19555         var t;
19556         if(t = this.findTargetItem(e)){
19557             if(t == this.activeItem && t.shouldDeactivate(e)){
19558                 this.activeItem.deactivate();
19559                 delete this.activeItem;
19560             }
19561         }
19562         this.fireEvent("mouseout", this, e, t);
19563     },
19564
19565     /**
19566      * Read-only.  Returns true if the menu is currently displayed, else false.
19567      * @type Boolean
19568      */
19569     isVisible : function(){
19570         return this.el && !this.hidden;
19571     },
19572
19573     /**
19574      * Displays this menu relative to another element
19575      * @param {String/HTMLElement/Roo.Element} element The element to align to
19576      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19577      * the element (defaults to this.defaultAlign)
19578      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19579      */
19580     show : function(el, pos, parentMenu){
19581         this.parentMenu = parentMenu;
19582         if(!this.el){
19583             this.render();
19584         }
19585         this.fireEvent("beforeshow", this);
19586         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19587     },
19588
19589     /**
19590      * Displays this menu at a specific xy position
19591      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19592      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19593      */
19594     showAt : function(xy, parentMenu, /* private: */_e){
19595         this.parentMenu = parentMenu;
19596         if(!this.el){
19597             this.render();
19598         }
19599         if(_e !== false){
19600             this.fireEvent("beforeshow", this);
19601             xy = this.el.adjustForConstraints(xy);
19602         }
19603         this.el.setXY(xy);
19604         this.el.show();
19605         this.hidden = false;
19606         this.focus();
19607         this.fireEvent("show", this);
19608     },
19609
19610     focus : function(){
19611         if(!this.hidden){
19612             this.doFocus.defer(50, this);
19613         }
19614     },
19615
19616     doFocus : function(){
19617         if(!this.hidden){
19618             this.focusEl.focus();
19619         }
19620     },
19621
19622     /**
19623      * Hides this menu and optionally all parent menus
19624      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19625      */
19626     hide : function(deep){
19627         if(this.el && this.isVisible()){
19628             this.fireEvent("beforehide", this);
19629             if(this.activeItem){
19630                 this.activeItem.deactivate();
19631                 this.activeItem = null;
19632             }
19633             this.el.hide();
19634             this.hidden = true;
19635             this.fireEvent("hide", this);
19636         }
19637         if(deep === true && this.parentMenu){
19638             this.parentMenu.hide(true);
19639         }
19640     },
19641
19642     /**
19643      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19644      * Any of the following are valid:
19645      * <ul>
19646      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19647      * <li>An HTMLElement object which will be converted to a menu item</li>
19648      * <li>A menu item config object that will be created as a new menu item</li>
19649      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19650      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19651      * </ul>
19652      * Usage:
19653      * <pre><code>
19654 // Create the menu
19655 var menu = new Roo.menu.Menu();
19656
19657 // Create a menu item to add by reference
19658 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19659
19660 // Add a bunch of items at once using different methods.
19661 // Only the last item added will be returned.
19662 var item = menu.add(
19663     menuItem,                // add existing item by ref
19664     'Dynamic Item',          // new TextItem
19665     '-',                     // new separator
19666     { text: 'Config Item' }  // new item by config
19667 );
19668 </code></pre>
19669      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19670      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19671      */
19672     add : function(){
19673         var a = arguments, l = a.length, item;
19674         for(var i = 0; i < l; i++){
19675             var el = a[i];
19676             if ((typeof(el) == "object") && el.xtype && el.xns) {
19677                 el = Roo.factory(el, Roo.menu);
19678             }
19679             
19680             if(el.render){ // some kind of Item
19681                 item = this.addItem(el);
19682             }else if(typeof el == "string"){ // string
19683                 if(el == "separator" || el == "-"){
19684                     item = this.addSeparator();
19685                 }else{
19686                     item = this.addText(el);
19687                 }
19688             }else if(el.tagName || el.el){ // element
19689                 item = this.addElement(el);
19690             }else if(typeof el == "object"){ // must be menu item config?
19691                 item = this.addMenuItem(el);
19692             }
19693         }
19694         return item;
19695     },
19696
19697     /**
19698      * Returns this menu's underlying {@link Roo.Element} object
19699      * @return {Roo.Element} The element
19700      */
19701     getEl : function(){
19702         if(!this.el){
19703             this.render();
19704         }
19705         return this.el;
19706     },
19707
19708     /**
19709      * Adds a separator bar to the menu
19710      * @return {Roo.menu.Item} The menu item that was added
19711      */
19712     addSeparator : function(){
19713         return this.addItem(new Roo.menu.Separator());
19714     },
19715
19716     /**
19717      * Adds an {@link Roo.Element} object to the menu
19718      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19719      * @return {Roo.menu.Item} The menu item that was added
19720      */
19721     addElement : function(el){
19722         return this.addItem(new Roo.menu.BaseItem(el));
19723     },
19724
19725     /**
19726      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19727      * @param {Roo.menu.Item} item The menu item to add
19728      * @return {Roo.menu.Item} The menu item that was added
19729      */
19730     addItem : function(item){
19731         this.items.add(item);
19732         if(this.ul){
19733             var li = document.createElement("li");
19734             li.className = "x-menu-list-item";
19735             this.ul.dom.appendChild(li);
19736             item.render(li, this);
19737             this.delayAutoWidth();
19738         }
19739         return item;
19740     },
19741
19742     /**
19743      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19744      * @param {Object} config A MenuItem config object
19745      * @return {Roo.menu.Item} The menu item that was added
19746      */
19747     addMenuItem : function(config){
19748         if(!(config instanceof Roo.menu.Item)){
19749             if(typeof config.checked == "boolean"){ // must be check menu item config?
19750                 config = new Roo.menu.CheckItem(config);
19751             }else{
19752                 config = new Roo.menu.Item(config);
19753             }
19754         }
19755         return this.addItem(config);
19756     },
19757
19758     /**
19759      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19760      * @param {String} text The text to display in the menu item
19761      * @return {Roo.menu.Item} The menu item that was added
19762      */
19763     addText : function(text){
19764         return this.addItem(new Roo.menu.TextItem({ text : text }));
19765     },
19766
19767     /**
19768      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19769      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19770      * @param {Roo.menu.Item} item The menu item to add
19771      * @return {Roo.menu.Item} The menu item that was added
19772      */
19773     insert : function(index, item){
19774         this.items.insert(index, item);
19775         if(this.ul){
19776             var li = document.createElement("li");
19777             li.className = "x-menu-list-item";
19778             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19779             item.render(li, this);
19780             this.delayAutoWidth();
19781         }
19782         return item;
19783     },
19784
19785     /**
19786      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19787      * @param {Roo.menu.Item} item The menu item to remove
19788      */
19789     remove : function(item){
19790         this.items.removeKey(item.id);
19791         item.destroy();
19792     },
19793
19794     /**
19795      * Removes and destroys all items in the menu
19796      */
19797     removeAll : function(){
19798         var f;
19799         while(f = this.items.first()){
19800             this.remove(f);
19801         }
19802     }
19803 });
19804
19805 // MenuNav is a private utility class used internally by the Menu
19806 Roo.menu.MenuNav = function(menu){
19807     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19808     this.scope = this.menu = menu;
19809 };
19810
19811 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19812     doRelay : function(e, h){
19813         var k = e.getKey();
19814         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19815             this.menu.tryActivate(0, 1);
19816             return false;
19817         }
19818         return h.call(this.scope || this, e, this.menu);
19819     },
19820
19821     up : function(e, m){
19822         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19823             m.tryActivate(m.items.length-1, -1);
19824         }
19825     },
19826
19827     down : function(e, m){
19828         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19829             m.tryActivate(0, 1);
19830         }
19831     },
19832
19833     right : function(e, m){
19834         if(m.activeItem){
19835             m.activeItem.expandMenu(true);
19836         }
19837     },
19838
19839     left : function(e, m){
19840         m.hide();
19841         if(m.parentMenu && m.parentMenu.activeItem){
19842             m.parentMenu.activeItem.activate();
19843         }
19844     },
19845
19846     enter : function(e, m){
19847         if(m.activeItem){
19848             e.stopPropagation();
19849             m.activeItem.onClick(e);
19850             m.fireEvent("click", this, m.activeItem);
19851             return true;
19852         }
19853     }
19854 });/*
19855  * Based on:
19856  * Ext JS Library 1.1.1
19857  * Copyright(c) 2006-2007, Ext JS, LLC.
19858  *
19859  * Originally Released Under LGPL - original licence link has changed is not relivant.
19860  *
19861  * Fork - LGPL
19862  * <script type="text/javascript">
19863  */
19864  
19865 /**
19866  * @class Roo.menu.MenuMgr
19867  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19868  * @singleton
19869  */
19870 Roo.menu.MenuMgr = function(){
19871    var menus, active, groups = {}, attached = false, lastShow = new Date();
19872
19873    // private - called when first menu is created
19874    function init(){
19875        menus = {};
19876        active = new Roo.util.MixedCollection();
19877        Roo.get(document).addKeyListener(27, function(){
19878            if(active.length > 0){
19879                hideAll();
19880            }
19881        });
19882    }
19883
19884    // private
19885    function hideAll(){
19886        if(active && active.length > 0){
19887            var c = active.clone();
19888            c.each(function(m){
19889                m.hide();
19890            });
19891        }
19892    }
19893
19894    // private
19895    function onHide(m){
19896        active.remove(m);
19897        if(active.length < 1){
19898            Roo.get(document).un("mousedown", onMouseDown);
19899            attached = false;
19900        }
19901    }
19902
19903    // private
19904    function onShow(m){
19905        var last = active.last();
19906        lastShow = new Date();
19907        active.add(m);
19908        if(!attached){
19909            Roo.get(document).on("mousedown", onMouseDown);
19910            attached = true;
19911        }
19912        if(m.parentMenu){
19913           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19914           m.parentMenu.activeChild = m;
19915        }else if(last && last.isVisible()){
19916           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19917        }
19918    }
19919
19920    // private
19921    function onBeforeHide(m){
19922        if(m.activeChild){
19923            m.activeChild.hide();
19924        }
19925        if(m.autoHideTimer){
19926            clearTimeout(m.autoHideTimer);
19927            delete m.autoHideTimer;
19928        }
19929    }
19930
19931    // private
19932    function onBeforeShow(m){
19933        var pm = m.parentMenu;
19934        if(!pm && !m.allowOtherMenus){
19935            hideAll();
19936        }else if(pm && pm.activeChild && active != m){
19937            pm.activeChild.hide();
19938        }
19939    }
19940
19941    // private
19942    function onMouseDown(e){
19943        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19944            hideAll();
19945        }
19946    }
19947
19948    // private
19949    function onBeforeCheck(mi, state){
19950        if(state){
19951            var g = groups[mi.group];
19952            for(var i = 0, l = g.length; i < l; i++){
19953                if(g[i] != mi){
19954                    g[i].setChecked(false);
19955                }
19956            }
19957        }
19958    }
19959
19960    return {
19961
19962        /**
19963         * Hides all menus that are currently visible
19964         */
19965        hideAll : function(){
19966             hideAll();  
19967        },
19968
19969        // private
19970        register : function(menu){
19971            if(!menus){
19972                init();
19973            }
19974            menus[menu.id] = menu;
19975            menu.on("beforehide", onBeforeHide);
19976            menu.on("hide", onHide);
19977            menu.on("beforeshow", onBeforeShow);
19978            menu.on("show", onShow);
19979            var g = menu.group;
19980            if(g && menu.events["checkchange"]){
19981                if(!groups[g]){
19982                    groups[g] = [];
19983                }
19984                groups[g].push(menu);
19985                menu.on("checkchange", onCheck);
19986            }
19987        },
19988
19989         /**
19990          * Returns a {@link Roo.menu.Menu} object
19991          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19992          * be used to generate and return a new Menu instance.
19993          */
19994        get : function(menu){
19995            if(typeof menu == "string"){ // menu id
19996                return menus[menu];
19997            }else if(menu.events){  // menu instance
19998                return menu;
19999            }else if(typeof menu.length == 'number'){ // array of menu items?
20000                return new Roo.menu.Menu({items:menu});
20001            }else{ // otherwise, must be a config
20002                return new Roo.menu.Menu(menu);
20003            }
20004        },
20005
20006        // private
20007        unregister : function(menu){
20008            delete menus[menu.id];
20009            menu.un("beforehide", onBeforeHide);
20010            menu.un("hide", onHide);
20011            menu.un("beforeshow", onBeforeShow);
20012            menu.un("show", onShow);
20013            var g = menu.group;
20014            if(g && menu.events["checkchange"]){
20015                groups[g].remove(menu);
20016                menu.un("checkchange", onCheck);
20017            }
20018        },
20019
20020        // private
20021        registerCheckable : function(menuItem){
20022            var g = menuItem.group;
20023            if(g){
20024                if(!groups[g]){
20025                    groups[g] = [];
20026                }
20027                groups[g].push(menuItem);
20028                menuItem.on("beforecheckchange", onBeforeCheck);
20029            }
20030        },
20031
20032        // private
20033        unregisterCheckable : function(menuItem){
20034            var g = menuItem.group;
20035            if(g){
20036                groups[g].remove(menuItem);
20037                menuItem.un("beforecheckchange", onBeforeCheck);
20038            }
20039        }
20040    };
20041 }();/*
20042  * Based on:
20043  * Ext JS Library 1.1.1
20044  * Copyright(c) 2006-2007, Ext JS, LLC.
20045  *
20046  * Originally Released Under LGPL - original licence link has changed is not relivant.
20047  *
20048  * Fork - LGPL
20049  * <script type="text/javascript">
20050  */
20051  
20052
20053 /**
20054  * @class Roo.menu.BaseItem
20055  * @extends Roo.Component
20056  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20057  * management and base configuration options shared by all menu components.
20058  * @constructor
20059  * Creates a new BaseItem
20060  * @param {Object} config Configuration options
20061  */
20062 Roo.menu.BaseItem = function(config){
20063     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20064
20065     this.addEvents({
20066         /**
20067          * @event click
20068          * Fires when this item is clicked
20069          * @param {Roo.menu.BaseItem} this
20070          * @param {Roo.EventObject} e
20071          */
20072         click: true,
20073         /**
20074          * @event activate
20075          * Fires when this item is activated
20076          * @param {Roo.menu.BaseItem} this
20077          */
20078         activate : true,
20079         /**
20080          * @event deactivate
20081          * Fires when this item is deactivated
20082          * @param {Roo.menu.BaseItem} this
20083          */
20084         deactivate : true
20085     });
20086
20087     if(this.handler){
20088         this.on("click", this.handler, this.scope, true);
20089     }
20090 };
20091
20092 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20093     /**
20094      * @cfg {Function} handler
20095      * A function that will handle the click event of this menu item (defaults to undefined)
20096      */
20097     /**
20098      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20099      */
20100     canActivate : false,
20101     /**
20102      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20103      */
20104     activeClass : "x-menu-item-active",
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20107      */
20108     hideOnClick : true,
20109     /**
20110      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20111      */
20112     hideDelay : 100,
20113
20114     // private
20115     ctype: "Roo.menu.BaseItem",
20116
20117     // private
20118     actionMode : "container",
20119
20120     // private
20121     render : function(container, parentMenu){
20122         this.parentMenu = parentMenu;
20123         Roo.menu.BaseItem.superclass.render.call(this, container);
20124         this.container.menuItemId = this.id;
20125     },
20126
20127     // private
20128     onRender : function(container, position){
20129         this.el = Roo.get(this.el);
20130         container.dom.appendChild(this.el.dom);
20131     },
20132
20133     // private
20134     onClick : function(e){
20135         if(!this.disabled && this.fireEvent("click", this, e) !== false
20136                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20137             this.handleClick(e);
20138         }else{
20139             e.stopEvent();
20140         }
20141     },
20142
20143     // private
20144     activate : function(){
20145         if(this.disabled){
20146             return false;
20147         }
20148         var li = this.container;
20149         li.addClass(this.activeClass);
20150         this.region = li.getRegion().adjust(2, 2, -2, -2);
20151         this.fireEvent("activate", this);
20152         return true;
20153     },
20154
20155     // private
20156     deactivate : function(){
20157         this.container.removeClass(this.activeClass);
20158         this.fireEvent("deactivate", this);
20159     },
20160
20161     // private
20162     shouldDeactivate : function(e){
20163         return !this.region || !this.region.contains(e.getPoint());
20164     },
20165
20166     // private
20167     handleClick : function(e){
20168         if(this.hideOnClick){
20169             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20170         }
20171     },
20172
20173     // private
20174     expandMenu : function(autoActivate){
20175         // do nothing
20176     },
20177
20178     // private
20179     hideMenu : function(){
20180         // do nothing
20181     }
20182 });/*
20183  * Based on:
20184  * Ext JS Library 1.1.1
20185  * Copyright(c) 2006-2007, Ext JS, LLC.
20186  *
20187  * Originally Released Under LGPL - original licence link has changed is not relivant.
20188  *
20189  * Fork - LGPL
20190  * <script type="text/javascript">
20191  */
20192  
20193 /**
20194  * @class Roo.menu.Adapter
20195  * @extends Roo.menu.BaseItem
20196  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20197  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20198  * @constructor
20199  * Creates a new Adapter
20200  * @param {Object} config Configuration options
20201  */
20202 Roo.menu.Adapter = function(component, config){
20203     Roo.menu.Adapter.superclass.constructor.call(this, config);
20204     this.component = component;
20205 };
20206 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20207     // private
20208     canActivate : true,
20209
20210     // private
20211     onRender : function(container, position){
20212         this.component.render(container);
20213         this.el = this.component.getEl();
20214     },
20215
20216     // private
20217     activate : function(){
20218         if(this.disabled){
20219             return false;
20220         }
20221         this.component.focus();
20222         this.fireEvent("activate", this);
20223         return true;
20224     },
20225
20226     // private
20227     deactivate : function(){
20228         this.fireEvent("deactivate", this);
20229     },
20230
20231     // private
20232     disable : function(){
20233         this.component.disable();
20234         Roo.menu.Adapter.superclass.disable.call(this);
20235     },
20236
20237     // private
20238     enable : function(){
20239         this.component.enable();
20240         Roo.menu.Adapter.superclass.enable.call(this);
20241     }
20242 });/*
20243  * Based on:
20244  * Ext JS Library 1.1.1
20245  * Copyright(c) 2006-2007, Ext JS, LLC.
20246  *
20247  * Originally Released Under LGPL - original licence link has changed is not relivant.
20248  *
20249  * Fork - LGPL
20250  * <script type="text/javascript">
20251  */
20252
20253 /**
20254  * @class Roo.menu.TextItem
20255  * @extends Roo.menu.BaseItem
20256  * Adds a static text string to a menu, usually used as either a heading or group separator.
20257  * Note: old style constructor with text is still supported.
20258  * 
20259  * @constructor
20260  * Creates a new TextItem
20261  * @param {Object} cfg Configuration
20262  */
20263 Roo.menu.TextItem = function(cfg){
20264     if (typeof(cfg) == 'string') {
20265         this.text = cfg;
20266     } else {
20267         Roo.apply(this,cfg);
20268     }
20269     
20270     Roo.menu.TextItem.superclass.constructor.call(this);
20271 };
20272
20273 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20274     /**
20275      * @cfg {Boolean} text Text to show on item.
20276      */
20277     text : '',
20278     
20279     /**
20280      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20281      */
20282     hideOnClick : false,
20283     /**
20284      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20285      */
20286     itemCls : "x-menu-text",
20287
20288     // private
20289     onRender : function(){
20290         var s = document.createElement("span");
20291         s.className = this.itemCls;
20292         s.innerHTML = this.text;
20293         this.el = s;
20294         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20295     }
20296 });/*
20297  * Based on:
20298  * Ext JS Library 1.1.1
20299  * Copyright(c) 2006-2007, Ext JS, LLC.
20300  *
20301  * Originally Released Under LGPL - original licence link has changed is not relivant.
20302  *
20303  * Fork - LGPL
20304  * <script type="text/javascript">
20305  */
20306
20307 /**
20308  * @class Roo.menu.Separator
20309  * @extends Roo.menu.BaseItem
20310  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20311  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20312  * @constructor
20313  * @param {Object} config Configuration options
20314  */
20315 Roo.menu.Separator = function(config){
20316     Roo.menu.Separator.superclass.constructor.call(this, config);
20317 };
20318
20319 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20320     /**
20321      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20322      */
20323     itemCls : "x-menu-sep",
20324     /**
20325      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20326      */
20327     hideOnClick : false,
20328
20329     // private
20330     onRender : function(li){
20331         var s = document.createElement("span");
20332         s.className = this.itemCls;
20333         s.innerHTML = "&#160;";
20334         this.el = s;
20335         li.addClass("x-menu-sep-li");
20336         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20337     }
20338 });/*
20339  * Based on:
20340  * Ext JS Library 1.1.1
20341  * Copyright(c) 2006-2007, Ext JS, LLC.
20342  *
20343  * Originally Released Under LGPL - original licence link has changed is not relivant.
20344  *
20345  * Fork - LGPL
20346  * <script type="text/javascript">
20347  */
20348 /**
20349  * @class Roo.menu.Item
20350  * @extends Roo.menu.BaseItem
20351  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20352  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20353  * activation and click handling.
20354  * @constructor
20355  * Creates a new Item
20356  * @param {Object} config Configuration options
20357  */
20358 Roo.menu.Item = function(config){
20359     Roo.menu.Item.superclass.constructor.call(this, config);
20360     if(this.menu){
20361         this.menu = Roo.menu.MenuMgr.get(this.menu);
20362     }
20363 };
20364 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20365     
20366     /**
20367      * @cfg {String} text
20368      * The text to show on the menu item.
20369      */
20370     text: '',
20371      /**
20372      * @cfg {String} HTML to render in menu
20373      * The text to show on the menu item (HTML version).
20374      */
20375     html: '',
20376     /**
20377      * @cfg {String} icon
20378      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20379      */
20380     icon: undefined,
20381     /**
20382      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20383      */
20384     itemCls : "x-menu-item",
20385     /**
20386      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20387      */
20388     canActivate : true,
20389     /**
20390      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20391      */
20392     showDelay: 200,
20393     // doc'd in BaseItem
20394     hideDelay: 200,
20395
20396     // private
20397     ctype: "Roo.menu.Item",
20398     
20399     // private
20400     onRender : function(container, position){
20401         var el = document.createElement("a");
20402         el.hideFocus = true;
20403         el.unselectable = "on";
20404         el.href = this.href || "#";
20405         if(this.hrefTarget){
20406             el.target = this.hrefTarget;
20407         }
20408         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20409         
20410         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20411         
20412         el.innerHTML = String.format(
20413                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20414                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20415         this.el = el;
20416         Roo.menu.Item.superclass.onRender.call(this, container, position);
20417     },
20418
20419     /**
20420      * Sets the text to display in this menu item
20421      * @param {String} text The text to display
20422      * @param {Boolean} isHTML true to indicate text is pure html.
20423      */
20424     setText : function(text, isHTML){
20425         if (isHTML) {
20426             this.html = text;
20427         } else {
20428             this.text = text;
20429             this.html = '';
20430         }
20431         if(this.rendered){
20432             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20433      
20434             this.el.update(String.format(
20435                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20436                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20437             this.parentMenu.autoWidth();
20438         }
20439     },
20440
20441     // private
20442     handleClick : function(e){
20443         if(!this.href){ // if no link defined, stop the event automatically
20444             e.stopEvent();
20445         }
20446         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20447     },
20448
20449     // private
20450     activate : function(autoExpand){
20451         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20452             this.focus();
20453             if(autoExpand){
20454                 this.expandMenu();
20455             }
20456         }
20457         return true;
20458     },
20459
20460     // private
20461     shouldDeactivate : function(e){
20462         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20463             if(this.menu && this.menu.isVisible()){
20464                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20465             }
20466             return true;
20467         }
20468         return false;
20469     },
20470
20471     // private
20472     deactivate : function(){
20473         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20474         this.hideMenu();
20475     },
20476
20477     // private
20478     expandMenu : function(autoActivate){
20479         if(!this.disabled && this.menu){
20480             clearTimeout(this.hideTimer);
20481             delete this.hideTimer;
20482             if(!this.menu.isVisible() && !this.showTimer){
20483                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20484             }else if (this.menu.isVisible() && autoActivate){
20485                 this.menu.tryActivate(0, 1);
20486             }
20487         }
20488     },
20489
20490     // private
20491     deferExpand : function(autoActivate){
20492         delete this.showTimer;
20493         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20494         if(autoActivate){
20495             this.menu.tryActivate(0, 1);
20496         }
20497     },
20498
20499     // private
20500     hideMenu : function(){
20501         clearTimeout(this.showTimer);
20502         delete this.showTimer;
20503         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20504             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20505         }
20506     },
20507
20508     // private
20509     deferHide : function(){
20510         delete this.hideTimer;
20511         this.menu.hide();
20512     }
20513 });