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|String} valid true/false or string (add/sub/ok/nodrop)
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 typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4024             this.valid ? this.dropAllowed : this.dropNotAllowed
4025         );
4026     },
4027
4028     /**
4029      * @hide
4030      */
4031     notifyOver : function(dd, e, data){
4032         this.valid = true;
4033         this.fireEvent('over', this, dd, e, data);
4034         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4035             this.valid ? this.dropAllowed : this.dropNotAllowed
4036         );
4037     },
4038
4039     /**
4040      * @hide
4041      */
4042     notifyOut : function(dd, e, data){
4043         this.fireEvent('out', this, dd, e, data);
4044         if(this.overClass){
4045             this.el.removeClass(this.overClass);
4046         }
4047     },
4048
4049     /**
4050      * @hide
4051      */
4052     notifyDrop : function(dd, e, data){
4053         this.success = false;
4054         this.fireEvent('drop', this, dd, e, data);
4055         return this.success;
4056     }
4057 });/*
4058  * Based on:
4059  * Ext JS Library 1.1.1
4060  * Copyright(c) 2006-2007, Ext JS, LLC.
4061  *
4062  * Originally Released Under LGPL - original licence link has changed is not relivant.
4063  *
4064  * Fork - LGPL
4065  * <script type="text/javascript">
4066  */
4067
4068
4069 /**
4070  * @class Roo.dd.DragZone
4071  * @extends Roo.dd.DragSource
4072  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4073  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4074  * @constructor
4075  * @param {String/HTMLElement/Element} el The container element
4076  * @param {Object} config
4077  */
4078 Roo.dd.DragZone = function(el, config){
4079     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4080     if(this.containerScroll){
4081         Roo.dd.ScrollManager.register(this.el);
4082     }
4083 };
4084
4085 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4086     /**
4087      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4088      * for auto scrolling during drag operations.
4089      */
4090     /**
4091      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4092      * method after a failed drop (defaults to "c3daf9" - light blue)
4093      */
4094
4095     /**
4096      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4097      * for a valid target to drag based on the mouse down. Override this method
4098      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4099      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4100      * @param {EventObject} e The mouse down event
4101      * @return {Object} The dragData
4102      */
4103     getDragData : function(e){
4104         return Roo.dd.Registry.getHandleFromEvent(e);
4105     },
4106     
4107     /**
4108      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4109      * this.dragData.ddel
4110      * @param {Number} x The x position of the click on the dragged object
4111      * @param {Number} y The y position of the click on the dragged object
4112      * @return {Boolean} true to continue the drag, false to cancel
4113      */
4114     onInitDrag : function(x, y){
4115         this.proxy.update(this.dragData.ddel.cloneNode(true));
4116         this.onStartDrag(x, y);
4117         return true;
4118     },
4119     
4120     /**
4121      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4122      */
4123     afterRepair : function(){
4124         if(Roo.enableFx){
4125             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4126         }
4127         this.dragging = false;
4128     },
4129
4130     /**
4131      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4132      * the XY of this.dragData.ddel
4133      * @param {EventObject} e The mouse up event
4134      * @return {Array} The xy location (e.g. [100, 200])
4135      */
4136     getRepairXY : function(e){
4137         return Roo.Element.fly(this.dragData.ddel).getXY();  
4138     }
4139 });/*
4140  * Based on:
4141  * Ext JS Library 1.1.1
4142  * Copyright(c) 2006-2007, Ext JS, LLC.
4143  *
4144  * Originally Released Under LGPL - original licence link has changed is not relivant.
4145  *
4146  * Fork - LGPL
4147  * <script type="text/javascript">
4148  */
4149 /**
4150  * @class Roo.dd.DropZone
4151  * @extends Roo.dd.DropTarget
4152  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4153  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4154  * @constructor
4155  * @param {String/HTMLElement/Element} el The container element
4156  * @param {Object} config
4157  */
4158 Roo.dd.DropZone = function(el, config){
4159     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4160 };
4161
4162 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4163     /**
4164      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4165      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4166      * provide your own custom lookup.
4167      * @param {Event} e The event
4168      * @return {Object} data The custom data
4169      */
4170     getTargetFromEvent : function(e){
4171         return Roo.dd.Registry.getTargetFromEvent(e);
4172     },
4173
4174     /**
4175      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4176      * that it has registered.  This method has no default implementation and should be overridden to provide
4177      * node-specific processing if necessary.
4178      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4179      * {@link #getTargetFromEvent} for this node)
4180      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4181      * @param {Event} e The event
4182      * @param {Object} data An object containing arbitrary data supplied by the drag source
4183      */
4184     onNodeEnter : function(n, dd, e, data){
4185         
4186     },
4187
4188     /**
4189      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4190      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4191      * overridden to provide the proper feedback.
4192      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4193      * {@link #getTargetFromEvent} for this node)
4194      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4195      * @param {Event} e The event
4196      * @param {Object} data An object containing arbitrary data supplied by the drag source
4197      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4198      * underlying {@link Roo.dd.StatusProxy} can be updated
4199      */
4200     onNodeOver : function(n, dd, e, data){
4201         return this.dropAllowed;
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4206      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeOut : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4220      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4221      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {Boolean} True if the drop was valid, else false
4228      */
4229     onNodeDrop : function(n, dd, e, data){
4230         return false;
4231     },
4232
4233     /**
4234      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4235      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4236      * it should be overridden to provide the proper feedback if necessary.
4237      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4238      * @param {Event} e The event
4239      * @param {Object} data An object containing arbitrary data supplied by the drag source
4240      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4241      * underlying {@link Roo.dd.StatusProxy} can be updated
4242      */
4243     onContainerOver : function(dd, e, data){
4244         return this.dropNotAllowed;
4245     },
4246
4247     /**
4248      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4249      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4250      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4251      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4253      * @param {Event} e The event
4254      * @param {Object} data An object containing arbitrary data supplied by the drag source
4255      * @return {Boolean} True if the drop was valid, else false
4256      */
4257     onContainerDrop : function(dd, e, data){
4258         return false;
4259     },
4260
4261     /**
4262      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4263      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4264      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4265      * you should override this method and provide a custom implementation.
4266      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4267      * @param {Event} e The event
4268      * @param {Object} data An object containing arbitrary data supplied by the drag source
4269      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4270      * underlying {@link Roo.dd.StatusProxy} can be updated
4271      */
4272     notifyEnter : function(dd, e, data){
4273         return this.dropNotAllowed;
4274     },
4275
4276     /**
4277      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4278      * This method will be called on every mouse movement while the drag source is over the drop zone.
4279      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4280      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4281      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4282      * registered node, it will call {@link #onContainerOver}.
4283      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4284      * @param {Event} e The event
4285      * @param {Object} data An object containing arbitrary data supplied by the drag source
4286      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4287      * underlying {@link Roo.dd.StatusProxy} can be updated
4288      */
4289     notifyOver : function(dd, e, data){
4290         var n = this.getTargetFromEvent(e);
4291         if(!n){ // not over valid drop target
4292             if(this.lastOverNode){
4293                 this.onNodeOut(this.lastOverNode, dd, e, data);
4294                 this.lastOverNode = null;
4295             }
4296             return this.onContainerOver(dd, e, data);
4297         }
4298         if(this.lastOverNode != n){
4299             if(this.lastOverNode){
4300                 this.onNodeOut(this.lastOverNode, dd, e, data);
4301             }
4302             this.onNodeEnter(n, dd, e, data);
4303             this.lastOverNode = n;
4304         }
4305         return this.onNodeOver(n, dd, e, data);
4306     },
4307
4308     /**
4309      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4310      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4311      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4312      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4313      * @param {Event} e The event
4314      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4315      */
4316     notifyOut : function(dd, e, data){
4317         if(this.lastOverNode){
4318             this.onNodeOut(this.lastOverNode, dd, e, data);
4319             this.lastOverNode = null;
4320         }
4321     },
4322
4323     /**
4324      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4325      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4326      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4327      * otherwise it will call {@link #onContainerDrop}.
4328      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4329      * @param {Event} e The event
4330      * @param {Object} data An object containing arbitrary data supplied by the drag source
4331      * @return {Boolean} True if the drop was valid, else false
4332      */
4333     notifyDrop : function(dd, e, data){
4334         if(this.lastOverNode){
4335             this.onNodeOut(this.lastOverNode, dd, e, data);
4336             this.lastOverNode = null;
4337         }
4338         var n = this.getTargetFromEvent(e);
4339         return n ?
4340             this.onNodeDrop(n, dd, e, data) :
4341             this.onContainerDrop(dd, e, data);
4342     },
4343
4344     // private
4345     triggerCacheRefresh : function(){
4346         Roo.dd.DDM.refreshCache(this.groups);
4347     }  
4348 });/*
4349  * Based on:
4350  * Ext JS Library 1.1.1
4351  * Copyright(c) 2006-2007, Ext JS, LLC.
4352  *
4353  * Originally Released Under LGPL - original licence link has changed is not relivant.
4354  *
4355  * Fork - LGPL
4356  * <script type="text/javascript">
4357  */
4358
4359
4360 /**
4361  * @class Roo.data.SortTypes
4362  * @singleton
4363  * Defines the default sorting (casting?) comparison functions used when sorting data.
4364  */
4365 Roo.data.SortTypes = {
4366     /**
4367      * Default sort that does nothing
4368      * @param {Mixed} s The value being converted
4369      * @return {Mixed} The comparison value
4370      */
4371     none : function(s){
4372         return s;
4373     },
4374     
4375     /**
4376      * The regular expression used to strip tags
4377      * @type {RegExp}
4378      * @property
4379      */
4380     stripTagsRE : /<\/?[^>]+>/gi,
4381     
4382     /**
4383      * Strips all HTML tags to sort on text only
4384      * @param {Mixed} s The value being converted
4385      * @return {String} The comparison value
4386      */
4387     asText : function(s){
4388         return String(s).replace(this.stripTagsRE, "");
4389     },
4390     
4391     /**
4392      * Strips all HTML tags to sort on text only - Case insensitive
4393      * @param {Mixed} s The value being converted
4394      * @return {String} The comparison value
4395      */
4396     asUCText : function(s){
4397         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4398     },
4399     
4400     /**
4401      * Case insensitive string
4402      * @param {Mixed} s The value being converted
4403      * @return {String} The comparison value
4404      */
4405     asUCString : function(s) {
4406         return String(s).toUpperCase();
4407     },
4408     
4409     /**
4410      * Date sorting
4411      * @param {Mixed} s The value being converted
4412      * @return {Number} The comparison value
4413      */
4414     asDate : function(s) {
4415         if(!s){
4416             return 0;
4417         }
4418         if(s instanceof Date){
4419             return s.getTime();
4420         }
4421         return Date.parse(String(s));
4422     },
4423     
4424     /**
4425      * Float sorting
4426      * @param {Mixed} s The value being converted
4427      * @return {Float} The comparison value
4428      */
4429     asFloat : function(s) {
4430         var val = parseFloat(String(s).replace(/,/g, ""));
4431         if(isNaN(val)) val = 0;
4432         return val;
4433     },
4434     
4435     /**
4436      * Integer sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Number} The comparison value
4439      */
4440     asInt : function(s) {
4441         var val = parseInt(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     }
4445 };/*
4446  * Based on:
4447  * Ext JS Library 1.1.1
4448  * Copyright(c) 2006-2007, Ext JS, LLC.
4449  *
4450  * Originally Released Under LGPL - original licence link has changed is not relivant.
4451  *
4452  * Fork - LGPL
4453  * <script type="text/javascript">
4454  */
4455
4456 /**
4457 * @class Roo.data.Record
4458  * Instances of this class encapsulate both record <em>definition</em> information, and record
4459  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4460  * to access Records cached in an {@link Roo.data.Store} object.<br>
4461  * <p>
4462  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4463  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4464  * objects.<br>
4465  * <p>
4466  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4467  * @constructor
4468  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4469  * {@link #create}. The parameters are the same.
4470  * @param {Array} data An associative Array of data values keyed by the field name.
4471  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4472  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4473  * not specified an integer id is generated.
4474  */
4475 Roo.data.Record = function(data, id){
4476     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4477     this.data = data;
4478 };
4479
4480 /**
4481  * Generate a constructor for a specific record layout.
4482  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4483  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4484  * Each field definition object may contain the following properties: <ul>
4485  * <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,
4486  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4487  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4488  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4489  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4490  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4491  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4492  * this may be omitted.</p></li>
4493  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4494  * <ul><li>auto (Default, implies no conversion)</li>
4495  * <li>string</li>
4496  * <li>int</li>
4497  * <li>float</li>
4498  * <li>boolean</li>
4499  * <li>date</li></ul></p></li>
4500  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4501  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4502  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4503  * by the Reader into an object that will be stored in the Record. It is passed the
4504  * following parameters:<ul>
4505  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4506  * </ul></p></li>
4507  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4508  * </ul>
4509  * <br>usage:<br><pre><code>
4510 var TopicRecord = Roo.data.Record.create(
4511     {name: 'title', mapping: 'topic_title'},
4512     {name: 'author', mapping: 'username'},
4513     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4514     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4515     {name: 'lastPoster', mapping: 'user2'},
4516     {name: 'excerpt', mapping: 'post_text'}
4517 );
4518
4519 var myNewRecord = new TopicRecord({
4520     title: 'Do my job please',
4521     author: 'noobie',
4522     totalPosts: 1,
4523     lastPost: new Date(),
4524     lastPoster: 'Animal',
4525     excerpt: 'No way dude!'
4526 });
4527 myStore.add(myNewRecord);
4528 </code></pre>
4529  * @method create
4530  * @static
4531  */
4532 Roo.data.Record.create = function(o){
4533     var f = function(){
4534         f.superclass.constructor.apply(this, arguments);
4535     };
4536     Roo.extend(f, Roo.data.Record);
4537     var p = f.prototype;
4538     p.fields = new Roo.util.MixedCollection(false, function(field){
4539         return field.name;
4540     });
4541     for(var i = 0, len = o.length; i < len; i++){
4542         p.fields.add(new Roo.data.Field(o[i]));
4543     }
4544     f.getField = function(name){
4545         return p.fields.get(name);  
4546     };
4547     return f;
4548 };
4549
4550 Roo.data.Record.AUTO_ID = 1000;
4551 Roo.data.Record.EDIT = 'edit';
4552 Roo.data.Record.REJECT = 'reject';
4553 Roo.data.Record.COMMIT = 'commit';
4554
4555 Roo.data.Record.prototype = {
4556     /**
4557      * Readonly flag - true if this record has been modified.
4558      * @type Boolean
4559      */
4560     dirty : false,
4561     editing : false,
4562     error: null,
4563     modified: null,
4564
4565     // private
4566     join : function(store){
4567         this.store = store;
4568     },
4569
4570     /**
4571      * Set the named field to the specified value.
4572      * @param {String} name The name of the field to set.
4573      * @param {Object} value The value to set the field to.
4574      */
4575     set : function(name, value){
4576         if(this.data[name] == value){
4577             return;
4578         }
4579         this.dirty = true;
4580         if(!this.modified){
4581             this.modified = {};
4582         }
4583         if(typeof this.modified[name] == 'undefined'){
4584             this.modified[name] = this.data[name];
4585         }
4586         this.data[name] = value;
4587         if(!this.editing){
4588             this.store.afterEdit(this);
4589         }       
4590     },
4591
4592     /**
4593      * Get the value of the named field.
4594      * @param {String} name The name of the field to get the value of.
4595      * @return {Object} The value of the field.
4596      */
4597     get : function(name){
4598         return this.data[name]; 
4599     },
4600
4601     // private
4602     beginEdit : function(){
4603         this.editing = true;
4604         this.modified = {}; 
4605     },
4606
4607     // private
4608     cancelEdit : function(){
4609         this.editing = false;
4610         delete this.modified;
4611     },
4612
4613     // private
4614     endEdit : function(){
4615         this.editing = false;
4616         if(this.dirty && this.store){
4617             this.store.afterEdit(this);
4618         }
4619     },
4620
4621     /**
4622      * Usually called by the {@link Roo.data.Store} which owns the Record.
4623      * Rejects all changes made to the Record since either creation, or the last commit operation.
4624      * Modified fields are reverted to their original values.
4625      * <p>
4626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4627      * of reject operations.
4628      */
4629     reject : function(){
4630         var m = this.modified;
4631         for(var n in m){
4632             if(typeof m[n] != "function"){
4633                 this.data[n] = m[n];
4634             }
4635         }
4636         this.dirty = false;
4637         delete this.modified;
4638         this.editing = false;
4639         if(this.store){
4640             this.store.afterReject(this);
4641         }
4642     },
4643
4644     /**
4645      * Usually called by the {@link Roo.data.Store} which owns the Record.
4646      * Commits all changes made to the Record since either creation, or the last commit operation.
4647      * <p>
4648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4649      * of commit operations.
4650      */
4651     commit : function(){
4652         this.dirty = false;
4653         delete this.modified;
4654         this.editing = false;
4655         if(this.store){
4656             this.store.afterCommit(this);
4657         }
4658     },
4659
4660     // private
4661     hasError : function(){
4662         return this.error != null;
4663     },
4664
4665     // private
4666     clearError : function(){
4667         this.error = null;
4668     },
4669
4670     /**
4671      * Creates a copy of this record.
4672      * @param {String} id (optional) A new record id if you don't want to use this record's id
4673      * @return {Record}
4674      */
4675     copy : function(newId) {
4676         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4677     }
4678 };/*
4679  * Based on:
4680  * Ext JS Library 1.1.1
4681  * Copyright(c) 2006-2007, Ext JS, LLC.
4682  *
4683  * Originally Released Under LGPL - original licence link has changed is not relivant.
4684  *
4685  * Fork - LGPL
4686  * <script type="text/javascript">
4687  */
4688
4689
4690
4691 /**
4692  * @class Roo.data.Store
4693  * @extends Roo.util.Observable
4694  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4695  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4696  * <p>
4697  * 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
4698  * has no knowledge of the format of the data returned by the Proxy.<br>
4699  * <p>
4700  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4701  * instances from the data object. These records are cached and made available through accessor functions.
4702  * @constructor
4703  * Creates a new Store.
4704  * @param {Object} config A config object containing the objects needed for the Store to access data,
4705  * and read the data into Records.
4706  */
4707 Roo.data.Store = function(config){
4708     this.data = new Roo.util.MixedCollection(false);
4709     this.data.getKey = function(o){
4710         return o.id;
4711     };
4712     this.baseParams = {};
4713     // private
4714     this.paramNames = {
4715         "start" : "start",
4716         "limit" : "limit",
4717         "sort" : "sort",
4718         "dir" : "dir"
4719     };
4720
4721     if(config && config.data){
4722         this.inlineData = config.data;
4723         delete config.data;
4724     }
4725
4726     Roo.apply(this, config);
4727     
4728     if(this.reader){ // reader passed
4729         this.reader = Roo.factory(this.reader, Roo.data);
4730         this.reader.xmodule = this.xmodule || false;
4731         if(!this.recordType){
4732             this.recordType = this.reader.recordType;
4733         }
4734         if(this.reader.onMetaChange){
4735             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4736         }
4737     }
4738
4739     if(this.recordType){
4740         this.fields = this.recordType.prototype.fields;
4741     }
4742     this.modified = [];
4743
4744     this.addEvents({
4745         /**
4746          * @event datachanged
4747          * Fires when the data cache has changed, and a widget which is using this Store
4748          * as a Record cache should refresh its view.
4749          * @param {Store} this
4750          */
4751         datachanged : true,
4752         /**
4753          * @event metachange
4754          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4755          * @param {Store} this
4756          * @param {Object} meta The JSON metadata
4757          */
4758         metachange : true,
4759         /**
4760          * @event add
4761          * Fires when Records have been added to the Store
4762          * @param {Store} this
4763          * @param {Roo.data.Record[]} records The array of Records added
4764          * @param {Number} index The index at which the record(s) were added
4765          */
4766         add : true,
4767         /**
4768          * @event remove
4769          * Fires when a Record has been removed from the Store
4770          * @param {Store} this
4771          * @param {Roo.data.Record} record The Record that was removed
4772          * @param {Number} index The index at which the record was removed
4773          */
4774         remove : true,
4775         /**
4776          * @event update
4777          * Fires when a Record has been updated
4778          * @param {Store} this
4779          * @param {Roo.data.Record} record The Record that was updated
4780          * @param {String} operation The update operation being performed.  Value may be one of:
4781          * <pre><code>
4782  Roo.data.Record.EDIT
4783  Roo.data.Record.REJECT
4784  Roo.data.Record.COMMIT
4785          * </code></pre>
4786          */
4787         update : true,
4788         /**
4789          * @event clear
4790          * Fires when the data cache has been cleared.
4791          * @param {Store} this
4792          */
4793         clear : true,
4794         /**
4795          * @event beforeload
4796          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4797          * the load action will be canceled.
4798          * @param {Store} this
4799          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4800          */
4801         beforeload : true,
4802         /**
4803          * @event load
4804          * Fires after a new set of Records has been loaded.
4805          * @param {Store} this
4806          * @param {Roo.data.Record[]} records The Records that were loaded
4807          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4808          */
4809         load : true,
4810         /**
4811          * @event loadexception
4812          * Fires if an exception occurs in the Proxy during loading.
4813          * Called with the signature of the Proxy's "loadexception" event.
4814          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4815          * 
4816          * @param {Proxy} 
4817          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4818          * @param {Object} load options 
4819          * @param {Object} jsonData from your request (normally this contains the Exception)
4820          */
4821         loadexception : true
4822     });
4823     
4824     if(this.proxy){
4825         this.proxy = Roo.factory(this.proxy, Roo.data);
4826         this.proxy.xmodule = this.xmodule || false;
4827         this.relayEvents(this.proxy,  ["loadexception"]);
4828     }
4829     this.sortToggle = {};
4830
4831     Roo.data.Store.superclass.constructor.call(this);
4832
4833     if(this.inlineData){
4834         this.loadData(this.inlineData);
4835         delete this.inlineData;
4836     }
4837 };
4838 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4839      /**
4840     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4841     * without a remote query - used by combo/forms at present.
4842     */
4843     
4844     /**
4845     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4846     */
4847     /**
4848     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4849     */
4850     /**
4851     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4852     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4853     */
4854     /**
4855     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4856     * on any HTTP request
4857     */
4858     /**
4859     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4860     */
4861     /**
4862     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4863     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4864     */
4865     remoteSort : false,
4866
4867     /**
4868     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4869      * loaded or when a record is removed. (defaults to false).
4870     */
4871     pruneModifiedRecords : false,
4872
4873     // private
4874     lastOptions : null,
4875
4876     /**
4877      * Add Records to the Store and fires the add event.
4878      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4879      */
4880     add : function(records){
4881         records = [].concat(records);
4882         for(var i = 0, len = records.length; i < len; i++){
4883             records[i].join(this);
4884         }
4885         var index = this.data.length;
4886         this.data.addAll(records);
4887         this.fireEvent("add", this, records, index);
4888     },
4889
4890     /**
4891      * Remove a Record from the Store and fires the remove event.
4892      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4893      */
4894     remove : function(record){
4895         var index = this.data.indexOf(record);
4896         this.data.removeAt(index);
4897         if(this.pruneModifiedRecords){
4898             this.modified.remove(record);
4899         }
4900         this.fireEvent("remove", this, record, index);
4901     },
4902
4903     /**
4904      * Remove all Records from the Store and fires the clear event.
4905      */
4906     removeAll : function(){
4907         this.data.clear();
4908         if(this.pruneModifiedRecords){
4909             this.modified = [];
4910         }
4911         this.fireEvent("clear", this);
4912     },
4913
4914     /**
4915      * Inserts Records to the Store at the given index and fires the add event.
4916      * @param {Number} index The start index at which to insert the passed Records.
4917      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4918      */
4919     insert : function(index, records){
4920         records = [].concat(records);
4921         for(var i = 0, len = records.length; i < len; i++){
4922             this.data.insert(index, records[i]);
4923             records[i].join(this);
4924         }
4925         this.fireEvent("add", this, records, index);
4926     },
4927
4928     /**
4929      * Get the index within the cache of the passed Record.
4930      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4931      * @return {Number} The index of the passed Record. Returns -1 if not found.
4932      */
4933     indexOf : function(record){
4934         return this.data.indexOf(record);
4935     },
4936
4937     /**
4938      * Get the index within the cache of the Record with the passed id.
4939      * @param {String} id The id of the Record to find.
4940      * @return {Number} The index of the Record. Returns -1 if not found.
4941      */
4942     indexOfId : function(id){
4943         return this.data.indexOfKey(id);
4944     },
4945
4946     /**
4947      * Get the Record with the specified id.
4948      * @param {String} id The id of the Record to find.
4949      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4950      */
4951     getById : function(id){
4952         return this.data.key(id);
4953     },
4954
4955     /**
4956      * Get the Record at the specified index.
4957      * @param {Number} index The index of the Record to find.
4958      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4959      */
4960     getAt : function(index){
4961         return this.data.itemAt(index);
4962     },
4963
4964     /**
4965      * Returns a range of Records between specified indices.
4966      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4967      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4968      * @return {Roo.data.Record[]} An array of Records
4969      */
4970     getRange : function(start, end){
4971         return this.data.getRange(start, end);
4972     },
4973
4974     // private
4975     storeOptions : function(o){
4976         o = Roo.apply({}, o);
4977         delete o.callback;
4978         delete o.scope;
4979         this.lastOptions = o;
4980     },
4981
4982     /**
4983      * Loads the Record cache from the configured Proxy using the configured Reader.
4984      * <p>
4985      * If using remote paging, then the first load call must specify the <em>start</em>
4986      * and <em>limit</em> properties in the options.params property to establish the initial
4987      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4988      * <p>
4989      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4990      * and this call will return before the new data has been loaded. Perform any post-processing
4991      * in a callback function, or in a "load" event handler.</strong>
4992      * <p>
4993      * @param {Object} options An object containing properties which control loading options:<ul>
4994      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4995      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4996      * passed the following arguments:<ul>
4997      * <li>r : Roo.data.Record[]</li>
4998      * <li>options: Options object from the load call</li>
4999      * <li>success: Boolean success indicator</li></ul></li>
5000      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5001      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5002      * </ul>
5003      */
5004     load : function(options){
5005         options = options || {};
5006         if(this.fireEvent("beforeload", this, options) !== false){
5007             this.storeOptions(options);
5008             var p = Roo.apply(options.params || {}, this.baseParams);
5009             // if meta was not loaded from remote source.. try requesting it.
5010             if (!this.reader.metaFromRemote) {
5011                 p._requestMeta = 1;
5012             }
5013             if(this.sortInfo && this.remoteSort){
5014                 var pn = this.paramNames;
5015                 p[pn["sort"]] = this.sortInfo.field;
5016                 p[pn["dir"]] = this.sortInfo.direction;
5017             }
5018             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5019         }
5020     },
5021
5022     /**
5023      * Reloads the Record cache from the configured Proxy using the configured Reader and
5024      * the options from the last load operation performed.
5025      * @param {Object} options (optional) An object containing properties which may override the options
5026      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5027      * the most recently used options are reused).
5028      */
5029     reload : function(options){
5030         this.load(Roo.applyIf(options||{}, this.lastOptions));
5031     },
5032
5033     // private
5034     // Called as a callback by the Reader during a load operation.
5035     loadRecords : function(o, options, success){
5036         if(!o || success === false){
5037             if(success !== false){
5038                 this.fireEvent("load", this, [], options);
5039             }
5040             if(options.callback){
5041                 options.callback.call(options.scope || this, [], options, false);
5042             }
5043             return;
5044         }
5045         // if data returned failure - throw an exception.
5046         if (o.success === false) {
5047             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5048             return;
5049         }
5050         var r = o.records, t = o.totalRecords || r.length;
5051         if(!options || options.add !== true){
5052             if(this.pruneModifiedRecords){
5053                 this.modified = [];
5054             }
5055             for(var i = 0, len = r.length; i < len; i++){
5056                 r[i].join(this);
5057             }
5058             if(this.snapshot){
5059                 this.data = this.snapshot;
5060                 delete this.snapshot;
5061             }
5062             this.data.clear();
5063             this.data.addAll(r);
5064             this.totalLength = t;
5065             this.applySort();
5066             this.fireEvent("datachanged", this);
5067         }else{
5068             this.totalLength = Math.max(t, this.data.length+r.length);
5069             this.add(r);
5070         }
5071         this.fireEvent("load", this, r, options);
5072         if(options.callback){
5073             options.callback.call(options.scope || this, r, options, true);
5074         }
5075     },
5076
5077     /**
5078      * Loads data from a passed data block. A Reader which understands the format of the data
5079      * must have been configured in the constructor.
5080      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5081      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5082      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5083      */
5084     loadData : function(o, append){
5085         var r = this.reader.readRecords(o);
5086         this.loadRecords(r, {add: append}, true);
5087     },
5088
5089     /**
5090      * Gets the number of cached records.
5091      * <p>
5092      * <em>If using paging, this may not be the total size of the dataset. If the data object
5093      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5094      * the data set size</em>
5095      */
5096     getCount : function(){
5097         return this.data.length || 0;
5098     },
5099
5100     /**
5101      * Gets the total number of records in the dataset as returned by the server.
5102      * <p>
5103      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5104      * the dataset size</em>
5105      */
5106     getTotalCount : function(){
5107         return this.totalLength || 0;
5108     },
5109
5110     /**
5111      * Returns the sort state of the Store as an object with two properties:
5112      * <pre><code>
5113  field {String} The name of the field by which the Records are sorted
5114  direction {String} The sort order, "ASC" or "DESC"
5115      * </code></pre>
5116      */
5117     getSortState : function(){
5118         return this.sortInfo;
5119     },
5120
5121     // private
5122     applySort : function(){
5123         if(this.sortInfo && !this.remoteSort){
5124             var s = this.sortInfo, f = s.field;
5125             var st = this.fields.get(f).sortType;
5126             var fn = function(r1, r2){
5127                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5128                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5129             };
5130             this.data.sort(s.direction, fn);
5131             if(this.snapshot && this.snapshot != this.data){
5132                 this.snapshot.sort(s.direction, fn);
5133             }
5134         }
5135     },
5136
5137     /**
5138      * Sets the default sort column and order to be used by the next load operation.
5139      * @param {String} fieldName The name of the field to sort by.
5140      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5141      */
5142     setDefaultSort : function(field, dir){
5143         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5144     },
5145
5146     /**
5147      * Sort the Records.
5148      * If remote sorting is used, the sort is performed on the server, and the cache is
5149      * reloaded. If local sorting is used, the cache is sorted internally.
5150      * @param {String} fieldName The name of the field to sort by.
5151      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5152      */
5153     sort : function(fieldName, dir){
5154         var f = this.fields.get(fieldName);
5155         if(!dir){
5156             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5157                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5158             }else{
5159                 dir = f.sortDir;
5160             }
5161         }
5162         this.sortToggle[f.name] = dir;
5163         this.sortInfo = {field: f.name, direction: dir};
5164         if(!this.remoteSort){
5165             this.applySort();
5166             this.fireEvent("datachanged", this);
5167         }else{
5168             this.load(this.lastOptions);
5169         }
5170     },
5171
5172     /**
5173      * Calls the specified function for each of the Records in the cache.
5174      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5175      * Returning <em>false</em> aborts and exits the iteration.
5176      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5177      */
5178     each : function(fn, scope){
5179         this.data.each(fn, scope);
5180     },
5181
5182     /**
5183      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5184      * (e.g., during paging).
5185      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5186      */
5187     getModifiedRecords : function(){
5188         return this.modified;
5189     },
5190
5191     // private
5192     createFilterFn : function(property, value, anyMatch){
5193         if(!value.exec){ // not a regex
5194             value = String(value);
5195             if(value.length == 0){
5196                 return false;
5197             }
5198             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5199         }
5200         return function(r){
5201             return value.test(r.data[property]);
5202         };
5203     },
5204
5205     /**
5206      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5207      * @param {String} property A field on your records
5208      * @param {Number} start The record index to start at (defaults to 0)
5209      * @param {Number} end The last record index to include (defaults to length - 1)
5210      * @return {Number} The sum
5211      */
5212     sum : function(property, start, end){
5213         var rs = this.data.items, v = 0;
5214         start = start || 0;
5215         end = (end || end === 0) ? end : rs.length-1;
5216
5217         for(var i = start; i <= end; i++){
5218             v += (rs[i].data[property] || 0);
5219         }
5220         return v;
5221     },
5222
5223     /**
5224      * Filter the records by a specified property.
5225      * @param {String} field A field on your records
5226      * @param {String/RegExp} value Either a string that the field
5227      * should start with or a RegExp to test against the field
5228      * @param {Boolean} anyMatch True to match any part not just the beginning
5229      */
5230     filter : function(property, value, anyMatch){
5231         var fn = this.createFilterFn(property, value, anyMatch);
5232         return fn ? this.filterBy(fn) : this.clearFilter();
5233     },
5234
5235     /**
5236      * Filter by a function. The specified function will be called with each
5237      * record in this data source. If the function returns true the record is included,
5238      * otherwise it is filtered.
5239      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5240      * @param {Object} scope (optional) The scope of the function (defaults to this)
5241      */
5242     filterBy : function(fn, scope){
5243         this.snapshot = this.snapshot || this.data;
5244         this.data = this.queryBy(fn, scope||this);
5245         this.fireEvent("datachanged", this);
5246     },
5247
5248     /**
5249      * Query the records by a specified property.
5250      * @param {String} field A field on your records
5251      * @param {String/RegExp} value Either a string that the field
5252      * should start with or a RegExp to test against the field
5253      * @param {Boolean} anyMatch True to match any part not just the beginning
5254      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5255      */
5256     query : function(property, value, anyMatch){
5257         var fn = this.createFilterFn(property, value, anyMatch);
5258         return fn ? this.queryBy(fn) : this.data.clone();
5259     },
5260
5261     /**
5262      * Query by a function. The specified function will be called with each
5263      * record in this data source. If the function returns true the record is included
5264      * in the results.
5265      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5266      * @param {Object} scope (optional) The scope of the function (defaults to this)
5267       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5268      **/
5269     queryBy : function(fn, scope){
5270         var data = this.snapshot || this.data;
5271         return data.filterBy(fn, scope||this);
5272     },
5273
5274     /**
5275      * Collects unique values for a particular dataIndex from this store.
5276      * @param {String} dataIndex The property to collect
5277      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5278      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5279      * @return {Array} An array of the unique values
5280      **/
5281     collect : function(dataIndex, allowNull, bypassFilter){
5282         var d = (bypassFilter === true && this.snapshot) ?
5283                 this.snapshot.items : this.data.items;
5284         var v, sv, r = [], l = {};
5285         for(var i = 0, len = d.length; i < len; i++){
5286             v = d[i].data[dataIndex];
5287             sv = String(v);
5288             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5289                 l[sv] = true;
5290                 r[r.length] = v;
5291             }
5292         }
5293         return r;
5294     },
5295
5296     /**
5297      * Revert to a view of the Record cache with no filtering applied.
5298      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5299      */
5300     clearFilter : function(suppressEvent){
5301         if(this.snapshot && this.snapshot != this.data){
5302             this.data = this.snapshot;
5303             delete this.snapshot;
5304             if(suppressEvent !== true){
5305                 this.fireEvent("datachanged", this);
5306             }
5307         }
5308     },
5309
5310     // private
5311     afterEdit : function(record){
5312         if(this.modified.indexOf(record) == -1){
5313             this.modified.push(record);
5314         }
5315         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5316     },
5317
5318     // private
5319     afterReject : function(record){
5320         this.modified.remove(record);
5321         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5322     },
5323
5324     // private
5325     afterCommit : function(record){
5326         this.modified.remove(record);
5327         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5328     },
5329
5330     /**
5331      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5332      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5333      */
5334     commitChanges : function(){
5335         var m = this.modified.slice(0);
5336         this.modified = [];
5337         for(var i = 0, len = m.length; i < len; i++){
5338             m[i].commit();
5339         }
5340     },
5341
5342     /**
5343      * Cancel outstanding changes on all changed records.
5344      */
5345     rejectChanges : function(){
5346         var m = this.modified.slice(0);
5347         this.modified = [];
5348         for(var i = 0, len = m.length; i < len; i++){
5349             m[i].reject();
5350         }
5351     },
5352
5353     onMetaChange : function(meta, rtype, o){
5354         this.recordType = rtype;
5355         this.fields = rtype.prototype.fields;
5356         delete this.snapshot;
5357         this.sortInfo = meta.sortInfo || this.sortInfo;
5358         this.modified = [];
5359         this.fireEvent('metachange', this, this.reader.meta);
5360     }
5361 });/*
5362  * Based on:
5363  * Ext JS Library 1.1.1
5364  * Copyright(c) 2006-2007, Ext JS, LLC.
5365  *
5366  * Originally Released Under LGPL - original licence link has changed is not relivant.
5367  *
5368  * Fork - LGPL
5369  * <script type="text/javascript">
5370  */
5371
5372 /**
5373  * @class Roo.data.SimpleStore
5374  * @extends Roo.data.Store
5375  * Small helper class to make creating Stores from Array data easier.
5376  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5377  * @cfg {Array} fields An array of field definition objects, or field name strings.
5378  * @cfg {Array} data The multi-dimensional array of data
5379  * @constructor
5380  * @param {Object} config
5381  */
5382 Roo.data.SimpleStore = function(config){
5383     Roo.data.SimpleStore.superclass.constructor.call(this, {
5384         isLocal : true,
5385         reader: new Roo.data.ArrayReader({
5386                 id: config.id
5387             },
5388             Roo.data.Record.create(config.fields)
5389         ),
5390         proxy : new Roo.data.MemoryProxy(config.data)
5391     });
5392     this.load();
5393 };
5394 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5395  * Based on:
5396  * Ext JS Library 1.1.1
5397  * Copyright(c) 2006-2007, Ext JS, LLC.
5398  *
5399  * Originally Released Under LGPL - original licence link has changed is not relivant.
5400  *
5401  * Fork - LGPL
5402  * <script type="text/javascript">
5403  */
5404
5405 /**
5406 /**
5407  * @extends Roo.data.Store
5408  * @class Roo.data.JsonStore
5409  * Small helper class to make creating Stores for JSON data easier. <br/>
5410 <pre><code>
5411 var store = new Roo.data.JsonStore({
5412     url: 'get-images.php',
5413     root: 'images',
5414     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5415 });
5416 </code></pre>
5417  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5418  * JsonReader and HttpProxy (unless inline data is provided).</b>
5419  * @cfg {Array} fields An array of field definition objects, or field name strings.
5420  * @constructor
5421  * @param {Object} config
5422  */
5423 Roo.data.JsonStore = function(c){
5424     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5425         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5426         reader: new Roo.data.JsonReader(c, c.fields)
5427     }));
5428 };
5429 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5430  * Based on:
5431  * Ext JS Library 1.1.1
5432  * Copyright(c) 2006-2007, Ext JS, LLC.
5433  *
5434  * Originally Released Under LGPL - original licence link has changed is not relivant.
5435  *
5436  * Fork - LGPL
5437  * <script type="text/javascript">
5438  */
5439
5440  
5441 Roo.data.Field = function(config){
5442     if(typeof config == "string"){
5443         config = {name: config};
5444     }
5445     Roo.apply(this, config);
5446     
5447     if(!this.type){
5448         this.type = "auto";
5449     }
5450     
5451     var st = Roo.data.SortTypes;
5452     // named sortTypes are supported, here we look them up
5453     if(typeof this.sortType == "string"){
5454         this.sortType = st[this.sortType];
5455     }
5456     
5457     // set default sortType for strings and dates
5458     if(!this.sortType){
5459         switch(this.type){
5460             case "string":
5461                 this.sortType = st.asUCString;
5462                 break;
5463             case "date":
5464                 this.sortType = st.asDate;
5465                 break;
5466             default:
5467                 this.sortType = st.none;
5468         }
5469     }
5470
5471     // define once
5472     var stripRe = /[\$,%]/g;
5473
5474     // prebuilt conversion function for this field, instead of
5475     // switching every time we're reading a value
5476     if(!this.convert){
5477         var cv, dateFormat = this.dateFormat;
5478         switch(this.type){
5479             case "":
5480             case "auto":
5481             case undefined:
5482                 cv = function(v){ return v; };
5483                 break;
5484             case "string":
5485                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5486                 break;
5487             case "int":
5488                 cv = function(v){
5489                     return v !== undefined && v !== null && v !== '' ?
5490                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5491                     };
5492                 break;
5493             case "float":
5494                 cv = function(v){
5495                     return v !== undefined && v !== null && v !== '' ?
5496                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5497                     };
5498                 break;
5499             case "bool":
5500             case "boolean":
5501                 cv = function(v){ return v === true || v === "true" || v == 1; };
5502                 break;
5503             case "date":
5504                 cv = function(v){
5505                     if(!v){
5506                         return '';
5507                     }
5508                     if(v instanceof Date){
5509                         return v;
5510                     }
5511                     if(dateFormat){
5512                         if(dateFormat == "timestamp"){
5513                             return new Date(v*1000);
5514                         }
5515                         return Date.parseDate(v, dateFormat);
5516                     }
5517                     var parsed = Date.parse(v);
5518                     return parsed ? new Date(parsed) : null;
5519                 };
5520              break;
5521             
5522         }
5523         this.convert = cv;
5524     }
5525 };
5526
5527 Roo.data.Field.prototype = {
5528     dateFormat: null,
5529     defaultValue: "",
5530     mapping: null,
5531     sortType : null,
5532     sortDir : "ASC"
5533 };/*
5534  * Based on:
5535  * Ext JS Library 1.1.1
5536  * Copyright(c) 2006-2007, Ext JS, LLC.
5537  *
5538  * Originally Released Under LGPL - original licence link has changed is not relivant.
5539  *
5540  * Fork - LGPL
5541  * <script type="text/javascript">
5542  */
5543  
5544 // Base class for reading structured data from a data source.  This class is intended to be
5545 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5546
5547 /**
5548  * @class Roo.data.DataReader
5549  * Base class for reading structured data from a data source.  This class is intended to be
5550  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5551  */
5552
5553 Roo.data.DataReader = function(meta, recordType){
5554     
5555     this.meta = meta;
5556     
5557     this.recordType = recordType instanceof Array ? 
5558         Roo.data.Record.create(recordType) : recordType;
5559 };
5560
5561 Roo.data.DataReader.prototype = {
5562      /**
5563      * Create an empty record
5564      * @param {Object} data (optional) - overlay some values
5565      * @return {Roo.data.Record} record created.
5566      */
5567     newRow :  function(d) {
5568         var da =  {};
5569         this.recordType.prototype.fields.each(function(c) {
5570             switch( c.type) {
5571                 case 'int' : da[c.name] = 0; break;
5572                 case 'date' : da[c.name] = new Date(); break;
5573                 case 'float' : da[c.name] = 0.0; break;
5574                 case 'boolean' : da[c.name] = false; break;
5575                 default : da[c.name] = ""; break;
5576             }
5577             
5578         });
5579         return new this.recordType(Roo.apply(da, d));
5580     }
5581     
5582 };/*
5583  * Based on:
5584  * Ext JS Library 1.1.1
5585  * Copyright(c) 2006-2007, Ext JS, LLC.
5586  *
5587  * Originally Released Under LGPL - original licence link has changed is not relivant.
5588  *
5589  * Fork - LGPL
5590  * <script type="text/javascript">
5591  */
5592
5593 /**
5594  * @class Roo.data.DataProxy
5595  * @extends Roo.data.Observable
5596  * This class is an abstract base class for implementations which provide retrieval of
5597  * unformatted data objects.<br>
5598  * <p>
5599  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5600  * (of the appropriate type which knows how to parse the data object) to provide a block of
5601  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5602  * <p>
5603  * Custom implementations must implement the load method as described in
5604  * {@link Roo.data.HttpProxy#load}.
5605  */
5606 Roo.data.DataProxy = function(){
5607     this.addEvents({
5608         /**
5609          * @event beforeload
5610          * Fires before a network request is made to retrieve a data object.
5611          * @param {Object} This DataProxy object.
5612          * @param {Object} params The params parameter to the load function.
5613          */
5614         beforeload : true,
5615         /**
5616          * @event load
5617          * Fires before the load method's callback is called.
5618          * @param {Object} This DataProxy object.
5619          * @param {Object} o The data object.
5620          * @param {Object} arg The callback argument object passed to the load function.
5621          */
5622         load : true,
5623         /**
5624          * @event loadexception
5625          * Fires if an Exception occurs during data retrieval.
5626          * @param {Object} This DataProxy object.
5627          * @param {Object} o The data object.
5628          * @param {Object} arg The callback argument object passed to the load function.
5629          * @param {Object} e The Exception.
5630          */
5631         loadexception : true
5632     });
5633     Roo.data.DataProxy.superclass.constructor.call(this);
5634 };
5635
5636 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5637
5638     /**
5639      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5640      */
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651 /**
5652  * @class Roo.data.MemoryProxy
5653  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5654  * to the Reader when its load method is called.
5655  * @constructor
5656  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5657  */
5658 Roo.data.MemoryProxy = function(data){
5659     if (data.data) {
5660         data = data.data;
5661     }
5662     Roo.data.MemoryProxy.superclass.constructor.call(this);
5663     this.data = data;
5664 };
5665
5666 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5667     /**
5668      * Load data from the requested source (in this case an in-memory
5669      * data object passed to the constructor), read the data object into
5670      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5671      * process that block using the passed callback.
5672      * @param {Object} params This parameter is not used by the MemoryProxy class.
5673      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5674      * object into a block of Roo.data.Records.
5675      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5676      * The function must be passed <ul>
5677      * <li>The Record block object</li>
5678      * <li>The "arg" argument from the load function</li>
5679      * <li>A boolean success indicator</li>
5680      * </ul>
5681      * @param {Object} scope The scope in which to call the callback
5682      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5683      */
5684     load : function(params, reader, callback, scope, arg){
5685         params = params || {};
5686         var result;
5687         try {
5688             result = reader.readRecords(this.data);
5689         }catch(e){
5690             this.fireEvent("loadexception", this, arg, null, e);
5691             callback.call(scope, null, arg, false);
5692             return;
5693         }
5694         callback.call(scope, result, arg, true);
5695     },
5696     
5697     // private
5698     update : function(params, records){
5699         
5700     }
5701 });/*
5702  * Based on:
5703  * Ext JS Library 1.1.1
5704  * Copyright(c) 2006-2007, Ext JS, LLC.
5705  *
5706  * Originally Released Under LGPL - original licence link has changed is not relivant.
5707  *
5708  * Fork - LGPL
5709  * <script type="text/javascript">
5710  */
5711 /**
5712  * @class Roo.data.HttpProxy
5713  * @extends Roo.data.DataProxy
5714  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5715  * configured to reference a certain URL.<br><br>
5716  * <p>
5717  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5718  * from which the running page was served.<br><br>
5719  * <p>
5720  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5721  * <p>
5722  * Be aware that to enable the browser to parse an XML document, the server must set
5723  * the Content-Type header in the HTTP response to "text/xml".
5724  * @constructor
5725  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5726  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5727  * will be used to make the request.
5728  */
5729 Roo.data.HttpProxy = function(conn){
5730     Roo.data.HttpProxy.superclass.constructor.call(this);
5731     // is conn a conn config or a real conn?
5732     this.conn = conn;
5733     this.useAjax = !conn || !conn.events;
5734   
5735 };
5736
5737 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5738     // thse are take from connection...
5739     
5740     /**
5741      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5742      */
5743     /**
5744      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5745      * extra parameters to each request made by this object. (defaults to undefined)
5746      */
5747     /**
5748      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5749      *  to each request made by this object. (defaults to undefined)
5750      */
5751     /**
5752      * @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)
5753      */
5754     /**
5755      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5756      */
5757      /**
5758      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5759      * @type Boolean
5760      */
5761   
5762
5763     /**
5764      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5765      * @type Boolean
5766      */
5767     /**
5768      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5769      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5770      * a finer-grained basis than the DataProxy events.
5771      */
5772     getConnection : function(){
5773         return this.useAjax ? Roo.Ajax : this.conn;
5774     },
5775
5776     /**
5777      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5778      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5779      * process that block using the passed callback.
5780      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5781      * for the request to the remote server.
5782      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5783      * object into a block of Roo.data.Records.
5784      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5785      * The function must be passed <ul>
5786      * <li>The Record block object</li>
5787      * <li>The "arg" argument from the load function</li>
5788      * <li>A boolean success indicator</li>
5789      * </ul>
5790      * @param {Object} scope The scope in which to call the callback
5791      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5792      */
5793     load : function(params, reader, callback, scope, arg){
5794         if(this.fireEvent("beforeload", this, params) !== false){
5795             var  o = {
5796                 params : params || {},
5797                 request: {
5798                     callback : callback,
5799                     scope : scope,
5800                     arg : arg
5801                 },
5802                 reader: reader,
5803                 callback : this.loadResponse,
5804                 scope: this
5805             };
5806             if(this.useAjax){
5807                 Roo.applyIf(o, this.conn);
5808                 if(this.activeRequest){
5809                     Roo.Ajax.abort(this.activeRequest);
5810                 }
5811                 this.activeRequest = Roo.Ajax.request(o);
5812             }else{
5813                 this.conn.request(o);
5814             }
5815         }else{
5816             callback.call(scope||this, null, arg, false);
5817         }
5818     },
5819
5820     // private
5821     loadResponse : function(o, success, response){
5822         delete this.activeRequest;
5823         if(!success){
5824             this.fireEvent("loadexception", this, o, response);
5825             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5826             return;
5827         }
5828         var result;
5829         try {
5830             result = o.reader.read(response);
5831         }catch(e){
5832             this.fireEvent("loadexception", this, o, response, e);
5833             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5834             return;
5835         }
5836         
5837         this.fireEvent("load", this, o, o.request.arg);
5838         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5839     },
5840
5841     // private
5842     update : function(dataSet){
5843
5844     },
5845
5846     // private
5847     updateResponse : function(dataSet){
5848
5849     }
5850 });/*
5851  * Based on:
5852  * Ext JS Library 1.1.1
5853  * Copyright(c) 2006-2007, Ext JS, LLC.
5854  *
5855  * Originally Released Under LGPL - original licence link has changed is not relivant.
5856  *
5857  * Fork - LGPL
5858  * <script type="text/javascript">
5859  */
5860
5861 /**
5862  * @class Roo.data.ScriptTagProxy
5863  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5864  * other than the originating domain of the running page.<br><br>
5865  * <p>
5866  * <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
5867  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5868  * <p>
5869  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5870  * source code that is used as the source inside a &lt;script> tag.<br><br>
5871  * <p>
5872  * In order for the browser to process the returned data, the server must wrap the data object
5873  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5874  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5875  * depending on whether the callback name was passed:
5876  * <p>
5877  * <pre><code>
5878 boolean scriptTag = false;
5879 String cb = request.getParameter("callback");
5880 if (cb != null) {
5881     scriptTag = true;
5882     response.setContentType("text/javascript");
5883 } else {
5884     response.setContentType("application/x-json");
5885 }
5886 Writer out = response.getWriter();
5887 if (scriptTag) {
5888     out.write(cb + "(");
5889 }
5890 out.print(dataBlock.toJsonString());
5891 if (scriptTag) {
5892     out.write(");");
5893 }
5894 </pre></code>
5895  *
5896  * @constructor
5897  * @param {Object} config A configuration object.
5898  */
5899 Roo.data.ScriptTagProxy = function(config){
5900     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5901     Roo.apply(this, config);
5902     this.head = document.getElementsByTagName("head")[0];
5903 };
5904
5905 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5906
5907 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5908     /**
5909      * @cfg {String} url The URL from which to request the data object.
5910      */
5911     /**
5912      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5913      */
5914     timeout : 30000,
5915     /**
5916      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5917      * the server the name of the callback function set up by the load call to process the returned data object.
5918      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5919      * javascript output which calls this named function passing the data object as its only parameter.
5920      */
5921     callbackParam : "callback",
5922     /**
5923      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5924      * name to the request.
5925      */
5926     nocache : true,
5927
5928     /**
5929      * Load data from the configured URL, read the data object into
5930      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5931      * process that block using the passed callback.
5932      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5933      * for the request to the remote server.
5934      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5935      * object into a block of Roo.data.Records.
5936      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5937      * The function must be passed <ul>
5938      * <li>The Record block object</li>
5939      * <li>The "arg" argument from the load function</li>
5940      * <li>A boolean success indicator</li>
5941      * </ul>
5942      * @param {Object} scope The scope in which to call the callback
5943      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5944      */
5945     load : function(params, reader, callback, scope, arg){
5946         if(this.fireEvent("beforeload", this, params) !== false){
5947
5948             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5949
5950             var url = this.url;
5951             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5952             if(this.nocache){
5953                 url += "&_dc=" + (new Date().getTime());
5954             }
5955             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5956             var trans = {
5957                 id : transId,
5958                 cb : "stcCallback"+transId,
5959                 scriptId : "stcScript"+transId,
5960                 params : params,
5961                 arg : arg,
5962                 url : url,
5963                 callback : callback,
5964                 scope : scope,
5965                 reader : reader
5966             };
5967             var conn = this;
5968
5969             window[trans.cb] = function(o){
5970                 conn.handleResponse(o, trans);
5971             };
5972
5973             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5974
5975             if(this.autoAbort !== false){
5976                 this.abort();
5977             }
5978
5979             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5980
5981             var script = document.createElement("script");
5982             script.setAttribute("src", url);
5983             script.setAttribute("type", "text/javascript");
5984             script.setAttribute("id", trans.scriptId);
5985             this.head.appendChild(script);
5986
5987             this.trans = trans;
5988         }else{
5989             callback.call(scope||this, null, arg, false);
5990         }
5991     },
5992
5993     // private
5994     isLoading : function(){
5995         return this.trans ? true : false;
5996     },
5997
5998     /**
5999      * Abort the current server request.
6000      */
6001     abort : function(){
6002         if(this.isLoading()){
6003             this.destroyTrans(this.trans);
6004         }
6005     },
6006
6007     // private
6008     destroyTrans : function(trans, isLoaded){
6009         this.head.removeChild(document.getElementById(trans.scriptId));
6010         clearTimeout(trans.timeoutId);
6011         if(isLoaded){
6012             window[trans.cb] = undefined;
6013             try{
6014                 delete window[trans.cb];
6015             }catch(e){}
6016         }else{
6017             // if hasn't been loaded, wait for load to remove it to prevent script error
6018             window[trans.cb] = function(){
6019                 window[trans.cb] = undefined;
6020                 try{
6021                     delete window[trans.cb];
6022                 }catch(e){}
6023             };
6024         }
6025     },
6026
6027     // private
6028     handleResponse : function(o, trans){
6029         this.trans = false;
6030         this.destroyTrans(trans, true);
6031         var result;
6032         try {
6033             result = trans.reader.readRecords(o);
6034         }catch(e){
6035             this.fireEvent("loadexception", this, o, trans.arg, e);
6036             trans.callback.call(trans.scope||window, null, trans.arg, false);
6037             return;
6038         }
6039         this.fireEvent("load", this, o, trans.arg);
6040         trans.callback.call(trans.scope||window, result, trans.arg, true);
6041     },
6042
6043     // private
6044     handleFailure : function(trans){
6045         this.trans = false;
6046         this.destroyTrans(trans, false);
6047         this.fireEvent("loadexception", this, null, trans.arg);
6048         trans.callback.call(trans.scope||window, null, trans.arg, false);
6049     }
6050 });/*
6051  * Based on:
6052  * Ext JS Library 1.1.1
6053  * Copyright(c) 2006-2007, Ext JS, LLC.
6054  *
6055  * Originally Released Under LGPL - original licence link has changed is not relivant.
6056  *
6057  * Fork - LGPL
6058  * <script type="text/javascript">
6059  */
6060
6061 /**
6062  * @class Roo.data.JsonReader
6063  * @extends Roo.data.DataReader
6064  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6065  * based on mappings in a provided Roo.data.Record constructor.
6066  * 
6067  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6068  * in the reply previously. 
6069  * 
6070  * <p>
6071  * Example code:
6072  * <pre><code>
6073 var RecordDef = Roo.data.Record.create([
6074     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6075     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6076 ]);
6077 var myReader = new Roo.data.JsonReader({
6078     totalProperty: "results",    // The property which contains the total dataset size (optional)
6079     root: "rows",                // The property which contains an Array of row objects
6080     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6081 }, RecordDef);
6082 </code></pre>
6083  * <p>
6084  * This would consume a JSON file like this:
6085  * <pre><code>
6086 { 'results': 2, 'rows': [
6087     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6088     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6089 }
6090 </code></pre>
6091  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6092  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6093  * paged from the remote server.
6094  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6095  * @cfg {String} root name of the property which contains the Array of row objects.
6096  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6097  * @constructor
6098  * Create a new JsonReader
6099  * @param {Object} meta Metadata configuration options
6100  * @param {Object} recordType Either an Array of field definition objects,
6101  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6102  */
6103 Roo.data.JsonReader = function(meta, recordType){
6104     
6105     meta = meta || {};
6106     // set some defaults:
6107     Roo.applyIf(meta, {
6108         totalProperty: 'total',
6109         successProperty : 'success',
6110         root : 'data',
6111         id : 'id'
6112     });
6113     
6114     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6115 };
6116 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6117     
6118     /**
6119      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6120      * Used by Store query builder to append _requestMeta to params.
6121      * 
6122      */
6123     metaFromRemote : false,
6124     /**
6125      * This method is only used by a DataProxy which has retrieved data from a remote server.
6126      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6127      * @return {Object} data A data block which is used by an Roo.data.Store object as
6128      * a cache of Roo.data.Records.
6129      */
6130     read : function(response){
6131         var json = response.responseText;
6132        
6133         var o = /* eval:var:o */ eval("("+json+")");
6134         if(!o) {
6135             throw {message: "JsonReader.read: Json object not found"};
6136         }
6137         
6138         if(o.metaData){
6139             
6140             delete this.ef;
6141             this.metaFromRemote = true;
6142             this.meta = o.metaData;
6143             this.recordType = Roo.data.Record.create(o.metaData.fields);
6144             this.onMetaChange(this.meta, this.recordType, o);
6145         }
6146         return this.readRecords(o);
6147     },
6148
6149     // private function a store will implement
6150     onMetaChange : function(meta, recordType, o){
6151
6152     },
6153
6154     /**
6155          * @ignore
6156          */
6157     simpleAccess: function(obj, subsc) {
6158         return obj[subsc];
6159     },
6160
6161         /**
6162          * @ignore
6163          */
6164     getJsonAccessor: function(){
6165         var re = /[\[\.]/;
6166         return function(expr) {
6167             try {
6168                 return(re.test(expr))
6169                     ? new Function("obj", "return obj." + expr)
6170                     : function(obj){
6171                         return obj[expr];
6172                     };
6173             } catch(e){}
6174             return Roo.emptyFn;
6175         };
6176     }(),
6177
6178     /**
6179      * Create a data block containing Roo.data.Records from an XML document.
6180      * @param {Object} o An object which contains an Array of row objects in the property specified
6181      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6182      * which contains the total size of the dataset.
6183      * @return {Object} data A data block which is used by an Roo.data.Store object as
6184      * a cache of Roo.data.Records.
6185      */
6186     readRecords : function(o){
6187         /**
6188          * After any data loads, the raw JSON data is available for further custom processing.
6189          * @type Object
6190          */
6191         this.jsonData = o;
6192         var s = this.meta, Record = this.recordType,
6193             f = Record.prototype.fields, fi = f.items, fl = f.length;
6194
6195 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6196         if (!this.ef) {
6197             if(s.totalProperty) {
6198                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6199                 }
6200                 if(s.successProperty) {
6201                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6202                 }
6203                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6204                 if (s.id) {
6205                         var g = this.getJsonAccessor(s.id);
6206                         this.getId = function(rec) {
6207                                 var r = g(rec);
6208                                 return (r === undefined || r === "") ? null : r;
6209                         };
6210                 } else {
6211                         this.getId = function(){return null;};
6212                 }
6213             this.ef = [];
6214             for(var jj = 0; jj < fl; jj++){
6215                 f = fi[jj];
6216                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6217                 this.ef[jj] = this.getJsonAccessor(map);
6218             }
6219         }
6220
6221         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6222         if(s.totalProperty){
6223             var vt = parseInt(this.getTotal(o), 10);
6224             if(!isNaN(vt)){
6225                 totalRecords = vt;
6226             }
6227         }
6228         if(s.successProperty){
6229             var vs = this.getSuccess(o);
6230             if(vs === false || vs === 'false'){
6231                 success = false;
6232             }
6233         }
6234         var records = [];
6235             for(var i = 0; i < c; i++){
6236                     var n = root[i];
6237                 var values = {};
6238                 var id = this.getId(n);
6239                 for(var j = 0; j < fl; j++){
6240                     f = fi[j];
6241                 var v = this.ef[j](n);
6242                 if (!f.convert) {
6243                     Roo.log('missing convert for ' + f.name);
6244                     Roo.log(f);
6245                     continue;
6246                 }
6247                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6248                 }
6249                 var record = new Record(values, id);
6250                 record.json = n;
6251                 records[i] = record;
6252             }
6253             return {
6254                 success : success,
6255                 records : records,
6256                 totalRecords : totalRecords
6257             };
6258     }
6259 });/*
6260  * Based on:
6261  * Ext JS Library 1.1.1
6262  * Copyright(c) 2006-2007, Ext JS, LLC.
6263  *
6264  * Originally Released Under LGPL - original licence link has changed is not relivant.
6265  *
6266  * Fork - LGPL
6267  * <script type="text/javascript">
6268  */
6269
6270 /**
6271  * @class Roo.data.XmlReader
6272  * @extends Roo.data.DataReader
6273  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6274  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6275  * <p>
6276  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6277  * header in the HTTP response must be set to "text/xml".</em>
6278  * <p>
6279  * Example code:
6280  * <pre><code>
6281 var RecordDef = Roo.data.Record.create([
6282    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6283    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6284 ]);
6285 var myReader = new Roo.data.XmlReader({
6286    totalRecords: "results", // The element which contains the total dataset size (optional)
6287    record: "row",           // The repeated element which contains row information
6288    id: "id"                 // The element within the row that provides an ID for the record (optional)
6289 }, RecordDef);
6290 </code></pre>
6291  * <p>
6292  * This would consume an XML file like this:
6293  * <pre><code>
6294 &lt;?xml?>
6295 &lt;dataset>
6296  &lt;results>2&lt;/results>
6297  &lt;row>
6298    &lt;id>1&lt;/id>
6299    &lt;name>Bill&lt;/name>
6300    &lt;occupation>Gardener&lt;/occupation>
6301  &lt;/row>
6302  &lt;row>
6303    &lt;id>2&lt;/id>
6304    &lt;name>Ben&lt;/name>
6305    &lt;occupation>Horticulturalist&lt;/occupation>
6306  &lt;/row>
6307 &lt;/dataset>
6308 </code></pre>
6309  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6310  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6311  * paged from the remote server.
6312  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6313  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6314  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6315  * a record identifier value.
6316  * @constructor
6317  * Create a new XmlReader
6318  * @param {Object} meta Metadata configuration options
6319  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6320  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6321  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6322  */
6323 Roo.data.XmlReader = function(meta, recordType){
6324     meta = meta || {};
6325     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6326 };
6327 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6328     /**
6329      * This method is only used by a DataProxy which has retrieved data from a remote server.
6330          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6331          * to contain a method called 'responseXML' that returns an XML document object.
6332      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6333      * a cache of Roo.data.Records.
6334      */
6335     read : function(response){
6336         var doc = response.responseXML;
6337         if(!doc) {
6338             throw {message: "XmlReader.read: XML Document not available"};
6339         }
6340         return this.readRecords(doc);
6341     },
6342
6343     /**
6344      * Create a data block containing Roo.data.Records from an XML document.
6345          * @param {Object} doc A parsed XML document.
6346      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6347      * a cache of Roo.data.Records.
6348      */
6349     readRecords : function(doc){
6350         /**
6351          * After any data loads/reads, the raw XML Document is available for further custom processing.
6352          * @type XMLDocument
6353          */
6354         this.xmlData = doc;
6355         var root = doc.documentElement || doc;
6356         var q = Roo.DomQuery;
6357         var recordType = this.recordType, fields = recordType.prototype.fields;
6358         var sid = this.meta.id;
6359         var totalRecords = 0, success = true;
6360         if(this.meta.totalRecords){
6361             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6362         }
6363         
6364         if(this.meta.success){
6365             var sv = q.selectValue(this.meta.success, root, true);
6366             success = sv !== false && sv !== 'false';
6367         }
6368         var records = [];
6369         var ns = q.select(this.meta.record, root);
6370         for(var i = 0, len = ns.length; i < len; i++) {
6371                 var n = ns[i];
6372                 var values = {};
6373                 var id = sid ? q.selectValue(sid, n) : undefined;
6374                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6375                     var f = fields.items[j];
6376                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6377                     v = f.convert(v);
6378                     values[f.name] = v;
6379                 }
6380                 var record = new recordType(values, id);
6381                 record.node = n;
6382                 records[records.length] = record;
6383             }
6384
6385             return {
6386                 success : success,
6387                 records : records,
6388                 totalRecords : totalRecords || records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402 /**
6403  * @class Roo.data.ArrayReader
6404  * @extends Roo.data.DataReader
6405  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6406  * Each element of that Array represents a row of data fields. The
6407  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6408  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6409  * <p>
6410  * Example code:.
6411  * <pre><code>
6412 var RecordDef = Roo.data.Record.create([
6413     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6414     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6415 ]);
6416 var myReader = new Roo.data.ArrayReader({
6417     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6418 }, RecordDef);
6419 </code></pre>
6420  * <p>
6421  * This would consume an Array like this:
6422  * <pre><code>
6423 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6424   </code></pre>
6425  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6426  * @constructor
6427  * Create a new JsonReader
6428  * @param {Object} meta Metadata configuration options.
6429  * @param {Object} recordType Either an Array of field definition objects
6430  * as specified to {@link Roo.data.Record#create},
6431  * or an {@link Roo.data.Record} object
6432  * created using {@link Roo.data.Record#create}.
6433  */
6434 Roo.data.ArrayReader = function(meta, recordType){
6435     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6436 };
6437
6438 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6439     /**
6440      * Create a data block containing Roo.data.Records from an XML document.
6441      * @param {Object} o An Array of row objects which represents the dataset.
6442      * @return {Object} data A data block which is used by an Roo.data.Store object as
6443      * a cache of Roo.data.Records.
6444      */
6445     readRecords : function(o){
6446         var sid = this.meta ? this.meta.id : null;
6447         var recordType = this.recordType, fields = recordType.prototype.fields;
6448         var records = [];
6449         var root = o;
6450             for(var i = 0; i < root.length; i++){
6451                     var n = root[i];
6452                 var values = {};
6453                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                 var f = fields.items[j];
6456                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6457                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6458                 v = f.convert(v);
6459                 values[f.name] = v;
6460             }
6461                 var record = new recordType(values, id);
6462                 record.json = n;
6463                 records[records.length] = record;
6464             }
6465             return {
6466                 records : records,
6467                 totalRecords : records.length
6468             };
6469     }
6470 });/*
6471  * Based on:
6472  * Ext JS Library 1.1.1
6473  * Copyright(c) 2006-2007, Ext JS, LLC.
6474  *
6475  * Originally Released Under LGPL - original licence link has changed is not relivant.
6476  *
6477  * Fork - LGPL
6478  * <script type="text/javascript">
6479  */
6480
6481
6482 /**
6483  * @class Roo.data.Tree
6484  * @extends Roo.util.Observable
6485  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6486  * in the tree have most standard DOM functionality.
6487  * @constructor
6488  * @param {Node} root (optional) The root node
6489  */
6490 Roo.data.Tree = function(root){
6491    this.nodeHash = {};
6492    /**
6493     * The root node for this tree
6494     * @type Node
6495     */
6496    this.root = null;
6497    if(root){
6498        this.setRootNode(root);
6499    }
6500    this.addEvents({
6501        /**
6502         * @event append
6503         * Fires when a new child node is appended to a node in this tree.
6504         * @param {Tree} tree The owner tree
6505         * @param {Node} parent The parent node
6506         * @param {Node} node The newly appended node
6507         * @param {Number} index The index of the newly appended node
6508         */
6509        "append" : true,
6510        /**
6511         * @event remove
6512         * Fires when a child node is removed from a node in this tree.
6513         * @param {Tree} tree The owner tree
6514         * @param {Node} parent The parent node
6515         * @param {Node} node The child node removed
6516         */
6517        "remove" : true,
6518        /**
6519         * @event move
6520         * Fires when a node is moved to a new location in the tree
6521         * @param {Tree} tree The owner tree
6522         * @param {Node} node The node moved
6523         * @param {Node} oldParent The old parent of this node
6524         * @param {Node} newParent The new parent of this node
6525         * @param {Number} index The index it was moved to
6526         */
6527        "move" : true,
6528        /**
6529         * @event insert
6530         * Fires when a new child node is inserted in a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node inserted
6534         * @param {Node} refNode The child node the node was inserted before
6535         */
6536        "insert" : true,
6537        /**
6538         * @event beforeappend
6539         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6540         * @param {Tree} tree The owner tree
6541         * @param {Node} parent The parent node
6542         * @param {Node} node The child node to be appended
6543         */
6544        "beforeappend" : true,
6545        /**
6546         * @event beforeremove
6547         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The child node to be removed
6551         */
6552        "beforeremove" : true,
6553        /**
6554         * @event beforemove
6555         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6556         * @param {Tree} tree The owner tree
6557         * @param {Node} node The node being moved
6558         * @param {Node} oldParent The parent of the node
6559         * @param {Node} newParent The new parent the node is moving to
6560         * @param {Number} index The index it is being moved to
6561         */
6562        "beforemove" : true,
6563        /**
6564         * @event beforeinsert
6565         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be inserted
6569         * @param {Node} refNode The child node the node is being inserted before
6570         */
6571        "beforeinsert" : true
6572    });
6573
6574     Roo.data.Tree.superclass.constructor.call(this);
6575 };
6576
6577 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6578     pathSeparator: "/",
6579
6580     proxyNodeEvent : function(){
6581         return this.fireEvent.apply(this, arguments);
6582     },
6583
6584     /**
6585      * Returns the root node for this tree.
6586      * @return {Node}
6587      */
6588     getRootNode : function(){
6589         return this.root;
6590     },
6591
6592     /**
6593      * Sets the root node for this tree.
6594      * @param {Node} node
6595      * @return {Node}
6596      */
6597     setRootNode : function(node){
6598         this.root = node;
6599         node.ownerTree = this;
6600         node.isRoot = true;
6601         this.registerNode(node);
6602         return node;
6603     },
6604
6605     /**
6606      * Gets a node in this tree by its id.
6607      * @param {String} id
6608      * @return {Node}
6609      */
6610     getNodeById : function(id){
6611         return this.nodeHash[id];
6612     },
6613
6614     registerNode : function(node){
6615         this.nodeHash[node.id] = node;
6616     },
6617
6618     unregisterNode : function(node){
6619         delete this.nodeHash[node.id];
6620     },
6621
6622     toString : function(){
6623         return "[Tree"+(this.id?" "+this.id:"")+"]";
6624     }
6625 });
6626
6627 /**
6628  * @class Roo.data.Node
6629  * @extends Roo.util.Observable
6630  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6631  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6632  * @constructor
6633  * @param {Object} attributes The attributes/config for the node
6634  */
6635 Roo.data.Node = function(attributes){
6636     /**
6637      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6638      * @type {Object}
6639      */
6640     this.attributes = attributes || {};
6641     this.leaf = this.attributes.leaf;
6642     /**
6643      * The node id. @type String
6644      */
6645     this.id = this.attributes.id;
6646     if(!this.id){
6647         this.id = Roo.id(null, "ynode-");
6648         this.attributes.id = this.id;
6649     }
6650     /**
6651      * All child nodes of this node. @type Array
6652      */
6653     this.childNodes = [];
6654     if(!this.childNodes.indexOf){ // indexOf is a must
6655         this.childNodes.indexOf = function(o){
6656             for(var i = 0, len = this.length; i < len; i++){
6657                 if(this[i] == o) {
6658                     return i;
6659                 }
6660             }
6661             return -1;
6662         };
6663     }
6664     /**
6665      * The parent node for this node. @type Node
6666      */
6667     this.parentNode = null;
6668     /**
6669      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6670      */
6671     this.firstChild = null;
6672     /**
6673      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6674      */
6675     this.lastChild = null;
6676     /**
6677      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6678      */
6679     this.previousSibling = null;
6680     /**
6681      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6682      */
6683     this.nextSibling = null;
6684
6685     this.addEvents({
6686        /**
6687         * @event append
6688         * Fires when a new child node is appended
6689         * @param {Tree} tree The owner tree
6690         * @param {Node} this This node
6691         * @param {Node} node The newly appended node
6692         * @param {Number} index The index of the newly appended node
6693         */
6694        "append" : true,
6695        /**
6696         * @event remove
6697         * Fires when a child node is removed
6698         * @param {Tree} tree The owner tree
6699         * @param {Node} this This node
6700         * @param {Node} node The removed node
6701         */
6702        "remove" : true,
6703        /**
6704         * @event move
6705         * Fires when this node is moved to a new location in the tree
6706         * @param {Tree} tree The owner tree
6707         * @param {Node} this This node
6708         * @param {Node} oldParent The old parent of this node
6709         * @param {Node} newParent The new parent of this node
6710         * @param {Number} index The index it was moved to
6711         */
6712        "move" : true,
6713        /**
6714         * @event insert
6715         * Fires when a new child node is inserted.
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The child node inserted
6719         * @param {Node} refNode The child node the node was inserted before
6720         */
6721        "insert" : true,
6722        /**
6723         * @event beforeappend
6724         * Fires before a new child is appended, return false to cancel the append.
6725         * @param {Tree} tree The owner tree
6726         * @param {Node} this This node
6727         * @param {Node} node The child node to be appended
6728         */
6729        "beforeappend" : true,
6730        /**
6731         * @event beforeremove
6732         * Fires before a child is removed, return false to cancel the remove.
6733         * @param {Tree} tree The owner tree
6734         * @param {Node} this This node
6735         * @param {Node} node The child node to be removed
6736         */
6737        "beforeremove" : true,
6738        /**
6739         * @event beforemove
6740         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6741         * @param {Tree} tree The owner tree
6742         * @param {Node} this This node
6743         * @param {Node} oldParent The parent of this node
6744         * @param {Node} newParent The new parent this node is moving to
6745         * @param {Number} index The index it is being moved to
6746         */
6747        "beforemove" : true,
6748        /**
6749         * @event beforeinsert
6750         * Fires before a new child is inserted, return false to cancel the insert.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be inserted
6754         * @param {Node} refNode The child node the node is being inserted before
6755         */
6756        "beforeinsert" : true
6757    });
6758     this.listeners = this.attributes.listeners;
6759     Roo.data.Node.superclass.constructor.call(this);
6760 };
6761
6762 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6763     fireEvent : function(evtName){
6764         // first do standard event for this node
6765         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6766             return false;
6767         }
6768         // then bubble it up to the tree if the event wasn't cancelled
6769         var ot = this.getOwnerTree();
6770         if(ot){
6771             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6772                 return false;
6773             }
6774         }
6775         return true;
6776     },
6777
6778     /**
6779      * Returns true if this node is a leaf
6780      * @return {Boolean}
6781      */
6782     isLeaf : function(){
6783         return this.leaf === true;
6784     },
6785
6786     // private
6787     setFirstChild : function(node){
6788         this.firstChild = node;
6789     },
6790
6791     //private
6792     setLastChild : function(node){
6793         this.lastChild = node;
6794     },
6795
6796
6797     /**
6798      * Returns true if this node is the last child of its parent
6799      * @return {Boolean}
6800      */
6801     isLast : function(){
6802        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6803     },
6804
6805     /**
6806      * Returns true if this node is the first child of its parent
6807      * @return {Boolean}
6808      */
6809     isFirst : function(){
6810        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6811     },
6812
6813     hasChildNodes : function(){
6814         return !this.isLeaf() && this.childNodes.length > 0;
6815     },
6816
6817     /**
6818      * Insert node(s) as the last child node of this node.
6819      * @param {Node/Array} node The node or Array of nodes to append
6820      * @return {Node} The appended node if single append, or null if an array was passed
6821      */
6822     appendChild : function(node){
6823         var multi = false;
6824         if(node instanceof Array){
6825             multi = node;
6826         }else if(arguments.length > 1){
6827             multi = arguments;
6828         }
6829         // if passed an array or multiple args do them one by one
6830         if(multi){
6831             for(var i = 0, len = multi.length; i < len; i++) {
6832                 this.appendChild(multi[i]);
6833             }
6834         }else{
6835             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6836                 return false;
6837             }
6838             var index = this.childNodes.length;
6839             var oldParent = node.parentNode;
6840             // it's a move, make sure we move it cleanly
6841             if(oldParent){
6842                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6843                     return false;
6844                 }
6845                 oldParent.removeChild(node);
6846             }
6847             index = this.childNodes.length;
6848             if(index == 0){
6849                 this.setFirstChild(node);
6850             }
6851             this.childNodes.push(node);
6852             node.parentNode = this;
6853             var ps = this.childNodes[index-1];
6854             if(ps){
6855                 node.previousSibling = ps;
6856                 ps.nextSibling = node;
6857             }else{
6858                 node.previousSibling = null;
6859             }
6860             node.nextSibling = null;
6861             this.setLastChild(node);
6862             node.setOwnerTree(this.getOwnerTree());
6863             this.fireEvent("append", this.ownerTree, this, node, index);
6864             if(oldParent){
6865                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6866             }
6867             return node;
6868         }
6869     },
6870
6871     /**
6872      * Removes a child node from this node.
6873      * @param {Node} node The node to remove
6874      * @return {Node} The removed node
6875      */
6876     removeChild : function(node){
6877         var index = this.childNodes.indexOf(node);
6878         if(index == -1){
6879             return false;
6880         }
6881         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6882             return false;
6883         }
6884
6885         // remove it from childNodes collection
6886         this.childNodes.splice(index, 1);
6887
6888         // update siblings
6889         if(node.previousSibling){
6890             node.previousSibling.nextSibling = node.nextSibling;
6891         }
6892         if(node.nextSibling){
6893             node.nextSibling.previousSibling = node.previousSibling;
6894         }
6895
6896         // update child refs
6897         if(this.firstChild == node){
6898             this.setFirstChild(node.nextSibling);
6899         }
6900         if(this.lastChild == node){
6901             this.setLastChild(node.previousSibling);
6902         }
6903
6904         node.setOwnerTree(null);
6905         // clear any references from the node
6906         node.parentNode = null;
6907         node.previousSibling = null;
6908         node.nextSibling = null;
6909         this.fireEvent("remove", this.ownerTree, this, node);
6910         return node;
6911     },
6912
6913     /**
6914      * Inserts the first node before the second node in this nodes childNodes collection.
6915      * @param {Node} node The node to insert
6916      * @param {Node} refNode The node to insert before (if null the node is appended)
6917      * @return {Node} The inserted node
6918      */
6919     insertBefore : function(node, refNode){
6920         if(!refNode){ // like standard Dom, refNode can be null for append
6921             return this.appendChild(node);
6922         }
6923         // nothing to do
6924         if(node == refNode){
6925             return false;
6926         }
6927
6928         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6929             return false;
6930         }
6931         var index = this.childNodes.indexOf(refNode);
6932         var oldParent = node.parentNode;
6933         var refIndex = index;
6934
6935         // when moving internally, indexes will change after remove
6936         if(oldParent == this && this.childNodes.indexOf(node) < index){
6937             refIndex--;
6938         }
6939
6940         // it's a move, make sure we move it cleanly
6941         if(oldParent){
6942             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6943                 return false;
6944             }
6945             oldParent.removeChild(node);
6946         }
6947         if(refIndex == 0){
6948             this.setFirstChild(node);
6949         }
6950         this.childNodes.splice(refIndex, 0, node);
6951         node.parentNode = this;
6952         var ps = this.childNodes[refIndex-1];
6953         if(ps){
6954             node.previousSibling = ps;
6955             ps.nextSibling = node;
6956         }else{
6957             node.previousSibling = null;
6958         }
6959         node.nextSibling = refNode;
6960         refNode.previousSibling = node;
6961         node.setOwnerTree(this.getOwnerTree());
6962         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6963         if(oldParent){
6964             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6965         }
6966         return node;
6967     },
6968
6969     /**
6970      * Returns the child node at the specified index.
6971      * @param {Number} index
6972      * @return {Node}
6973      */
6974     item : function(index){
6975         return this.childNodes[index];
6976     },
6977
6978     /**
6979      * Replaces one child node in this node with another.
6980      * @param {Node} newChild The replacement node
6981      * @param {Node} oldChild The node to replace
6982      * @return {Node} The replaced node
6983      */
6984     replaceChild : function(newChild, oldChild){
6985         this.insertBefore(newChild, oldChild);
6986         this.removeChild(oldChild);
6987         return oldChild;
6988     },
6989
6990     /**
6991      * Returns the index of a child node
6992      * @param {Node} node
6993      * @return {Number} The index of the node or -1 if it was not found
6994      */
6995     indexOf : function(child){
6996         return this.childNodes.indexOf(child);
6997     },
6998
6999     /**
7000      * Returns the tree this node is in.
7001      * @return {Tree}
7002      */
7003     getOwnerTree : function(){
7004         // if it doesn't have one, look for one
7005         if(!this.ownerTree){
7006             var p = this;
7007             while(p){
7008                 if(p.ownerTree){
7009                     this.ownerTree = p.ownerTree;
7010                     break;
7011                 }
7012                 p = p.parentNode;
7013             }
7014         }
7015         return this.ownerTree;
7016     },
7017
7018     /**
7019      * Returns depth of this node (the root node has a depth of 0)
7020      * @return {Number}
7021      */
7022     getDepth : function(){
7023         var depth = 0;
7024         var p = this;
7025         while(p.parentNode){
7026             ++depth;
7027             p = p.parentNode;
7028         }
7029         return depth;
7030     },
7031
7032     // private
7033     setOwnerTree : function(tree){
7034         // if it's move, we need to update everyone
7035         if(tree != this.ownerTree){
7036             if(this.ownerTree){
7037                 this.ownerTree.unregisterNode(this);
7038             }
7039             this.ownerTree = tree;
7040             var cs = this.childNodes;
7041             for(var i = 0, len = cs.length; i < len; i++) {
7042                 cs[i].setOwnerTree(tree);
7043             }
7044             if(tree){
7045                 tree.registerNode(this);
7046             }
7047         }
7048     },
7049
7050     /**
7051      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7052      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7053      * @return {String} The path
7054      */
7055     getPath : function(attr){
7056         attr = attr || "id";
7057         var p = this.parentNode;
7058         var b = [this.attributes[attr]];
7059         while(p){
7060             b.unshift(p.attributes[attr]);
7061             p = p.parentNode;
7062         }
7063         var sep = this.getOwnerTree().pathSeparator;
7064         return sep + b.join(sep);
7065     },
7066
7067     /**
7068      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7069      * function call will be the scope provided or the current node. The arguments to the function
7070      * will be the args provided or the current node. If the function returns false at any point,
7071      * the bubble is stopped.
7072      * @param {Function} fn The function to call
7073      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7074      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7075      */
7076     bubble : function(fn, scope, args){
7077         var p = this;
7078         while(p){
7079             if(fn.call(scope || p, args || p) === false){
7080                 break;
7081             }
7082             p = p.parentNode;
7083         }
7084     },
7085
7086     /**
7087      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7088      * function call will be the scope provided or the current node. The arguments to the function
7089      * will be the args provided or the current node. If the function returns false at any point,
7090      * the cascade is stopped on that branch.
7091      * @param {Function} fn The function to call
7092      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7093      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7094      */
7095     cascade : function(fn, scope, args){
7096         if(fn.call(scope || this, args || this) !== false){
7097             var cs = this.childNodes;
7098             for(var i = 0, len = cs.length; i < len; i++) {
7099                 cs[i].cascade(fn, scope, args);
7100             }
7101         }
7102     },
7103
7104     /**
7105      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the iteration stops.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     eachChild : function(fn, scope, args){
7114         var cs = this.childNodes;
7115         for(var i = 0, len = cs.length; i < len; i++) {
7116                 if(fn.call(scope || this, args || cs[i]) === false){
7117                     break;
7118                 }
7119         }
7120     },
7121
7122     /**
7123      * Finds the first child that has the attribute with the specified value.
7124      * @param {String} attribute The attribute name
7125      * @param {Mixed} value The value to search for
7126      * @return {Node} The found child or null if none was found
7127      */
7128     findChild : function(attribute, value){
7129         var cs = this.childNodes;
7130         for(var i = 0, len = cs.length; i < len; i++) {
7131                 if(cs[i].attributes[attribute] == value){
7132                     return cs[i];
7133                 }
7134         }
7135         return null;
7136     },
7137
7138     /**
7139      * Finds the first child by a custom function. The child matches if the function passed
7140      * returns true.
7141      * @param {Function} fn
7142      * @param {Object} scope (optional)
7143      * @return {Node} The found child or null if none was found
7144      */
7145     findChildBy : function(fn, scope){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope||cs[i], cs[i]) === true){
7149                     return cs[i];
7150                 }
7151         }
7152         return null;
7153     },
7154
7155     /**
7156      * Sorts this nodes children using the supplied sort function
7157      * @param {Function} fn
7158      * @param {Object} scope (optional)
7159      */
7160     sort : function(fn, scope){
7161         var cs = this.childNodes;
7162         var len = cs.length;
7163         if(len > 0){
7164             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7165             cs.sort(sortFn);
7166             for(var i = 0; i < len; i++){
7167                 var n = cs[i];
7168                 n.previousSibling = cs[i-1];
7169                 n.nextSibling = cs[i+1];
7170                 if(i == 0){
7171                     this.setFirstChild(n);
7172                 }
7173                 if(i == len-1){
7174                     this.setLastChild(n);
7175                 }
7176             }
7177         }
7178     },
7179
7180     /**
7181      * Returns true if this node is an ancestor (at any point) of the passed node.
7182      * @param {Node} node
7183      * @return {Boolean}
7184      */
7185     contains : function(node){
7186         return node.isAncestor(this);
7187     },
7188
7189     /**
7190      * Returns true if the passed node is an ancestor (at any point) of this node.
7191      * @param {Node} node
7192      * @return {Boolean}
7193      */
7194     isAncestor : function(node){
7195         var p = this.parentNode;
7196         while(p){
7197             if(p == node){
7198                 return true;
7199             }
7200             p = p.parentNode;
7201         }
7202         return false;
7203     },
7204
7205     toString : function(){
7206         return "[Node"+(this.id?" "+this.id:"")+"]";
7207     }
7208 });/*
7209  * Based on:
7210  * Ext JS Library 1.1.1
7211  * Copyright(c) 2006-2007, Ext JS, LLC.
7212  *
7213  * Originally Released Under LGPL - original licence link has changed is not relivant.
7214  *
7215  * Fork - LGPL
7216  * <script type="text/javascript">
7217  */
7218  
7219
7220 /**
7221  * @class Roo.ComponentMgr
7222  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7223  * @singleton
7224  */
7225 Roo.ComponentMgr = function(){
7226     var all = new Roo.util.MixedCollection();
7227
7228     return {
7229         /**
7230          * Registers a component.
7231          * @param {Roo.Component} c The component
7232          */
7233         register : function(c){
7234             all.add(c);
7235         },
7236
7237         /**
7238          * Unregisters a component.
7239          * @param {Roo.Component} c The component
7240          */
7241         unregister : function(c){
7242             all.remove(c);
7243         },
7244
7245         /**
7246          * Returns a component by id
7247          * @param {String} id The component id
7248          */
7249         get : function(id){
7250             return all.get(id);
7251         },
7252
7253         /**
7254          * Registers a function that will be called when a specified component is added to ComponentMgr
7255          * @param {String} id The component id
7256          * @param {Funtction} fn The callback function
7257          * @param {Object} scope The scope of the callback
7258          */
7259         onAvailable : function(id, fn, scope){
7260             all.on("add", function(index, o){
7261                 if(o.id == id){
7262                     fn.call(scope || o, o);
7263                     all.un("add", fn, scope);
7264                 }
7265             });
7266         }
7267     };
7268 }();/*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279 /**
7280  * @class Roo.Component
7281  * @extends Roo.util.Observable
7282  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7283  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7284  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7285  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7286  * All visual components (widgets) that require rendering into a layout should subclass Component.
7287  * @constructor
7288  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7289  * 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
7290  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7291  */
7292 Roo.Component = function(config){
7293     config = config || {};
7294     if(config.tagName || config.dom || typeof config == "string"){ // element object
7295         config = {el: config, id: config.id || config};
7296     }
7297     this.initialConfig = config;
7298
7299     Roo.apply(this, config);
7300     this.addEvents({
7301         /**
7302          * @event disable
7303          * Fires after the component is disabled.
7304              * @param {Roo.Component} this
7305              */
7306         disable : true,
7307         /**
7308          * @event enable
7309          * Fires after the component is enabled.
7310              * @param {Roo.Component} this
7311              */
7312         enable : true,
7313         /**
7314          * @event beforeshow
7315          * Fires before the component is shown.  Return false to stop the show.
7316              * @param {Roo.Component} this
7317              */
7318         beforeshow : true,
7319         /**
7320          * @event show
7321          * Fires after the component is shown.
7322              * @param {Roo.Component} this
7323              */
7324         show : true,
7325         /**
7326          * @event beforehide
7327          * Fires before the component is hidden. Return false to stop the hide.
7328              * @param {Roo.Component} this
7329              */
7330         beforehide : true,
7331         /**
7332          * @event hide
7333          * Fires after the component is hidden.
7334              * @param {Roo.Component} this
7335              */
7336         hide : true,
7337         /**
7338          * @event beforerender
7339          * Fires before the component is rendered. Return false to stop the render.
7340              * @param {Roo.Component} this
7341              */
7342         beforerender : true,
7343         /**
7344          * @event render
7345          * Fires after the component is rendered.
7346              * @param {Roo.Component} this
7347              */
7348         render : true,
7349         /**
7350          * @event beforedestroy
7351          * Fires before the component is destroyed. Return false to stop the destroy.
7352              * @param {Roo.Component} this
7353              */
7354         beforedestroy : true,
7355         /**
7356          * @event destroy
7357          * Fires after the component is destroyed.
7358              * @param {Roo.Component} this
7359              */
7360         destroy : true
7361     });
7362     if(!this.id){
7363         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7364     }
7365     Roo.ComponentMgr.register(this);
7366     Roo.Component.superclass.constructor.call(this);
7367     this.initComponent();
7368     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7369         this.render(this.renderTo);
7370         delete this.renderTo;
7371     }
7372 };
7373
7374 // private
7375 Roo.Component.AUTO_ID = 1000;
7376
7377 Roo.extend(Roo.Component, Roo.util.Observable, {
7378     /**
7379      * @property {Boolean} hidden
7380      * true if this component is hidden. Read-only.
7381      */
7382     hidden : false,
7383     /**
7384      * true if this component is disabled. Read-only.
7385      */
7386     disabled : false,
7387     /**
7388      * true if this component has been rendered. Read-only.
7389      */
7390     rendered : false,
7391     
7392     /** @cfg {String} disableClass
7393      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7394      */
7395     disabledClass : "x-item-disabled",
7396         /** @cfg {Boolean} allowDomMove
7397          * Whether the component can move the Dom node when rendering (defaults to true).
7398          */
7399     allowDomMove : true,
7400     /** @cfg {String} hideMode
7401      * How this component should hidden. Supported values are
7402      * "visibility" (css visibility), "offsets" (negative offset position) and
7403      * "display" (css display) - defaults to "display".
7404      */
7405     hideMode: 'display',
7406
7407     // private
7408     ctype : "Roo.Component",
7409
7410     /** @cfg {String} actionMode 
7411      * which property holds the element that used for  hide() / show() / disable() / enable()
7412      * default is 'el' 
7413      */
7414     actionMode : "el",
7415
7416     // private
7417     getActionEl : function(){
7418         return this[this.actionMode];
7419     },
7420
7421     initComponent : Roo.emptyFn,
7422     /**
7423      * If this is a lazy rendering component, render it to its container element.
7424      * @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.
7425      */
7426     render : function(container, position){
7427         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7428             if(!container && this.el){
7429                 this.el = Roo.get(this.el);
7430                 container = this.el.dom.parentNode;
7431                 this.allowDomMove = false;
7432             }
7433             this.container = Roo.get(container);
7434             this.rendered = true;
7435             if(position !== undefined){
7436                 if(typeof position == 'number'){
7437                     position = this.container.dom.childNodes[position];
7438                 }else{
7439                     position = Roo.getDom(position);
7440                 }
7441             }
7442             this.onRender(this.container, position || null);
7443             if(this.cls){
7444                 this.el.addClass(this.cls);
7445                 delete this.cls;
7446             }
7447             if(this.style){
7448                 this.el.applyStyles(this.style);
7449                 delete this.style;
7450             }
7451             this.fireEvent("render", this);
7452             this.afterRender(this.container);
7453             if(this.hidden){
7454                 this.hide();
7455             }
7456             if(this.disabled){
7457                 this.disable();
7458             }
7459         }
7460         return this;
7461     },
7462
7463     // private
7464     // default function is not really useful
7465     onRender : function(ct, position){
7466         if(this.el){
7467             this.el = Roo.get(this.el);
7468             if(this.allowDomMove !== false){
7469                 ct.dom.insertBefore(this.el.dom, position);
7470             }
7471         }
7472     },
7473
7474     // private
7475     getAutoCreate : function(){
7476         var cfg = typeof this.autoCreate == "object" ?
7477                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7478         if(this.id && !cfg.id){
7479             cfg.id = this.id;
7480         }
7481         return cfg;
7482     },
7483
7484     // private
7485     afterRender : Roo.emptyFn,
7486
7487     /**
7488      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7489      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7490      */
7491     destroy : function(){
7492         if(this.fireEvent("beforedestroy", this) !== false){
7493             this.purgeListeners();
7494             this.beforeDestroy();
7495             if(this.rendered){
7496                 this.el.removeAllListeners();
7497                 this.el.remove();
7498                 if(this.actionMode == "container"){
7499                     this.container.remove();
7500                 }
7501             }
7502             this.onDestroy();
7503             Roo.ComponentMgr.unregister(this);
7504             this.fireEvent("destroy", this);
7505         }
7506     },
7507
7508         // private
7509     beforeDestroy : function(){
7510
7511     },
7512
7513         // private
7514         onDestroy : function(){
7515
7516     },
7517
7518     /**
7519      * Returns the underlying {@link Roo.Element}.
7520      * @return {Roo.Element} The element
7521      */
7522     getEl : function(){
7523         return this.el;
7524     },
7525
7526     /**
7527      * Returns the id of this component.
7528      * @return {String}
7529      */
7530     getId : function(){
7531         return this.id;
7532     },
7533
7534     /**
7535      * Try to focus this component.
7536      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7537      * @return {Roo.Component} this
7538      */
7539     focus : function(selectText){
7540         if(this.rendered){
7541             this.el.focus();
7542             if(selectText === true){
7543                 this.el.dom.select();
7544             }
7545         }
7546         return this;
7547     },
7548
7549     // private
7550     blur : function(){
7551         if(this.rendered){
7552             this.el.blur();
7553         }
7554         return this;
7555     },
7556
7557     /**
7558      * Disable this component.
7559      * @return {Roo.Component} this
7560      */
7561     disable : function(){
7562         if(this.rendered){
7563             this.onDisable();
7564         }
7565         this.disabled = true;
7566         this.fireEvent("disable", this);
7567         return this;
7568     },
7569
7570         // private
7571     onDisable : function(){
7572         this.getActionEl().addClass(this.disabledClass);
7573         this.el.dom.disabled = true;
7574     },
7575
7576     /**
7577      * Enable this component.
7578      * @return {Roo.Component} this
7579      */
7580     enable : function(){
7581         if(this.rendered){
7582             this.onEnable();
7583         }
7584         this.disabled = false;
7585         this.fireEvent("enable", this);
7586         return this;
7587     },
7588
7589         // private
7590     onEnable : function(){
7591         this.getActionEl().removeClass(this.disabledClass);
7592         this.el.dom.disabled = false;
7593     },
7594
7595     /**
7596      * Convenience function for setting disabled/enabled by boolean.
7597      * @param {Boolean} disabled
7598      */
7599     setDisabled : function(disabled){
7600         this[disabled ? "disable" : "enable"]();
7601     },
7602
7603     /**
7604      * Show this component.
7605      * @return {Roo.Component} this
7606      */
7607     show: function(){
7608         if(this.fireEvent("beforeshow", this) !== false){
7609             this.hidden = false;
7610             if(this.rendered){
7611                 this.onShow();
7612             }
7613             this.fireEvent("show", this);
7614         }
7615         return this;
7616     },
7617
7618     // private
7619     onShow : function(){
7620         var ae = this.getActionEl();
7621         if(this.hideMode == 'visibility'){
7622             ae.dom.style.visibility = "visible";
7623         }else if(this.hideMode == 'offsets'){
7624             ae.removeClass('x-hidden');
7625         }else{
7626             ae.dom.style.display = "";
7627         }
7628     },
7629
7630     /**
7631      * Hide this component.
7632      * @return {Roo.Component} this
7633      */
7634     hide: function(){
7635         if(this.fireEvent("beforehide", this) !== false){
7636             this.hidden = true;
7637             if(this.rendered){
7638                 this.onHide();
7639             }
7640             this.fireEvent("hide", this);
7641         }
7642         return this;
7643     },
7644
7645     // private
7646     onHide : function(){
7647         var ae = this.getActionEl();
7648         if(this.hideMode == 'visibility'){
7649             ae.dom.style.visibility = "hidden";
7650         }else if(this.hideMode == 'offsets'){
7651             ae.addClass('x-hidden');
7652         }else{
7653             ae.dom.style.display = "none";
7654         }
7655     },
7656
7657     /**
7658      * Convenience function to hide or show this component by boolean.
7659      * @param {Boolean} visible True to show, false to hide
7660      * @return {Roo.Component} this
7661      */
7662     setVisible: function(visible){
7663         if(visible) {
7664             this.show();
7665         }else{
7666             this.hide();
7667         }
7668         return this;
7669     },
7670
7671     /**
7672      * Returns true if this component is visible.
7673      */
7674     isVisible : function(){
7675         return this.getActionEl().isVisible();
7676     },
7677
7678     cloneConfig : function(overrides){
7679         overrides = overrides || {};
7680         var id = overrides.id || Roo.id();
7681         var cfg = Roo.applyIf(overrides, this.initialConfig);
7682         cfg.id = id; // prevent dup id
7683         return new this.constructor(cfg);
7684     }
7685 });/*
7686  * Based on:
7687  * Ext JS Library 1.1.1
7688  * Copyright(c) 2006-2007, Ext JS, LLC.
7689  *
7690  * Originally Released Under LGPL - original licence link has changed is not relivant.
7691  *
7692  * Fork - LGPL
7693  * <script type="text/javascript">
7694  */
7695  (function(){ 
7696 /**
7697  * @class Roo.Layer
7698  * @extends Roo.Element
7699  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7700  * automatic maintaining of shadow/shim positions.
7701  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7702  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7703  * you can pass a string with a CSS class name. False turns off the shadow.
7704  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7705  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7706  * @cfg {String} cls CSS class to add to the element
7707  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7708  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7709  * @constructor
7710  * @param {Object} config An object with config options.
7711  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7712  */
7713
7714 Roo.Layer = function(config, existingEl){
7715     config = config || {};
7716     var dh = Roo.DomHelper;
7717     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7718     if(existingEl){
7719         this.dom = Roo.getDom(existingEl);
7720     }
7721     if(!this.dom){
7722         var o = config.dh || {tag: "div", cls: "x-layer"};
7723         this.dom = dh.append(pel, o);
7724     }
7725     if(config.cls){
7726         this.addClass(config.cls);
7727     }
7728     this.constrain = config.constrain !== false;
7729     this.visibilityMode = Roo.Element.VISIBILITY;
7730     if(config.id){
7731         this.id = this.dom.id = config.id;
7732     }else{
7733         this.id = Roo.id(this.dom);
7734     }
7735     this.zindex = config.zindex || this.getZIndex();
7736     this.position("absolute", this.zindex);
7737     if(config.shadow){
7738         this.shadowOffset = config.shadowOffset || 4;
7739         this.shadow = new Roo.Shadow({
7740             offset : this.shadowOffset,
7741             mode : config.shadow
7742         });
7743     }else{
7744         this.shadowOffset = 0;
7745     }
7746     this.useShim = config.shim !== false && Roo.useShims;
7747     this.useDisplay = config.useDisplay;
7748     this.hide();
7749 };
7750
7751 var supr = Roo.Element.prototype;
7752
7753 // shims are shared among layer to keep from having 100 iframes
7754 var shims = [];
7755
7756 Roo.extend(Roo.Layer, Roo.Element, {
7757
7758     getZIndex : function(){
7759         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7760     },
7761
7762     getShim : function(){
7763         if(!this.useShim){
7764             return null;
7765         }
7766         if(this.shim){
7767             return this.shim;
7768         }
7769         var shim = shims.shift();
7770         if(!shim){
7771             shim = this.createShim();
7772             shim.enableDisplayMode('block');
7773             shim.dom.style.display = 'none';
7774             shim.dom.style.visibility = 'visible';
7775         }
7776         var pn = this.dom.parentNode;
7777         if(shim.dom.parentNode != pn){
7778             pn.insertBefore(shim.dom, this.dom);
7779         }
7780         shim.setStyle('z-index', this.getZIndex()-2);
7781         this.shim = shim;
7782         return shim;
7783     },
7784
7785     hideShim : function(){
7786         if(this.shim){
7787             this.shim.setDisplayed(false);
7788             shims.push(this.shim);
7789             delete this.shim;
7790         }
7791     },
7792
7793     disableShadow : function(){
7794         if(this.shadow){
7795             this.shadowDisabled = true;
7796             this.shadow.hide();
7797             this.lastShadowOffset = this.shadowOffset;
7798             this.shadowOffset = 0;
7799         }
7800     },
7801
7802     enableShadow : function(show){
7803         if(this.shadow){
7804             this.shadowDisabled = false;
7805             this.shadowOffset = this.lastShadowOffset;
7806             delete this.lastShadowOffset;
7807             if(show){
7808                 this.sync(true);
7809             }
7810         }
7811     },
7812
7813     // private
7814     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7815     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7816     sync : function(doShow){
7817         var sw = this.shadow;
7818         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7819             var sh = this.getShim();
7820
7821             var w = this.getWidth(),
7822                 h = this.getHeight();
7823
7824             var l = this.getLeft(true),
7825                 t = this.getTop(true);
7826
7827             if(sw && !this.shadowDisabled){
7828                 if(doShow && !sw.isVisible()){
7829                     sw.show(this);
7830                 }else{
7831                     sw.realign(l, t, w, h);
7832                 }
7833                 if(sh){
7834                     if(doShow){
7835                        sh.show();
7836                     }
7837                     // fit the shim behind the shadow, so it is shimmed too
7838                     var a = sw.adjusts, s = sh.dom.style;
7839                     s.left = (Math.min(l, l+a.l))+"px";
7840                     s.top = (Math.min(t, t+a.t))+"px";
7841                     s.width = (w+a.w)+"px";
7842                     s.height = (h+a.h)+"px";
7843                 }
7844             }else if(sh){
7845                 if(doShow){
7846                    sh.show();
7847                 }
7848                 sh.setSize(w, h);
7849                 sh.setLeftTop(l, t);
7850             }
7851             
7852         }
7853     },
7854
7855     // private
7856     destroy : function(){
7857         this.hideShim();
7858         if(this.shadow){
7859             this.shadow.hide();
7860         }
7861         this.removeAllListeners();
7862         var pn = this.dom.parentNode;
7863         if(pn){
7864             pn.removeChild(this.dom);
7865         }
7866         Roo.Element.uncache(this.id);
7867     },
7868
7869     remove : function(){
7870         this.destroy();
7871     },
7872
7873     // private
7874     beginUpdate : function(){
7875         this.updating = true;
7876     },
7877
7878     // private
7879     endUpdate : function(){
7880         this.updating = false;
7881         this.sync(true);
7882     },
7883
7884     // private
7885     hideUnders : function(negOffset){
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.hideShim();
7890     },
7891
7892     // private
7893     constrainXY : function(){
7894         if(this.constrain){
7895             var vw = Roo.lib.Dom.getViewWidth(),
7896                 vh = Roo.lib.Dom.getViewHeight();
7897             var s = Roo.get(document).getScroll();
7898
7899             var xy = this.getXY();
7900             var x = xy[0], y = xy[1];   
7901             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7902             // only move it if it needs it
7903             var moved = false;
7904             // first validate right/bottom
7905             if((x + w) > vw+s.left){
7906                 x = vw - w - this.shadowOffset;
7907                 moved = true;
7908             }
7909             if((y + h) > vh+s.top){
7910                 y = vh - h - this.shadowOffset;
7911                 moved = true;
7912             }
7913             // then make sure top/left isn't negative
7914             if(x < s.left){
7915                 x = s.left;
7916                 moved = true;
7917             }
7918             if(y < s.top){
7919                 y = s.top;
7920                 moved = true;
7921             }
7922             if(moved){
7923                 if(this.avoidY){
7924                     var ay = this.avoidY;
7925                     if(y <= ay && (y+h) >= ay){
7926                         y = ay-h-5;   
7927                     }
7928                 }
7929                 xy = [x, y];
7930                 this.storeXY(xy);
7931                 supr.setXY.call(this, xy);
7932                 this.sync();
7933             }
7934         }
7935     },
7936
7937     isVisible : function(){
7938         return this.visible;    
7939     },
7940
7941     // private
7942     showAction : function(){
7943         this.visible = true; // track visibility to prevent getStyle calls
7944         if(this.useDisplay === true){
7945             this.setDisplayed("");
7946         }else if(this.lastXY){
7947             supr.setXY.call(this, this.lastXY);
7948         }else if(this.lastLT){
7949             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7950         }
7951     },
7952
7953     // private
7954     hideAction : function(){
7955         this.visible = false;
7956         if(this.useDisplay === true){
7957             this.setDisplayed(false);
7958         }else{
7959             this.setLeftTop(-10000,-10000);
7960         }
7961     },
7962
7963     // overridden Element method
7964     setVisible : function(v, a, d, c, e){
7965         if(v){
7966             this.showAction();
7967         }
7968         if(a && v){
7969             var cb = function(){
7970                 this.sync(true);
7971                 if(c){
7972                     c();
7973                 }
7974             }.createDelegate(this);
7975             supr.setVisible.call(this, true, true, d, cb, e);
7976         }else{
7977             if(!v){
7978                 this.hideUnders(true);
7979             }
7980             var cb = c;
7981             if(a){
7982                 cb = function(){
7983                     this.hideAction();
7984                     if(c){
7985                         c();
7986                     }
7987                 }.createDelegate(this);
7988             }
7989             supr.setVisible.call(this, v, a, d, cb, e);
7990             if(v){
7991                 this.sync(true);
7992             }else if(!a){
7993                 this.hideAction();
7994             }
7995         }
7996     },
7997
7998     storeXY : function(xy){
7999         delete this.lastLT;
8000         this.lastXY = xy;
8001     },
8002
8003     storeLeftTop : function(left, top){
8004         delete this.lastXY;
8005         this.lastLT = [left, top];
8006     },
8007
8008     // private
8009     beforeFx : function(){
8010         this.beforeAction();
8011         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8012     },
8013
8014     // private
8015     afterFx : function(){
8016         Roo.Layer.superclass.afterFx.apply(this, arguments);
8017         this.sync(this.isVisible());
8018     },
8019
8020     // private
8021     beforeAction : function(){
8022         if(!this.updating && this.shadow){
8023             this.shadow.hide();
8024         }
8025     },
8026
8027     // overridden Element method
8028     setLeft : function(left){
8029         this.storeLeftTop(left, this.getTop(true));
8030         supr.setLeft.apply(this, arguments);
8031         this.sync();
8032     },
8033
8034     setTop : function(top){
8035         this.storeLeftTop(this.getLeft(true), top);
8036         supr.setTop.apply(this, arguments);
8037         this.sync();
8038     },
8039
8040     setLeftTop : function(left, top){
8041         this.storeLeftTop(left, top);
8042         supr.setLeftTop.apply(this, arguments);
8043         this.sync();
8044     },
8045
8046     setXY : function(xy, a, d, c, e){
8047         this.fixDisplay();
8048         this.beforeAction();
8049         this.storeXY(xy);
8050         var cb = this.createCB(c);
8051         supr.setXY.call(this, xy, a, d, cb, e);
8052         if(!a){
8053             cb();
8054         }
8055     },
8056
8057     // private
8058     createCB : function(c){
8059         var el = this;
8060         return function(){
8061             el.constrainXY();
8062             el.sync(true);
8063             if(c){
8064                 c();
8065             }
8066         };
8067     },
8068
8069     // overridden Element method
8070     setX : function(x, a, d, c, e){
8071         this.setXY([x, this.getY()], a, d, c, e);
8072     },
8073
8074     // overridden Element method
8075     setY : function(y, a, d, c, e){
8076         this.setXY([this.getX(), y], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setSize : function(w, h, a, d, c, e){
8081         this.beforeAction();
8082         var cb = this.createCB(c);
8083         supr.setSize.call(this, w, h, a, d, cb, e);
8084         if(!a){
8085             cb();
8086         }
8087     },
8088
8089     // overridden Element method
8090     setWidth : function(w, a, d, c, e){
8091         this.beforeAction();
8092         var cb = this.createCB(c);
8093         supr.setWidth.call(this, w, a, d, cb, e);
8094         if(!a){
8095             cb();
8096         }
8097     },
8098
8099     // overridden Element method
8100     setHeight : function(h, a, d, c, e){
8101         this.beforeAction();
8102         var cb = this.createCB(c);
8103         supr.setHeight.call(this, h, a, d, cb, e);
8104         if(!a){
8105             cb();
8106         }
8107     },
8108
8109     // overridden Element method
8110     setBounds : function(x, y, w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         if(!a){
8114             this.storeXY([x, y]);
8115             supr.setXY.call(this, [x, y]);
8116             supr.setSize.call(this, w, h, a, d, cb, e);
8117             cb();
8118         }else{
8119             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8120         }
8121         return this;
8122     },
8123     
8124     /**
8125      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8126      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8127      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8128      * @param {Number} zindex The new z-index to set
8129      * @return {this} The Layer
8130      */
8131     setZIndex : function(zindex){
8132         this.zindex = zindex;
8133         this.setStyle("z-index", zindex + 2);
8134         if(this.shadow){
8135             this.shadow.setZIndex(zindex + 1);
8136         }
8137         if(this.shim){
8138             this.shim.setStyle("z-index", zindex);
8139         }
8140     }
8141 });
8142 })();/*
8143  * Based on:
8144  * Ext JS Library 1.1.1
8145  * Copyright(c) 2006-2007, Ext JS, LLC.
8146  *
8147  * Originally Released Under LGPL - original licence link has changed is not relivant.
8148  *
8149  * Fork - LGPL
8150  * <script type="text/javascript">
8151  */
8152
8153
8154 /**
8155  * @class Roo.Shadow
8156  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8157  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8158  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8159  * @constructor
8160  * Create a new Shadow
8161  * @param {Object} config The config object
8162  */
8163 Roo.Shadow = function(config){
8164     Roo.apply(this, config);
8165     if(typeof this.mode != "string"){
8166         this.mode = this.defaultMode;
8167     }
8168     var o = this.offset, a = {h: 0};
8169     var rad = Math.floor(this.offset/2);
8170     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8171         case "drop":
8172             a.w = 0;
8173             a.l = a.t = o;
8174             a.t -= 1;
8175             if(Roo.isIE){
8176                 a.l -= this.offset + rad;
8177                 a.t -= this.offset + rad;
8178                 a.w -= rad;
8179                 a.h -= rad;
8180                 a.t += 1;
8181             }
8182         break;
8183         case "sides":
8184             a.w = (o*2);
8185             a.l = -o;
8186             a.t = o-1;
8187             if(Roo.isIE){
8188                 a.l -= (this.offset - rad);
8189                 a.t -= this.offset + rad;
8190                 a.l += 1;
8191                 a.w -= (this.offset - rad)*2;
8192                 a.w -= rad + 1;
8193                 a.h -= 1;
8194             }
8195         break;
8196         case "frame":
8197             a.w = a.h = (o*2);
8198             a.l = a.t = -o;
8199             a.t += 1;
8200             a.h -= 2;
8201             if(Roo.isIE){
8202                 a.l -= (this.offset - rad);
8203                 a.t -= (this.offset - rad);
8204                 a.l += 1;
8205                 a.w -= (this.offset + rad + 1);
8206                 a.h -= (this.offset + rad);
8207                 a.h += 1;
8208             }
8209         break;
8210     };
8211
8212     this.adjusts = a;
8213 };
8214
8215 Roo.Shadow.prototype = {
8216     /**
8217      * @cfg {String} mode
8218      * The shadow display mode.  Supports the following options:<br />
8219      * sides: Shadow displays on both sides and bottom only<br />
8220      * frame: Shadow displays equally on all four sides<br />
8221      * drop: Traditional bottom-right drop shadow (default)
8222      */
8223     /**
8224      * @cfg {String} offset
8225      * The number of pixels to offset the shadow from the element (defaults to 4)
8226      */
8227     offset: 4,
8228
8229     // private
8230     defaultMode: "drop",
8231
8232     /**
8233      * Displays the shadow under the target element
8234      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8235      */
8236     show : function(target){
8237         target = Roo.get(target);
8238         if(!this.el){
8239             this.el = Roo.Shadow.Pool.pull();
8240             if(this.el.dom.nextSibling != target.dom){
8241                 this.el.insertBefore(target);
8242             }
8243         }
8244         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8245         if(Roo.isIE){
8246             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8247         }
8248         this.realign(
8249             target.getLeft(true),
8250             target.getTop(true),
8251             target.getWidth(),
8252             target.getHeight()
8253         );
8254         this.el.dom.style.display = "block";
8255     },
8256
8257     /**
8258      * Returns true if the shadow is visible, else false
8259      */
8260     isVisible : function(){
8261         return this.el ? true : false;  
8262     },
8263
8264     /**
8265      * Direct alignment when values are already available. Show must be called at least once before
8266      * calling this method to ensure it is initialized.
8267      * @param {Number} left The target element left position
8268      * @param {Number} top The target element top position
8269      * @param {Number} width The target element width
8270      * @param {Number} height The target element height
8271      */
8272     realign : function(l, t, w, h){
8273         if(!this.el){
8274             return;
8275         }
8276         var a = this.adjusts, d = this.el.dom, s = d.style;
8277         var iea = 0;
8278         s.left = (l+a.l)+"px";
8279         s.top = (t+a.t)+"px";
8280         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8281  
8282         if(s.width != sws || s.height != shs){
8283             s.width = sws;
8284             s.height = shs;
8285             if(!Roo.isIE){
8286                 var cn = d.childNodes;
8287                 var sww = Math.max(0, (sw-12))+"px";
8288                 cn[0].childNodes[1].style.width = sww;
8289                 cn[1].childNodes[1].style.width = sww;
8290                 cn[2].childNodes[1].style.width = sww;
8291                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8292             }
8293         }
8294     },
8295
8296     /**
8297      * Hides this shadow
8298      */
8299     hide : function(){
8300         if(this.el){
8301             this.el.dom.style.display = "none";
8302             Roo.Shadow.Pool.push(this.el);
8303             delete this.el;
8304         }
8305     },
8306
8307     /**
8308      * Adjust the z-index of this shadow
8309      * @param {Number} zindex The new z-index
8310      */
8311     setZIndex : function(z){
8312         this.zIndex = z;
8313         if(this.el){
8314             this.el.setStyle("z-index", z);
8315         }
8316     }
8317 };
8318
8319 // Private utility class that manages the internal Shadow cache
8320 Roo.Shadow.Pool = function(){
8321     var p = [];
8322     var markup = Roo.isIE ?
8323                  '<div class="x-ie-shadow"></div>' :
8324                  '<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>';
8325     return {
8326         pull : function(){
8327             var sh = p.shift();
8328             if(!sh){
8329                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8330                 sh.autoBoxAdjust = false;
8331             }
8332             return sh;
8333         },
8334
8335         push : function(sh){
8336             p.push(sh);
8337         }
8338     };
8339 }();/*
8340  * Based on:
8341  * Ext JS Library 1.1.1
8342  * Copyright(c) 2006-2007, Ext JS, LLC.
8343  *
8344  * Originally Released Under LGPL - original licence link has changed is not relivant.
8345  *
8346  * Fork - LGPL
8347  * <script type="text/javascript">
8348  */
8349
8350 /**
8351  * @class Roo.BoxComponent
8352  * @extends Roo.Component
8353  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8354  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8355  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8356  * layout containers.
8357  * @constructor
8358  * @param {Roo.Element/String/Object} config The configuration options.
8359  */
8360 Roo.BoxComponent = function(config){
8361     Roo.Component.call(this, config);
8362     this.addEvents({
8363         /**
8364          * @event resize
8365          * Fires after the component is resized.
8366              * @param {Roo.Component} this
8367              * @param {Number} adjWidth The box-adjusted width that was set
8368              * @param {Number} adjHeight The box-adjusted height that was set
8369              * @param {Number} rawWidth The width that was originally specified
8370              * @param {Number} rawHeight The height that was originally specified
8371              */
8372         resize : true,
8373         /**
8374          * @event move
8375          * Fires after the component is moved.
8376              * @param {Roo.Component} this
8377              * @param {Number} x The new x position
8378              * @param {Number} y The new y position
8379              */
8380         move : true
8381     });
8382 };
8383
8384 Roo.extend(Roo.BoxComponent, Roo.Component, {
8385     // private, set in afterRender to signify that the component has been rendered
8386     boxReady : false,
8387     // private, used to defer height settings to subclasses
8388     deferHeight: false,
8389     /** @cfg {Number} width
8390      * width (optional) size of component
8391      */
8392      /** @cfg {Number} height
8393      * height (optional) size of component
8394      */
8395      
8396     /**
8397      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8398      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8399      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8400      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8401      * @return {Roo.BoxComponent} this
8402      */
8403     setSize : function(w, h){
8404         // support for standard size objects
8405         if(typeof w == 'object'){
8406             h = w.height;
8407             w = w.width;
8408         }
8409         // not rendered
8410         if(!this.boxReady){
8411             this.width = w;
8412             this.height = h;
8413             return this;
8414         }
8415
8416         // prevent recalcs when not needed
8417         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8418             return this;
8419         }
8420         this.lastSize = {width: w, height: h};
8421
8422         var adj = this.adjustSize(w, h);
8423         var aw = adj.width, ah = adj.height;
8424         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8425             var rz = this.getResizeEl();
8426             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8427                 rz.setSize(aw, ah);
8428             }else if(!this.deferHeight && ah !== undefined){
8429                 rz.setHeight(ah);
8430             }else if(aw !== undefined){
8431                 rz.setWidth(aw);
8432             }
8433             this.onResize(aw, ah, w, h);
8434             this.fireEvent('resize', this, aw, ah, w, h);
8435         }
8436         return this;
8437     },
8438
8439     /**
8440      * Gets the current size of the component's underlying element.
8441      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8442      */
8443     getSize : function(){
8444         return this.el.getSize();
8445     },
8446
8447     /**
8448      * Gets the current XY position of the component's underlying element.
8449      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8450      * @return {Array} The XY position of the element (e.g., [100, 200])
8451      */
8452     getPosition : function(local){
8453         if(local === true){
8454             return [this.el.getLeft(true), this.el.getTop(true)];
8455         }
8456         return this.xy || this.el.getXY();
8457     },
8458
8459     /**
8460      * Gets the current box measurements of the component's underlying element.
8461      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8462      * @returns {Object} box An object in the format {x, y, width, height}
8463      */
8464     getBox : function(local){
8465         var s = this.el.getSize();
8466         if(local){
8467             s.x = this.el.getLeft(true);
8468             s.y = this.el.getTop(true);
8469         }else{
8470             var xy = this.xy || this.el.getXY();
8471             s.x = xy[0];
8472             s.y = xy[1];
8473         }
8474         return s;
8475     },
8476
8477     /**
8478      * Sets the current box measurements of the component's underlying element.
8479      * @param {Object} box An object in the format {x, y, width, height}
8480      * @returns {Roo.BoxComponent} this
8481      */
8482     updateBox : function(box){
8483         this.setSize(box.width, box.height);
8484         this.setPagePosition(box.x, box.y);
8485         return this;
8486     },
8487
8488     // protected
8489     getResizeEl : function(){
8490         return this.resizeEl || this.el;
8491     },
8492
8493     // protected
8494     getPositionEl : function(){
8495         return this.positionEl || this.el;
8496     },
8497
8498     /**
8499      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8500      * This method fires the move event.
8501      * @param {Number} left The new left
8502      * @param {Number} top The new top
8503      * @returns {Roo.BoxComponent} this
8504      */
8505     setPosition : function(x, y){
8506         this.x = x;
8507         this.y = y;
8508         if(!this.boxReady){
8509             return this;
8510         }
8511         var adj = this.adjustPosition(x, y);
8512         var ax = adj.x, ay = adj.y;
8513
8514         var el = this.getPositionEl();
8515         if(ax !== undefined || ay !== undefined){
8516             if(ax !== undefined && ay !== undefined){
8517                 el.setLeftTop(ax, ay);
8518             }else if(ax !== undefined){
8519                 el.setLeft(ax);
8520             }else if(ay !== undefined){
8521                 el.setTop(ay);
8522             }
8523             this.onPosition(ax, ay);
8524             this.fireEvent('move', this, ax, ay);
8525         }
8526         return this;
8527     },
8528
8529     /**
8530      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8531      * This method fires the move event.
8532      * @param {Number} x The new x position
8533      * @param {Number} y The new y position
8534      * @returns {Roo.BoxComponent} this
8535      */
8536     setPagePosition : function(x, y){
8537         this.pageX = x;
8538         this.pageY = y;
8539         if(!this.boxReady){
8540             return;
8541         }
8542         if(x === undefined || y === undefined){ // cannot translate undefined points
8543             return;
8544         }
8545         var p = this.el.translatePoints(x, y);
8546         this.setPosition(p.left, p.top);
8547         return this;
8548     },
8549
8550     // private
8551     onRender : function(ct, position){
8552         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8553         if(this.resizeEl){
8554             this.resizeEl = Roo.get(this.resizeEl);
8555         }
8556         if(this.positionEl){
8557             this.positionEl = Roo.get(this.positionEl);
8558         }
8559     },
8560
8561     // private
8562     afterRender : function(){
8563         Roo.BoxComponent.superclass.afterRender.call(this);
8564         this.boxReady = true;
8565         this.setSize(this.width, this.height);
8566         if(this.x || this.y){
8567             this.setPosition(this.x, this.y);
8568         }
8569         if(this.pageX || this.pageY){
8570             this.setPagePosition(this.pageX, this.pageY);
8571         }
8572     },
8573
8574     /**
8575      * Force the component's size to recalculate based on the underlying element's current height and width.
8576      * @returns {Roo.BoxComponent} this
8577      */
8578     syncSize : function(){
8579         delete this.lastSize;
8580         this.setSize(this.el.getWidth(), this.el.getHeight());
8581         return this;
8582     },
8583
8584     /**
8585      * Called after the component is resized, this method is empty by default but can be implemented by any
8586      * subclass that needs to perform custom logic after a resize occurs.
8587      * @param {Number} adjWidth The box-adjusted width that was set
8588      * @param {Number} adjHeight The box-adjusted height that was set
8589      * @param {Number} rawWidth The width that was originally specified
8590      * @param {Number} rawHeight The height that was originally specified
8591      */
8592     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8593
8594     },
8595
8596     /**
8597      * Called after the component is moved, this method is empty by default but can be implemented by any
8598      * subclass that needs to perform custom logic after a move occurs.
8599      * @param {Number} x The new x position
8600      * @param {Number} y The new y position
8601      */
8602     onPosition : function(x, y){
8603
8604     },
8605
8606     // private
8607     adjustSize : function(w, h){
8608         if(this.autoWidth){
8609             w = 'auto';
8610         }
8611         if(this.autoHeight){
8612             h = 'auto';
8613         }
8614         return {width : w, height: h};
8615     },
8616
8617     // private
8618     adjustPosition : function(x, y){
8619         return {x : x, y: y};
8620     }
8621 });/*
8622  * Based on:
8623  * Ext JS Library 1.1.1
8624  * Copyright(c) 2006-2007, Ext JS, LLC.
8625  *
8626  * Originally Released Under LGPL - original licence link has changed is not relivant.
8627  *
8628  * Fork - LGPL
8629  * <script type="text/javascript">
8630  */
8631
8632
8633 /**
8634  * @class Roo.SplitBar
8635  * @extends Roo.util.Observable
8636  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8637  * <br><br>
8638  * Usage:
8639  * <pre><code>
8640 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8641                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8642 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8643 split.minSize = 100;
8644 split.maxSize = 600;
8645 split.animate = true;
8646 split.on('moved', splitterMoved);
8647 </code></pre>
8648  * @constructor
8649  * Create a new SplitBar
8650  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8651  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8652  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8653  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8654                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8655                         position of the SplitBar).
8656  */
8657 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8658     
8659     /** @private */
8660     this.el = Roo.get(dragElement, true);
8661     this.el.dom.unselectable = "on";
8662     /** @private */
8663     this.resizingEl = Roo.get(resizingElement, true);
8664
8665     /**
8666      * @private
8667      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8668      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8669      * @type Number
8670      */
8671     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8672     
8673     /**
8674      * The minimum size of the resizing element. (Defaults to 0)
8675      * @type Number
8676      */
8677     this.minSize = 0;
8678     
8679     /**
8680      * The maximum size of the resizing element. (Defaults to 2000)
8681      * @type Number
8682      */
8683     this.maxSize = 2000;
8684     
8685     /**
8686      * Whether to animate the transition to the new size
8687      * @type Boolean
8688      */
8689     this.animate = false;
8690     
8691     /**
8692      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8693      * @type Boolean
8694      */
8695     this.useShim = false;
8696     
8697     /** @private */
8698     this.shim = null;
8699     
8700     if(!existingProxy){
8701         /** @private */
8702         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8703     }else{
8704         this.proxy = Roo.get(existingProxy).dom;
8705     }
8706     /** @private */
8707     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8708     
8709     /** @private */
8710     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8711     
8712     /** @private */
8713     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8714     
8715     /** @private */
8716     this.dragSpecs = {};
8717     
8718     /**
8719      * @private The adapter to use to positon and resize elements
8720      */
8721     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8722     this.adapter.init(this);
8723     
8724     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8725         /** @private */
8726         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8727         this.el.addClass("x-splitbar-h");
8728     }else{
8729         /** @private */
8730         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8731         this.el.addClass("x-splitbar-v");
8732     }
8733     
8734     this.addEvents({
8735         /**
8736          * @event resize
8737          * Fires when the splitter is moved (alias for {@link #event-moved})
8738          * @param {Roo.SplitBar} this
8739          * @param {Number} newSize the new width or height
8740          */
8741         "resize" : true,
8742         /**
8743          * @event moved
8744          * Fires when the splitter is moved
8745          * @param {Roo.SplitBar} this
8746          * @param {Number} newSize the new width or height
8747          */
8748         "moved" : true,
8749         /**
8750          * @event beforeresize
8751          * Fires before the splitter is dragged
8752          * @param {Roo.SplitBar} this
8753          */
8754         "beforeresize" : true,
8755
8756         "beforeapply" : true
8757     });
8758
8759     Roo.util.Observable.call(this);
8760 };
8761
8762 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8763     onStartProxyDrag : function(x, y){
8764         this.fireEvent("beforeresize", this);
8765         if(!this.overlay){
8766             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8767             o.unselectable();
8768             o.enableDisplayMode("block");
8769             // all splitbars share the same overlay
8770             Roo.SplitBar.prototype.overlay = o;
8771         }
8772         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8773         this.overlay.show();
8774         Roo.get(this.proxy).setDisplayed("block");
8775         var size = this.adapter.getElementSize(this);
8776         this.activeMinSize = this.getMinimumSize();;
8777         this.activeMaxSize = this.getMaximumSize();;
8778         var c1 = size - this.activeMinSize;
8779         var c2 = Math.max(this.activeMaxSize - size, 0);
8780         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8781             this.dd.resetConstraints();
8782             this.dd.setXConstraint(
8783                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8784                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8785             );
8786             this.dd.setYConstraint(0, 0);
8787         }else{
8788             this.dd.resetConstraints();
8789             this.dd.setXConstraint(0, 0);
8790             this.dd.setYConstraint(
8791                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8792                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8793             );
8794          }
8795         this.dragSpecs.startSize = size;
8796         this.dragSpecs.startPoint = [x, y];
8797         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8798     },
8799     
8800     /** 
8801      * @private Called after the drag operation by the DDProxy
8802      */
8803     onEndProxyDrag : function(e){
8804         Roo.get(this.proxy).setDisplayed(false);
8805         var endPoint = Roo.lib.Event.getXY(e);
8806         if(this.overlay){
8807             this.overlay.hide();
8808         }
8809         var newSize;
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             newSize = this.dragSpecs.startSize + 
8812                 (this.placement == Roo.SplitBar.LEFT ?
8813                     endPoint[0] - this.dragSpecs.startPoint[0] :
8814                     this.dragSpecs.startPoint[0] - endPoint[0]
8815                 );
8816         }else{
8817             newSize = this.dragSpecs.startSize + 
8818                 (this.placement == Roo.SplitBar.TOP ?
8819                     endPoint[1] - this.dragSpecs.startPoint[1] :
8820                     this.dragSpecs.startPoint[1] - endPoint[1]
8821                 );
8822         }
8823         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8824         if(newSize != this.dragSpecs.startSize){
8825             if(this.fireEvent('beforeapply', this, newSize) !== false){
8826                 this.adapter.setElementSize(this, newSize);
8827                 this.fireEvent("moved", this, newSize);
8828                 this.fireEvent("resize", this, newSize);
8829             }
8830         }
8831     },
8832     
8833     /**
8834      * Get the adapter this SplitBar uses
8835      * @return The adapter object
8836      */
8837     getAdapter : function(){
8838         return this.adapter;
8839     },
8840     
8841     /**
8842      * Set the adapter this SplitBar uses
8843      * @param {Object} adapter A SplitBar adapter object
8844      */
8845     setAdapter : function(adapter){
8846         this.adapter = adapter;
8847         this.adapter.init(this);
8848     },
8849     
8850     /**
8851      * Gets the minimum size for the resizing element
8852      * @return {Number} The minimum size
8853      */
8854     getMinimumSize : function(){
8855         return this.minSize;
8856     },
8857     
8858     /**
8859      * Sets the minimum size for the resizing element
8860      * @param {Number} minSize The minimum size
8861      */
8862     setMinimumSize : function(minSize){
8863         this.minSize = minSize;
8864     },
8865     
8866     /**
8867      * Gets the maximum size for the resizing element
8868      * @return {Number} The maximum size
8869      */
8870     getMaximumSize : function(){
8871         return this.maxSize;
8872     },
8873     
8874     /**
8875      * Sets the maximum size for the resizing element
8876      * @param {Number} maxSize The maximum size
8877      */
8878     setMaximumSize : function(maxSize){
8879         this.maxSize = maxSize;
8880     },
8881     
8882     /**
8883      * Sets the initialize size for the resizing element
8884      * @param {Number} size The initial size
8885      */
8886     setCurrentSize : function(size){
8887         var oldAnimate = this.animate;
8888         this.animate = false;
8889         this.adapter.setElementSize(this, size);
8890         this.animate = oldAnimate;
8891     },
8892     
8893     /**
8894      * Destroy this splitbar. 
8895      * @param {Boolean} removeEl True to remove the element
8896      */
8897     destroy : function(removeEl){
8898         if(this.shim){
8899             this.shim.remove();
8900         }
8901         this.dd.unreg();
8902         this.proxy.parentNode.removeChild(this.proxy);
8903         if(removeEl){
8904             this.el.remove();
8905         }
8906     }
8907 });
8908
8909 /**
8910  * @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.
8911  */
8912 Roo.SplitBar.createProxy = function(dir){
8913     var proxy = new Roo.Element(document.createElement("div"));
8914     proxy.unselectable();
8915     var cls = 'x-splitbar-proxy';
8916     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8917     document.body.appendChild(proxy.dom);
8918     return proxy.dom;
8919 };
8920
8921 /** 
8922  * @class Roo.SplitBar.BasicLayoutAdapter
8923  * Default Adapter. It assumes the splitter and resizing element are not positioned
8924  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8925  */
8926 Roo.SplitBar.BasicLayoutAdapter = function(){
8927 };
8928
8929 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8930     // do nothing for now
8931     init : function(s){
8932     
8933     },
8934     /**
8935      * Called before drag operations to get the current size of the resizing element. 
8936      * @param {Roo.SplitBar} s The SplitBar using this adapter
8937      */
8938      getElementSize : function(s){
8939         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8940             return s.resizingEl.getWidth();
8941         }else{
8942             return s.resizingEl.getHeight();
8943         }
8944     },
8945     
8946     /**
8947      * Called after drag operations to set the size of the resizing element.
8948      * @param {Roo.SplitBar} s The SplitBar using this adapter
8949      * @param {Number} newSize The new size to set
8950      * @param {Function} onComplete A function to be invoked when resizing is complete
8951      */
8952     setElementSize : function(s, newSize, onComplete){
8953         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8954             if(!s.animate){
8955                 s.resizingEl.setWidth(newSize);
8956                 if(onComplete){
8957                     onComplete(s, newSize);
8958                 }
8959             }else{
8960                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8961             }
8962         }else{
8963             
8964             if(!s.animate){
8965                 s.resizingEl.setHeight(newSize);
8966                 if(onComplete){
8967                     onComplete(s, newSize);
8968                 }
8969             }else{
8970                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8971             }
8972         }
8973     }
8974 };
8975
8976 /** 
8977  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8978  * @extends Roo.SplitBar.BasicLayoutAdapter
8979  * Adapter that  moves the splitter element to align with the resized sizing element. 
8980  * Used with an absolute positioned SplitBar.
8981  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8982  * document.body, make sure you assign an id to the body element.
8983  */
8984 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8985     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8986     this.container = Roo.get(container);
8987 };
8988
8989 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8990     init : function(s){
8991         this.basic.init(s);
8992     },
8993     
8994     getElementSize : function(s){
8995         return this.basic.getElementSize(s);
8996     },
8997     
8998     setElementSize : function(s, newSize, onComplete){
8999         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9000     },
9001     
9002     moveSplitter : function(s){
9003         var yes = Roo.SplitBar;
9004         switch(s.placement){
9005             case yes.LEFT:
9006                 s.el.setX(s.resizingEl.getRight());
9007                 break;
9008             case yes.RIGHT:
9009                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9010                 break;
9011             case yes.TOP:
9012                 s.el.setY(s.resizingEl.getBottom());
9013                 break;
9014             case yes.BOTTOM:
9015                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9016                 break;
9017         }
9018     }
9019 };
9020
9021 /**
9022  * Orientation constant - Create a vertical SplitBar
9023  * @static
9024  * @type Number
9025  */
9026 Roo.SplitBar.VERTICAL = 1;
9027
9028 /**
9029  * Orientation constant - Create a horizontal SplitBar
9030  * @static
9031  * @type Number
9032  */
9033 Roo.SplitBar.HORIZONTAL = 2;
9034
9035 /**
9036  * Placement constant - The resizing element is to the left of the splitter element
9037  * @static
9038  * @type Number
9039  */
9040 Roo.SplitBar.LEFT = 1;
9041
9042 /**
9043  * Placement constant - The resizing element is to the right of the splitter element
9044  * @static
9045  * @type Number
9046  */
9047 Roo.SplitBar.RIGHT = 2;
9048
9049 /**
9050  * Placement constant - The resizing element is positioned above the splitter element
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.TOP = 3;
9055
9056 /**
9057  * Placement constant - The resizing element is positioned under splitter element
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.BOTTOM = 4;
9062 /*
9063  * Based on:
9064  * Ext JS Library 1.1.1
9065  * Copyright(c) 2006-2007, Ext JS, LLC.
9066  *
9067  * Originally Released Under LGPL - original licence link has changed is not relivant.
9068  *
9069  * Fork - LGPL
9070  * <script type="text/javascript">
9071  */
9072
9073 /**
9074  * @class Roo.View
9075  * @extends Roo.util.Observable
9076  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9077  * This class also supports single and multi selection modes. <br>
9078  * Create a data model bound view:
9079  <pre><code>
9080  var store = new Roo.data.Store(...);
9081
9082  var view = new Roo.View({
9083     el : "my-element",
9084     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9085  
9086     singleSelect: true,
9087     selectedClass: "ydataview-selected",
9088     store: store
9089  });
9090
9091  // listen for node click?
9092  view.on("click", function(vw, index, node, e){
9093  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9094  });
9095
9096  // load XML data
9097  dataModel.load("foobar.xml");
9098  </code></pre>
9099  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9100  * <br><br>
9101  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9102  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9103  * 
9104  * Note: old style constructor is still suported (container, template, config)
9105  * 
9106  * @constructor
9107  * Create a new View
9108  * @param {Object} config The config object
9109  * 
9110  */
9111 Roo.View = function(config, depreciated_tpl, depreciated_config){
9112     
9113     if (typeof(depreciated_tpl) == 'undefined') {
9114         // new way.. - universal constructor.
9115         Roo.apply(this, config);
9116         this.el  = Roo.get(this.el);
9117     } else {
9118         // old format..
9119         this.el  = Roo.get(config);
9120         this.tpl = depreciated_tpl;
9121         Roo.apply(this, depreciated_config);
9122     }
9123      
9124     
9125     if(typeof(this.tpl) == "string"){
9126         this.tpl = new Roo.Template(this.tpl);
9127     } else {
9128         // support xtype ctors..
9129         this.tpl = new Roo.factory(this.tpl, Roo);
9130     }
9131     
9132     
9133     this.tpl.compile();
9134    
9135
9136      
9137     /** @private */
9138     this.addEvents({
9139     /**
9140      * @event beforeclick
9141      * Fires before a click is processed. Returns false to cancel the default action.
9142      * @param {Roo.View} this
9143      * @param {Number} index The index of the target node
9144      * @param {HTMLElement} node The target node
9145      * @param {Roo.EventObject} e The raw event object
9146      */
9147         "beforeclick" : true,
9148     /**
9149      * @event click
9150      * Fires when a template node is clicked.
9151      * @param {Roo.View} this
9152      * @param {Number} index The index of the target node
9153      * @param {HTMLElement} node The target node
9154      * @param {Roo.EventObject} e The raw event object
9155      */
9156         "click" : true,
9157     /**
9158      * @event dblclick
9159      * Fires when a template node is double clicked.
9160      * @param {Roo.View} this
9161      * @param {Number} index The index of the target node
9162      * @param {HTMLElement} node The target node
9163      * @param {Roo.EventObject} e The raw event object
9164      */
9165         "dblclick" : true,
9166     /**
9167      * @event contextmenu
9168      * Fires when a template node is right clicked.
9169      * @param {Roo.View} this
9170      * @param {Number} index The index of the target node
9171      * @param {HTMLElement} node The target node
9172      * @param {Roo.EventObject} e The raw event object
9173      */
9174         "contextmenu" : true,
9175     /**
9176      * @event selectionchange
9177      * Fires when the selected nodes change.
9178      * @param {Roo.View} this
9179      * @param {Array} selections Array of the selected nodes
9180      */
9181         "selectionchange" : true,
9182
9183     /**
9184      * @event beforeselect
9185      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9186      * @param {Roo.View} this
9187      * @param {HTMLElement} node The node to be selected
9188      * @param {Array} selections Array of currently selected nodes
9189      */
9190         "beforeselect" : true
9191     });
9192
9193     this.el.on({
9194         "click": this.onClick,
9195         "dblclick": this.onDblClick,
9196         "contextmenu": this.onContextMenu,
9197         scope:this
9198     });
9199
9200     this.selections = [];
9201     this.nodes = [];
9202     this.cmp = new Roo.CompositeElementLite([]);
9203     if(this.store){
9204         this.store = Roo.factory(this.store, Roo.data);
9205         this.setStore(this.store, true);
9206     }
9207     Roo.View.superclass.constructor.call(this);
9208 };
9209
9210 Roo.extend(Roo.View, Roo.util.Observable, {
9211     
9212      /**
9213      * @cfg {Roo.data.Store} store Data store to load data from.
9214      */
9215     store : false,
9216     
9217     /**
9218      * @cfg {String|Roo.Element} el The container element.
9219      */
9220     el : '',
9221     
9222     /**
9223      * @cfg {String|Roo.Template} tpl The template used by this View 
9224      */
9225     tpl : false,
9226     
9227     /**
9228      * @cfg {String} selectedClass The css class to add to selected nodes
9229      */
9230     selectedClass : "x-view-selected",
9231      /**
9232      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9233      */
9234     emptyText : "",
9235     /**
9236      * @cfg {Boolean} multiSelect Allow multiple selection
9237      */
9238     
9239     multiSelect : false,
9240     /**
9241      * @cfg {Boolean} singleSelect Allow single selection
9242      */
9243     singleSelect:  false,
9244     
9245     /**
9246      * Returns the element this view is bound to.
9247      * @return {Roo.Element}
9248      */
9249     getEl : function(){
9250         return this.el;
9251     },
9252
9253     /**
9254      * Refreshes the view.
9255      */
9256     refresh : function(){
9257         var t = this.tpl;
9258         this.clearSelections();
9259         this.el.update("");
9260         var html = [];
9261         var records = this.store.getRange();
9262         if(records.length < 1){
9263             this.el.update(this.emptyText);
9264             return;
9265         }
9266         for(var i = 0, len = records.length; i < len; i++){
9267             var data = this.prepareData(records[i].data, i, records[i]);
9268             html[html.length] = t.apply(data);
9269         }
9270         this.el.update(html.join(""));
9271         this.nodes = this.el.dom.childNodes;
9272         this.updateIndexes(0);
9273     },
9274
9275     /**
9276      * Function to override to reformat the data that is sent to
9277      * the template for each node.
9278      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9279      * a JSON object for an UpdateManager bound view).
9280      */
9281     prepareData : function(data){
9282         return data;
9283     },
9284
9285     onUpdate : function(ds, record){
9286         this.clearSelections();
9287         var index = this.store.indexOf(record);
9288         var n = this.nodes[index];
9289         this.tpl.insertBefore(n, this.prepareData(record.data));
9290         n.parentNode.removeChild(n);
9291         this.updateIndexes(index, index);
9292     },
9293
9294     onAdd : function(ds, records, index){
9295         this.clearSelections();
9296         if(this.nodes.length == 0){
9297             this.refresh();
9298             return;
9299         }
9300         var n = this.nodes[index];
9301         for(var i = 0, len = records.length; i < len; i++){
9302             var d = this.prepareData(records[i].data);
9303             if(n){
9304                 this.tpl.insertBefore(n, d);
9305             }else{
9306                 this.tpl.append(this.el, d);
9307             }
9308         }
9309         this.updateIndexes(index);
9310     },
9311
9312     onRemove : function(ds, record, index){
9313         this.clearSelections();
9314         this.el.dom.removeChild(this.nodes[index]);
9315         this.updateIndexes(index);
9316     },
9317
9318     /**
9319      * Refresh an individual node.
9320      * @param {Number} index
9321      */
9322     refreshNode : function(index){
9323         this.onUpdate(this.store, this.store.getAt(index));
9324     },
9325
9326     updateIndexes : function(startIndex, endIndex){
9327         var ns = this.nodes;
9328         startIndex = startIndex || 0;
9329         endIndex = endIndex || ns.length - 1;
9330         for(var i = startIndex; i <= endIndex; i++){
9331             ns[i].nodeIndex = i;
9332         }
9333     },
9334
9335     /**
9336      * Changes the data store this view uses and refresh the view.
9337      * @param {Store} store
9338      */
9339     setStore : function(store, initial){
9340         if(!initial && this.store){
9341             this.store.un("datachanged", this.refresh);
9342             this.store.un("add", this.onAdd);
9343             this.store.un("remove", this.onRemove);
9344             this.store.un("update", this.onUpdate);
9345             this.store.un("clear", this.refresh);
9346         }
9347         if(store){
9348           
9349             store.on("datachanged", this.refresh, this);
9350             store.on("add", this.onAdd, this);
9351             store.on("remove", this.onRemove, this);
9352             store.on("update", this.onUpdate, this);
9353             store.on("clear", this.refresh, this);
9354         }
9355         
9356         if(store){
9357             this.refresh();
9358         }
9359     },
9360
9361     /**
9362      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9363      * @param {HTMLElement} node
9364      * @return {HTMLElement} The template node
9365      */
9366     findItemFromChild : function(node){
9367         var el = this.el.dom;
9368         if(!node || node.parentNode == el){
9369                     return node;
9370             }
9371             var p = node.parentNode;
9372             while(p && p != el){
9373             if(p.parentNode == el){
9374                 return p;
9375             }
9376             p = p.parentNode;
9377         }
9378             return null;
9379     },
9380
9381     /** @ignore */
9382     onClick : function(e){
9383         var item = this.findItemFromChild(e.getTarget());
9384         if(item){
9385             var index = this.indexOf(item);
9386             if(this.onItemClick(item, index, e) !== false){
9387                 this.fireEvent("click", this, index, item, e);
9388             }
9389         }else{
9390             this.clearSelections();
9391         }
9392     },
9393
9394     /** @ignore */
9395     onContextMenu : function(e){
9396         var item = this.findItemFromChild(e.getTarget());
9397         if(item){
9398             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9399         }
9400     },
9401
9402     /** @ignore */
9403     onDblClick : function(e){
9404         var item = this.findItemFromChild(e.getTarget());
9405         if(item){
9406             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9407         }
9408     },
9409
9410     onItemClick : function(item, index, e){
9411         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9412             return false;
9413         }
9414         if(this.multiSelect || this.singleSelect){
9415             if(this.multiSelect && e.shiftKey && this.lastSelection){
9416                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9417             }else{
9418                 this.select(item, this.multiSelect && e.ctrlKey);
9419                 this.lastSelection = item;
9420             }
9421             e.preventDefault();
9422         }
9423         return true;
9424     },
9425
9426     /**
9427      * Get the number of selected nodes.
9428      * @return {Number}
9429      */
9430     getSelectionCount : function(){
9431         return this.selections.length;
9432     },
9433
9434     /**
9435      * Get the currently selected nodes.
9436      * @return {Array} An array of HTMLElements
9437      */
9438     getSelectedNodes : function(){
9439         return this.selections;
9440     },
9441
9442     /**
9443      * Get the indexes of the selected nodes.
9444      * @return {Array}
9445      */
9446     getSelectedIndexes : function(){
9447         var indexes = [], s = this.selections;
9448         for(var i = 0, len = s.length; i < len; i++){
9449             indexes.push(s[i].nodeIndex);
9450         }
9451         return indexes;
9452     },
9453
9454     /**
9455      * Clear all selections
9456      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9457      */
9458     clearSelections : function(suppressEvent){
9459         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9460             this.cmp.elements = this.selections;
9461             this.cmp.removeClass(this.selectedClass);
9462             this.selections = [];
9463             if(!suppressEvent){
9464                 this.fireEvent("selectionchange", this, this.selections);
9465             }
9466         }
9467     },
9468
9469     /**
9470      * Returns true if the passed node is selected
9471      * @param {HTMLElement/Number} node The node or node index
9472      * @return {Boolean}
9473      */
9474     isSelected : function(node){
9475         var s = this.selections;
9476         if(s.length < 1){
9477             return false;
9478         }
9479         node = this.getNode(node);
9480         return s.indexOf(node) !== -1;
9481     },
9482
9483     /**
9484      * Selects nodes.
9485      * @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
9486      * @param {Boolean} keepExisting (optional) true to keep existing selections
9487      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9488      */
9489     select : function(nodeInfo, keepExisting, suppressEvent){
9490         if(nodeInfo instanceof Array){
9491             if(!keepExisting){
9492                 this.clearSelections(true);
9493             }
9494             for(var i = 0, len = nodeInfo.length; i < len; i++){
9495                 this.select(nodeInfo[i], true, true);
9496             }
9497         } else{
9498             var node = this.getNode(nodeInfo);
9499             if(node && !this.isSelected(node)){
9500                 if(!keepExisting){
9501                     this.clearSelections(true);
9502                 }
9503                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9504                     Roo.fly(node).addClass(this.selectedClass);
9505                     this.selections.push(node);
9506                     if(!suppressEvent){
9507                         this.fireEvent("selectionchange", this, this.selections);
9508                     }
9509                 }
9510             }
9511         }
9512     },
9513
9514     /**
9515      * Gets a template node.
9516      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9517      * @return {HTMLElement} The node or null if it wasn't found
9518      */
9519     getNode : function(nodeInfo){
9520         if(typeof nodeInfo == "string"){
9521             return document.getElementById(nodeInfo);
9522         }else if(typeof nodeInfo == "number"){
9523             return this.nodes[nodeInfo];
9524         }
9525         return nodeInfo;
9526     },
9527
9528     /**
9529      * Gets a range template nodes.
9530      * @param {Number} startIndex
9531      * @param {Number} endIndex
9532      * @return {Array} An array of nodes
9533      */
9534     getNodes : function(start, end){
9535         var ns = this.nodes;
9536         start = start || 0;
9537         end = typeof end == "undefined" ? ns.length - 1 : end;
9538         var nodes = [];
9539         if(start <= end){
9540             for(var i = start; i <= end; i++){
9541                 nodes.push(ns[i]);
9542             }
9543         } else{
9544             for(var i = start; i >= end; i--){
9545                 nodes.push(ns[i]);
9546             }
9547         }
9548         return nodes;
9549     },
9550
9551     /**
9552      * Finds the index of the passed node
9553      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9554      * @return {Number} The index of the node or -1
9555      */
9556     indexOf : function(node){
9557         node = this.getNode(node);
9558         if(typeof node.nodeIndex == "number"){
9559             return node.nodeIndex;
9560         }
9561         var ns = this.nodes;
9562         for(var i = 0, len = ns.length; i < len; i++){
9563             if(ns[i] == node){
9564                 return i;
9565             }
9566         }
9567         return -1;
9568     }
9569 });
9570 /*
9571  * Based on:
9572  * Ext JS Library 1.1.1
9573  * Copyright(c) 2006-2007, Ext JS, LLC.
9574  *
9575  * Originally Released Under LGPL - original licence link has changed is not relivant.
9576  *
9577  * Fork - LGPL
9578  * <script type="text/javascript">
9579  */
9580
9581 /**
9582  * @class Roo.JsonView
9583  * @extends Roo.View
9584  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9585 <pre><code>
9586 var view = new Roo.JsonView({
9587     container: "my-element",
9588     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9589     multiSelect: true, 
9590     jsonRoot: "data" 
9591 });
9592
9593 // listen for node click?
9594 view.on("click", function(vw, index, node, e){
9595     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9596 });
9597
9598 // direct load of JSON data
9599 view.load("foobar.php");
9600
9601 // Example from my blog list
9602 var tpl = new Roo.Template(
9603     '&lt;div class="entry"&gt;' +
9604     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9605     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9606     "&lt;/div&gt;&lt;hr /&gt;"
9607 );
9608
9609 var moreView = new Roo.JsonView({
9610     container :  "entry-list", 
9611     template : tpl,
9612     jsonRoot: "posts"
9613 });
9614 moreView.on("beforerender", this.sortEntries, this);
9615 moreView.load({
9616     url: "/blog/get-posts.php",
9617     params: "allposts=true",
9618     text: "Loading Blog Entries..."
9619 });
9620 </code></pre>
9621
9622 * Note: old code is supported with arguments : (container, template, config)
9623
9624
9625  * @constructor
9626  * Create a new JsonView
9627  * 
9628  * @param {Object} config The config object
9629  * 
9630  */
9631 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9632     
9633     
9634     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9635
9636     var um = this.el.getUpdateManager();
9637     um.setRenderer(this);
9638     um.on("update", this.onLoad, this);
9639     um.on("failure", this.onLoadException, this);
9640
9641     /**
9642      * @event beforerender
9643      * Fires before rendering of the downloaded JSON data.
9644      * @param {Roo.JsonView} this
9645      * @param {Object} data The JSON data loaded
9646      */
9647     /**
9648      * @event load
9649      * Fires when data is loaded.
9650      * @param {Roo.JsonView} this
9651      * @param {Object} data The JSON data loaded
9652      * @param {Object} response The raw Connect response object
9653      */
9654     /**
9655      * @event loadexception
9656      * Fires when loading fails.
9657      * @param {Roo.JsonView} this
9658      * @param {Object} response The raw Connect response object
9659      */
9660     this.addEvents({
9661         'beforerender' : true,
9662         'load' : true,
9663         'loadexception' : true
9664     });
9665 };
9666 Roo.extend(Roo.JsonView, Roo.View, {
9667     /**
9668      * @type {String} The root property in the loaded JSON object that contains the data
9669      */
9670     jsonRoot : "",
9671
9672     /**
9673      * Refreshes the view.
9674      */
9675     refresh : function(){
9676         this.clearSelections();
9677         this.el.update("");
9678         var html = [];
9679         var o = this.jsonData;
9680         if(o && o.length > 0){
9681             for(var i = 0, len = o.length; i < len; i++){
9682                 var data = this.prepareData(o[i], i, o);
9683                 html[html.length] = this.tpl.apply(data);
9684             }
9685         }else{
9686             html.push(this.emptyText);
9687         }
9688         this.el.update(html.join(""));
9689         this.nodes = this.el.dom.childNodes;
9690         this.updateIndexes(0);
9691     },
9692
9693     /**
9694      * 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.
9695      * @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:
9696      <pre><code>
9697      view.load({
9698          url: "your-url.php",
9699          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9700          callback: yourFunction,
9701          scope: yourObject, //(optional scope)
9702          discardUrl: false,
9703          nocache: false,
9704          text: "Loading...",
9705          timeout: 30,
9706          scripts: false
9707      });
9708      </code></pre>
9709      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9710      * 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.
9711      * @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}
9712      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9713      * @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.
9714      */
9715     load : function(){
9716         var um = this.el.getUpdateManager();
9717         um.update.apply(um, arguments);
9718     },
9719
9720     render : function(el, response){
9721         this.clearSelections();
9722         this.el.update("");
9723         var o;
9724         try{
9725             o = Roo.util.JSON.decode(response.responseText);
9726             if(this.jsonRoot){
9727                 
9728                 o = o[this.jsonRoot];
9729             }
9730         } catch(e){
9731         }
9732         /**
9733          * The current JSON data or null
9734          */
9735         this.jsonData = o;
9736         this.beforeRender();
9737         this.refresh();
9738     },
9739
9740 /**
9741  * Get the number of records in the current JSON dataset
9742  * @return {Number}
9743  */
9744     getCount : function(){
9745         return this.jsonData ? this.jsonData.length : 0;
9746     },
9747
9748 /**
9749  * Returns the JSON object for the specified node(s)
9750  * @param {HTMLElement/Array} node The node or an array of nodes
9751  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9752  * you get the JSON object for the node
9753  */
9754     getNodeData : function(node){
9755         if(node instanceof Array){
9756             var data = [];
9757             for(var i = 0, len = node.length; i < len; i++){
9758                 data.push(this.getNodeData(node[i]));
9759             }
9760             return data;
9761         }
9762         return this.jsonData[this.indexOf(node)] || null;
9763     },
9764
9765     beforeRender : function(){
9766         this.snapshot = this.jsonData;
9767         if(this.sortInfo){
9768             this.sort.apply(this, this.sortInfo);
9769         }
9770         this.fireEvent("beforerender", this, this.jsonData);
9771     },
9772
9773     onLoad : function(el, o){
9774         this.fireEvent("load", this, this.jsonData, o);
9775     },
9776
9777     onLoadException : function(el, o){
9778         this.fireEvent("loadexception", this, o);
9779     },
9780
9781 /**
9782  * Filter the data by a specific property.
9783  * @param {String} property A property on your JSON objects
9784  * @param {String/RegExp} value Either string that the property values
9785  * should start with, or a RegExp to test against the property
9786  */
9787     filter : function(property, value){
9788         if(this.jsonData){
9789             var data = [];
9790             var ss = this.snapshot;
9791             if(typeof value == "string"){
9792                 var vlen = value.length;
9793                 if(vlen == 0){
9794                     this.clearFilter();
9795                     return;
9796                 }
9797                 value = value.toLowerCase();
9798                 for(var i = 0, len = ss.length; i < len; i++){
9799                     var o = ss[i];
9800                     if(o[property].substr(0, vlen).toLowerCase() == value){
9801                         data.push(o);
9802                     }
9803                 }
9804             } else if(value.exec){ // regex?
9805                 for(var i = 0, len = ss.length; i < len; i++){
9806                     var o = ss[i];
9807                     if(value.test(o[property])){
9808                         data.push(o);
9809                     }
9810                 }
9811             } else{
9812                 return;
9813             }
9814             this.jsonData = data;
9815             this.refresh();
9816         }
9817     },
9818
9819 /**
9820  * Filter by a function. The passed function will be called with each
9821  * object in the current dataset. If the function returns true the value is kept,
9822  * otherwise it is filtered.
9823  * @param {Function} fn
9824  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9825  */
9826     filterBy : function(fn, scope){
9827         if(this.jsonData){
9828             var data = [];
9829             var ss = this.snapshot;
9830             for(var i = 0, len = ss.length; i < len; i++){
9831                 var o = ss[i];
9832                 if(fn.call(scope || this, o)){
9833                     data.push(o);
9834                 }
9835             }
9836             this.jsonData = data;
9837             this.refresh();
9838         }
9839     },
9840
9841 /**
9842  * Clears the current filter.
9843  */
9844     clearFilter : function(){
9845         if(this.snapshot && this.jsonData != this.snapshot){
9846             this.jsonData = this.snapshot;
9847             this.refresh();
9848         }
9849     },
9850
9851
9852 /**
9853  * Sorts the data for this view and refreshes it.
9854  * @param {String} property A property on your JSON objects to sort on
9855  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9856  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9857  */
9858     sort : function(property, dir, sortType){
9859         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9860         if(this.jsonData){
9861             var p = property;
9862             var dsc = dir && dir.toLowerCase() == "desc";
9863             var f = function(o1, o2){
9864                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9865                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9866                 ;
9867                 if(v1 < v2){
9868                     return dsc ? +1 : -1;
9869                 } else if(v1 > v2){
9870                     return dsc ? -1 : +1;
9871                 } else{
9872                     return 0;
9873                 }
9874             };
9875             this.jsonData.sort(f);
9876             this.refresh();
9877             if(this.jsonData != this.snapshot){
9878                 this.snapshot.sort(f);
9879             }
9880         }
9881     }
9882 });/*
9883  * Based on:
9884  * Ext JS Library 1.1.1
9885  * Copyright(c) 2006-2007, Ext JS, LLC.
9886  *
9887  * Originally Released Under LGPL - original licence link has changed is not relivant.
9888  *
9889  * Fork - LGPL
9890  * <script type="text/javascript">
9891  */
9892  
9893
9894 /**
9895  * @class Roo.ColorPalette
9896  * @extends Roo.Component
9897  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9898  * Here's an example of typical usage:
9899  * <pre><code>
9900 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9901 cp.render('my-div');
9902
9903 cp.on('select', function(palette, selColor){
9904     // do something with selColor
9905 });
9906 </code></pre>
9907  * @constructor
9908  * Create a new ColorPalette
9909  * @param {Object} config The config object
9910  */
9911 Roo.ColorPalette = function(config){
9912     Roo.ColorPalette.superclass.constructor.call(this, config);
9913     this.addEvents({
9914         /**
9915              * @event select
9916              * Fires when a color is selected
9917              * @param {ColorPalette} this
9918              * @param {String} color The 6-digit color hex code (without the # symbol)
9919              */
9920         select: true
9921     });
9922
9923     if(this.handler){
9924         this.on("select", this.handler, this.scope, true);
9925     }
9926 };
9927 Roo.extend(Roo.ColorPalette, Roo.Component, {
9928     /**
9929      * @cfg {String} itemCls
9930      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9931      */
9932     itemCls : "x-color-palette",
9933     /**
9934      * @cfg {String} value
9935      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9936      * the hex codes are case-sensitive.
9937      */
9938     value : null,
9939     clickEvent:'click',
9940     // private
9941     ctype: "Roo.ColorPalette",
9942
9943     /**
9944      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9945      */
9946     allowReselect : false,
9947
9948     /**
9949      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9950      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9951      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9952      * of colors with the width setting until the box is symmetrical.</p>
9953      * <p>You can override individual colors if needed:</p>
9954      * <pre><code>
9955 var cp = new Roo.ColorPalette();
9956 cp.colors[0] = "FF0000";  // change the first box to red
9957 </code></pre>
9958
9959 Or you can provide a custom array of your own for complete control:
9960 <pre><code>
9961 var cp = new Roo.ColorPalette();
9962 cp.colors = ["000000", "993300", "333300"];
9963 </code></pre>
9964      * @type Array
9965      */
9966     colors : [
9967         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9968         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9969         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9970         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9971         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9972     ],
9973
9974     // private
9975     onRender : function(container, position){
9976         var t = new Roo.MasterTemplate(
9977             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9978         );
9979         var c = this.colors;
9980         for(var i = 0, len = c.length; i < len; i++){
9981             t.add([c[i]]);
9982         }
9983         var el = document.createElement("div");
9984         el.className = this.itemCls;
9985         t.overwrite(el);
9986         container.dom.insertBefore(el, position);
9987         this.el = Roo.get(el);
9988         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9989         if(this.clickEvent != 'click'){
9990             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9991         }
9992     },
9993
9994     // private
9995     afterRender : function(){
9996         Roo.ColorPalette.superclass.afterRender.call(this);
9997         if(this.value){
9998             var s = this.value;
9999             this.value = null;
10000             this.select(s);
10001         }
10002     },
10003
10004     // private
10005     handleClick : function(e, t){
10006         e.preventDefault();
10007         if(!this.disabled){
10008             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10009             this.select(c.toUpperCase());
10010         }
10011     },
10012
10013     /**
10014      * Selects the specified color in the palette (fires the select event)
10015      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10016      */
10017     select : function(color){
10018         color = color.replace("#", "");
10019         if(color != this.value || this.allowReselect){
10020             var el = this.el;
10021             if(this.value){
10022                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10023             }
10024             el.child("a.color-"+color).addClass("x-color-palette-sel");
10025             this.value = color;
10026             this.fireEvent("select", this, color);
10027         }
10028     }
10029 });/*
10030  * Based on:
10031  * Ext JS Library 1.1.1
10032  * Copyright(c) 2006-2007, Ext JS, LLC.
10033  *
10034  * Originally Released Under LGPL - original licence link has changed is not relivant.
10035  *
10036  * Fork - LGPL
10037  * <script type="text/javascript">
10038  */
10039  
10040 /**
10041  * @class Roo.DatePicker
10042  * @extends Roo.Component
10043  * Simple date picker class.
10044  * @constructor
10045  * Create a new DatePicker
10046  * @param {Object} config The config object
10047  */
10048 Roo.DatePicker = function(config){
10049     Roo.DatePicker.superclass.constructor.call(this, config);
10050
10051     this.value = config && config.value ?
10052                  config.value.clearTime() : new Date().clearTime();
10053
10054     this.addEvents({
10055         /**
10056              * @event select
10057              * Fires when a date is selected
10058              * @param {DatePicker} this
10059              * @param {Date} date The selected date
10060              */
10061         select: true
10062     });
10063
10064     if(this.handler){
10065         this.on("select", this.handler,  this.scope || this);
10066     }
10067     // build the disabledDatesRE
10068     if(!this.disabledDatesRE && this.disabledDates){
10069         var dd = this.disabledDates;
10070         var re = "(?:";
10071         for(var i = 0; i < dd.length; i++){
10072             re += dd[i];
10073             if(i != dd.length-1) re += "|";
10074         }
10075         this.disabledDatesRE = new RegExp(re + ")");
10076     }
10077 };
10078
10079 Roo.extend(Roo.DatePicker, Roo.Component, {
10080     /**
10081      * @cfg {String} todayText
10082      * The text to display on the button that selects the current date (defaults to "Today")
10083      */
10084     todayText : "Today",
10085     /**
10086      * @cfg {String} okText
10087      * The text to display on the ok button
10088      */
10089     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10090     /**
10091      * @cfg {String} cancelText
10092      * The text to display on the cancel button
10093      */
10094     cancelText : "Cancel",
10095     /**
10096      * @cfg {String} todayTip
10097      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10098      */
10099     todayTip : "{0} (Spacebar)",
10100     /**
10101      * @cfg {Date} minDate
10102      * Minimum allowable date (JavaScript date object, defaults to null)
10103      */
10104     minDate : null,
10105     /**
10106      * @cfg {Date} maxDate
10107      * Maximum allowable date (JavaScript date object, defaults to null)
10108      */
10109     maxDate : null,
10110     /**
10111      * @cfg {String} minText
10112      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10113      */
10114     minText : "This date is before the minimum date",
10115     /**
10116      * @cfg {String} maxText
10117      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10118      */
10119     maxText : "This date is after the maximum date",
10120     /**
10121      * @cfg {String} format
10122      * The default date format string which can be overriden for localization support.  The format must be
10123      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10124      */
10125     format : "m/d/y",
10126     /**
10127      * @cfg {Array} disabledDays
10128      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10129      */
10130     disabledDays : null,
10131     /**
10132      * @cfg {String} disabledDaysText
10133      * The tooltip to display when the date falls on a disabled day (defaults to "")
10134      */
10135     disabledDaysText : "",
10136     /**
10137      * @cfg {RegExp} disabledDatesRE
10138      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10139      */
10140     disabledDatesRE : null,
10141     /**
10142      * @cfg {String} disabledDatesText
10143      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10144      */
10145     disabledDatesText : "",
10146     /**
10147      * @cfg {Boolean} constrainToViewport
10148      * True to constrain the date picker to the viewport (defaults to true)
10149      */
10150     constrainToViewport : true,
10151     /**
10152      * @cfg {Array} monthNames
10153      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10154      */
10155     monthNames : Date.monthNames,
10156     /**
10157      * @cfg {Array} dayNames
10158      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10159      */
10160     dayNames : Date.dayNames,
10161     /**
10162      * @cfg {String} nextText
10163      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10164      */
10165     nextText: 'Next Month (Control+Right)',
10166     /**
10167      * @cfg {String} prevText
10168      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10169      */
10170     prevText: 'Previous Month (Control+Left)',
10171     /**
10172      * @cfg {String} monthYearText
10173      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10174      */
10175     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10176     /**
10177      * @cfg {Number} startDay
10178      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10179      */
10180     startDay : 0,
10181     /**
10182      * @cfg {Bool} showClear
10183      * Show a clear button (usefull for date form elements that can be blank.)
10184      */
10185     
10186     showClear: false,
10187     
10188     /**
10189      * Sets the value of the date field
10190      * @param {Date} value The date to set
10191      */
10192     setValue : function(value){
10193         var old = this.value;
10194         this.value = value.clearTime(true);
10195         if(this.el){
10196             this.update(this.value);
10197         }
10198     },
10199
10200     /**
10201      * Gets the current selected value of the date field
10202      * @return {Date} The selected date
10203      */
10204     getValue : function(){
10205         return this.value;
10206     },
10207
10208     // private
10209     focus : function(){
10210         if(this.el){
10211             this.update(this.activeDate);
10212         }
10213     },
10214
10215     // private
10216     onRender : function(container, position){
10217         var m = [
10218              '<table cellspacing="0">',
10219                 '<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>',
10220                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10221         var dn = this.dayNames;
10222         for(var i = 0; i < 7; i++){
10223             var d = this.startDay+i;
10224             if(d > 6){
10225                 d = d-7;
10226             }
10227             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10228         }
10229         m[m.length] = "</tr></thead><tbody><tr>";
10230         for(var i = 0; i < 42; i++) {
10231             if(i % 7 == 0 && i != 0){
10232                 m[m.length] = "</tr><tr>";
10233             }
10234             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10235         }
10236         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10237             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10238
10239         var el = document.createElement("div");
10240         el.className = "x-date-picker";
10241         el.innerHTML = m.join("");
10242
10243         container.dom.insertBefore(el, position);
10244
10245         this.el = Roo.get(el);
10246         this.eventEl = Roo.get(el.firstChild);
10247
10248         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10249             handler: this.showPrevMonth,
10250             scope: this,
10251             preventDefault:true,
10252             stopDefault:true
10253         });
10254
10255         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10256             handler: this.showNextMonth,
10257             scope: this,
10258             preventDefault:true,
10259             stopDefault:true
10260         });
10261
10262         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10263
10264         this.monthPicker = this.el.down('div.x-date-mp');
10265         this.monthPicker.enableDisplayMode('block');
10266         
10267         var kn = new Roo.KeyNav(this.eventEl, {
10268             "left" : function(e){
10269                 e.ctrlKey ?
10270                     this.showPrevMonth() :
10271                     this.update(this.activeDate.add("d", -1));
10272             },
10273
10274             "right" : function(e){
10275                 e.ctrlKey ?
10276                     this.showNextMonth() :
10277                     this.update(this.activeDate.add("d", 1));
10278             },
10279
10280             "up" : function(e){
10281                 e.ctrlKey ?
10282                     this.showNextYear() :
10283                     this.update(this.activeDate.add("d", -7));
10284             },
10285
10286             "down" : function(e){
10287                 e.ctrlKey ?
10288                     this.showPrevYear() :
10289                     this.update(this.activeDate.add("d", 7));
10290             },
10291
10292             "pageUp" : function(e){
10293                 this.showNextMonth();
10294             },
10295
10296             "pageDown" : function(e){
10297                 this.showPrevMonth();
10298             },
10299
10300             "enter" : function(e){
10301                 e.stopPropagation();
10302                 return true;
10303             },
10304
10305             scope : this
10306         });
10307
10308         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10309
10310         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10311
10312         this.el.unselectable();
10313         
10314         this.cells = this.el.select("table.x-date-inner tbody td");
10315         this.textNodes = this.el.query("table.x-date-inner tbody span");
10316
10317         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10318             text: "&#160;",
10319             tooltip: this.monthYearText
10320         });
10321
10322         this.mbtn.on('click', this.showMonthPicker, this);
10323         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10324
10325
10326         var today = (new Date()).dateFormat(this.format);
10327         
10328         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10329         if (this.showClear) {
10330             baseTb.add( new Roo.Toolbar.Fill());
10331         }
10332         baseTb.add({
10333             text: String.format(this.todayText, today),
10334             tooltip: String.format(this.todayTip, today),
10335             handler: this.selectToday,
10336             scope: this
10337         });
10338         
10339         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10340             
10341         //});
10342         if (this.showClear) {
10343             
10344             baseTb.add( new Roo.Toolbar.Fill());
10345             baseTb.add({
10346                 text: '&#160;',
10347                 cls: 'x-btn-icon x-btn-clear',
10348                 handler: function() {
10349                     //this.value = '';
10350                     this.fireEvent("select", this, '');
10351                 },
10352                 scope: this
10353             });
10354         }
10355         
10356         
10357         if(Roo.isIE){
10358             this.el.repaint();
10359         }
10360         this.update(this.value);
10361     },
10362
10363     createMonthPicker : function(){
10364         if(!this.monthPicker.dom.firstChild){
10365             var buf = ['<table border="0" cellspacing="0">'];
10366             for(var i = 0; i < 6; i++){
10367                 buf.push(
10368                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10369                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10370                     i == 0 ?
10371                     '<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>' :
10372                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10373                 );
10374             }
10375             buf.push(
10376                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10377                     this.okText,
10378                     '</button><button type="button" class="x-date-mp-cancel">',
10379                     this.cancelText,
10380                     '</button></td></tr>',
10381                 '</table>'
10382             );
10383             this.monthPicker.update(buf.join(''));
10384             this.monthPicker.on('click', this.onMonthClick, this);
10385             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10386
10387             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10388             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10389
10390             this.mpMonths.each(function(m, a, i){
10391                 i += 1;
10392                 if((i%2) == 0){
10393                     m.dom.xmonth = 5 + Math.round(i * .5);
10394                 }else{
10395                     m.dom.xmonth = Math.round((i-1) * .5);
10396                 }
10397             });
10398         }
10399     },
10400
10401     showMonthPicker : function(){
10402         this.createMonthPicker();
10403         var size = this.el.getSize();
10404         this.monthPicker.setSize(size);
10405         this.monthPicker.child('table').setSize(size);
10406
10407         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10408         this.updateMPMonth(this.mpSelMonth);
10409         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10410         this.updateMPYear(this.mpSelYear);
10411
10412         this.monthPicker.slideIn('t', {duration:.2});
10413     },
10414
10415     updateMPYear : function(y){
10416         this.mpyear = y;
10417         var ys = this.mpYears.elements;
10418         for(var i = 1; i <= 10; i++){
10419             var td = ys[i-1], y2;
10420             if((i%2) == 0){
10421                 y2 = y + Math.round(i * .5);
10422                 td.firstChild.innerHTML = y2;
10423                 td.xyear = y2;
10424             }else{
10425                 y2 = y - (5-Math.round(i * .5));
10426                 td.firstChild.innerHTML = y2;
10427                 td.xyear = y2;
10428             }
10429             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10430         }
10431     },
10432
10433     updateMPMonth : function(sm){
10434         this.mpMonths.each(function(m, a, i){
10435             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10436         });
10437     },
10438
10439     selectMPMonth: function(m){
10440         
10441     },
10442
10443     onMonthClick : function(e, t){
10444         e.stopEvent();
10445         var el = new Roo.Element(t), pn;
10446         if(el.is('button.x-date-mp-cancel')){
10447             this.hideMonthPicker();
10448         }
10449         else if(el.is('button.x-date-mp-ok')){
10450             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10451             this.hideMonthPicker();
10452         }
10453         else if(pn = el.up('td.x-date-mp-month', 2)){
10454             this.mpMonths.removeClass('x-date-mp-sel');
10455             pn.addClass('x-date-mp-sel');
10456             this.mpSelMonth = pn.dom.xmonth;
10457         }
10458         else if(pn = el.up('td.x-date-mp-year', 2)){
10459             this.mpYears.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelYear = pn.dom.xyear;
10462         }
10463         else if(el.is('a.x-date-mp-prev')){
10464             this.updateMPYear(this.mpyear-10);
10465         }
10466         else if(el.is('a.x-date-mp-next')){
10467             this.updateMPYear(this.mpyear+10);
10468         }
10469     },
10470
10471     onMonthDblClick : function(e, t){
10472         e.stopEvent();
10473         var el = new Roo.Element(t), pn;
10474         if(pn = el.up('td.x-date-mp-month', 2)){
10475             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10476             this.hideMonthPicker();
10477         }
10478         else if(pn = el.up('td.x-date-mp-year', 2)){
10479             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10480             this.hideMonthPicker();
10481         }
10482     },
10483
10484     hideMonthPicker : function(disableAnim){
10485         if(this.monthPicker){
10486             if(disableAnim === true){
10487                 this.monthPicker.hide();
10488             }else{
10489                 this.monthPicker.slideOut('t', {duration:.2});
10490             }
10491         }
10492     },
10493
10494     // private
10495     showPrevMonth : function(e){
10496         this.update(this.activeDate.add("mo", -1));
10497     },
10498
10499     // private
10500     showNextMonth : function(e){
10501         this.update(this.activeDate.add("mo", 1));
10502     },
10503
10504     // private
10505     showPrevYear : function(){
10506         this.update(this.activeDate.add("y", -1));
10507     },
10508
10509     // private
10510     showNextYear : function(){
10511         this.update(this.activeDate.add("y", 1));
10512     },
10513
10514     // private
10515     handleMouseWheel : function(e){
10516         var delta = e.getWheelDelta();
10517         if(delta > 0){
10518             this.showPrevMonth();
10519             e.stopEvent();
10520         } else if(delta < 0){
10521             this.showNextMonth();
10522             e.stopEvent();
10523         }
10524     },
10525
10526     // private
10527     handleDateClick : function(e, t){
10528         e.stopEvent();
10529         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10530             this.setValue(new Date(t.dateValue));
10531             this.fireEvent("select", this, this.value);
10532         }
10533     },
10534
10535     // private
10536     selectToday : function(){
10537         this.setValue(new Date().clearTime());
10538         this.fireEvent("select", this, this.value);
10539     },
10540
10541     // private
10542     update : function(date){
10543         var vd = this.activeDate;
10544         this.activeDate = date;
10545         if(vd && this.el){
10546             var t = date.getTime();
10547             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10548                 this.cells.removeClass("x-date-selected");
10549                 this.cells.each(function(c){
10550                    if(c.dom.firstChild.dateValue == t){
10551                        c.addClass("x-date-selected");
10552                        setTimeout(function(){
10553                             try{c.dom.firstChild.focus();}catch(e){}
10554                        }, 50);
10555                        return false;
10556                    }
10557                 });
10558                 return;
10559             }
10560         }
10561         var days = date.getDaysInMonth();
10562         var firstOfMonth = date.getFirstDateOfMonth();
10563         var startingPos = firstOfMonth.getDay()-this.startDay;
10564
10565         if(startingPos <= this.startDay){
10566             startingPos += 7;
10567         }
10568
10569         var pm = date.add("mo", -1);
10570         var prevStart = pm.getDaysInMonth()-startingPos;
10571
10572         var cells = this.cells.elements;
10573         var textEls = this.textNodes;
10574         days += startingPos;
10575
10576         // convert everything to numbers so it's fast
10577         var day = 86400000;
10578         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10579         var today = new Date().clearTime().getTime();
10580         var sel = date.clearTime().getTime();
10581         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10582         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10583         var ddMatch = this.disabledDatesRE;
10584         var ddText = this.disabledDatesText;
10585         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10586         var ddaysText = this.disabledDaysText;
10587         var format = this.format;
10588
10589         var setCellClass = function(cal, cell){
10590             cell.title = "";
10591             var t = d.getTime();
10592             cell.firstChild.dateValue = t;
10593             if(t == today){
10594                 cell.className += " x-date-today";
10595                 cell.title = cal.todayText;
10596             }
10597             if(t == sel){
10598                 cell.className += " x-date-selected";
10599                 setTimeout(function(){
10600                     try{cell.firstChild.focus();}catch(e){}
10601                 }, 50);
10602             }
10603             // disabling
10604             if(t < min) {
10605                 cell.className = " x-date-disabled";
10606                 cell.title = cal.minText;
10607                 return;
10608             }
10609             if(t > max) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.maxText;
10612                 return;
10613             }
10614             if(ddays){
10615                 if(ddays.indexOf(d.getDay()) != -1){
10616                     cell.title = ddaysText;
10617                     cell.className = " x-date-disabled";
10618                 }
10619             }
10620             if(ddMatch && format){
10621                 var fvalue = d.dateFormat(format);
10622                 if(ddMatch.test(fvalue)){
10623                     cell.title = ddText.replace("%0", fvalue);
10624                     cell.className = " x-date-disabled";
10625                 }
10626             }
10627         };
10628
10629         var i = 0;
10630         for(; i < startingPos; i++) {
10631             textEls[i].innerHTML = (++prevStart);
10632             d.setDate(d.getDate()+1);
10633             cells[i].className = "x-date-prevday";
10634             setCellClass(this, cells[i]);
10635         }
10636         for(; i < days; i++){
10637             intDay = i - startingPos + 1;
10638             textEls[i].innerHTML = (intDay);
10639             d.setDate(d.getDate()+1);
10640             cells[i].className = "x-date-active";
10641             setCellClass(this, cells[i]);
10642         }
10643         var extraDays = 0;
10644         for(; i < 42; i++) {
10645              textEls[i].innerHTML = (++extraDays);
10646              d.setDate(d.getDate()+1);
10647              cells[i].className = "x-date-nextday";
10648              setCellClass(this, cells[i]);
10649         }
10650
10651         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10652
10653         if(!this.internalRender){
10654             var main = this.el.dom.firstChild;
10655             var w = main.offsetWidth;
10656             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10657             Roo.fly(main).setWidth(w);
10658             this.internalRender = true;
10659             // opera does not respect the auto grow header center column
10660             // then, after it gets a width opera refuses to recalculate
10661             // without a second pass
10662             if(Roo.isOpera && !this.secondPass){
10663                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10664                 this.secondPass = true;
10665                 this.update.defer(10, this, [date]);
10666             }
10667         }
10668     }
10669 });/*
10670  * Based on:
10671  * Ext JS Library 1.1.1
10672  * Copyright(c) 2006-2007, Ext JS, LLC.
10673  *
10674  * Originally Released Under LGPL - original licence link has changed is not relivant.
10675  *
10676  * Fork - LGPL
10677  * <script type="text/javascript">
10678  */
10679 /**
10680  * @class Roo.TabPanel
10681  * @extends Roo.util.Observable
10682  * A lightweight tab container.
10683  * <br><br>
10684  * Usage:
10685  * <pre><code>
10686 // basic tabs 1, built from existing content
10687 var tabs = new Roo.TabPanel("tabs1");
10688 tabs.addTab("script", "View Script");
10689 tabs.addTab("markup", "View Markup");
10690 tabs.activate("script");
10691
10692 // more advanced tabs, built from javascript
10693 var jtabs = new Roo.TabPanel("jtabs");
10694 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10695
10696 // set up the UpdateManager
10697 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10698 var updater = tab2.getUpdateManager();
10699 updater.setDefaultUrl("ajax1.htm");
10700 tab2.on('activate', updater.refresh, updater, true);
10701
10702 // Use setUrl for Ajax loading
10703 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10704 tab3.setUrl("ajax2.htm", null, true);
10705
10706 // Disabled tab
10707 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10708 tab4.disable();
10709
10710 jtabs.activate("jtabs-1");
10711  * </code></pre>
10712  * @constructor
10713  * Create a new TabPanel.
10714  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10715  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10716  */
10717 Roo.TabPanel = function(container, config){
10718     /**
10719     * The container element for this TabPanel.
10720     * @type Roo.Element
10721     */
10722     this.el = Roo.get(container, true);
10723     if(config){
10724         if(typeof config == "boolean"){
10725             this.tabPosition = config ? "bottom" : "top";
10726         }else{
10727             Roo.apply(this, config);
10728         }
10729     }
10730     if(this.tabPosition == "bottom"){
10731         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10732         this.el.addClass("x-tabs-bottom");
10733     }
10734     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10735     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10736     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10737     if(Roo.isIE){
10738         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10739     }
10740     if(this.tabPosition != "bottom"){
10741     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10742      * @type Roo.Element
10743      */
10744       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10745       this.el.addClass("x-tabs-top");
10746     }
10747     this.items = [];
10748
10749     this.bodyEl.setStyle("position", "relative");
10750
10751     this.active = null;
10752     this.activateDelegate = this.activate.createDelegate(this);
10753
10754     this.addEvents({
10755         /**
10756          * @event tabchange
10757          * Fires when the active tab changes
10758          * @param {Roo.TabPanel} this
10759          * @param {Roo.TabPanelItem} activePanel The new active tab
10760          */
10761         "tabchange": true,
10762         /**
10763          * @event beforetabchange
10764          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10765          * @param {Roo.TabPanel} this
10766          * @param {Object} e Set cancel to true on this object to cancel the tab change
10767          * @param {Roo.TabPanelItem} tab The tab being changed to
10768          */
10769         "beforetabchange" : true
10770     });
10771
10772     Roo.EventManager.onWindowResize(this.onResize, this);
10773     this.cpad = this.el.getPadding("lr");
10774     this.hiddenCount = 0;
10775
10776     Roo.TabPanel.superclass.constructor.call(this);
10777 };
10778
10779 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10780         /*
10781          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10782          */
10783     tabPosition : "top",
10784         /*
10785          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10786          */
10787     currentTabWidth : 0,
10788         /*
10789          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10790          */
10791     minTabWidth : 40,
10792         /*
10793          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10794          */
10795     maxTabWidth : 250,
10796         /*
10797          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10798          */
10799     preferredTabWidth : 175,
10800         /*
10801          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10802          */
10803     resizeTabs : false,
10804         /*
10805          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10806          */
10807     monitorResize : true,
10808
10809     /**
10810      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10811      * @param {String} id The id of the div to use <b>or create</b>
10812      * @param {String} text The text for the tab
10813      * @param {String} content (optional) Content to put in the TabPanelItem body
10814      * @param {Boolean} closable (optional) True to create a close icon on the tab
10815      * @return {Roo.TabPanelItem} The created TabPanelItem
10816      */
10817     addTab : function(id, text, content, closable){
10818         var item = new Roo.TabPanelItem(this, id, text, closable);
10819         this.addTabItem(item);
10820         if(content){
10821             item.setContent(content);
10822         }
10823         return item;
10824     },
10825
10826     /**
10827      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10828      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10829      * @return {Roo.TabPanelItem}
10830      */
10831     getTab : function(id){
10832         return this.items[id];
10833     },
10834
10835     /**
10836      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10837      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10838      */
10839     hideTab : function(id){
10840         var t = this.items[id];
10841         if(!t.isHidden()){
10842            t.setHidden(true);
10843            this.hiddenCount++;
10844            this.autoSizeTabs();
10845         }
10846     },
10847
10848     /**
10849      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10850      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10851      */
10852     unhideTab : function(id){
10853         var t = this.items[id];
10854         if(t.isHidden()){
10855            t.setHidden(false);
10856            this.hiddenCount--;
10857            this.autoSizeTabs();
10858         }
10859     },
10860
10861     /**
10862      * Adds an existing {@link Roo.TabPanelItem}.
10863      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10864      */
10865     addTabItem : function(item){
10866         this.items[item.id] = item;
10867         this.items.push(item);
10868         if(this.resizeTabs){
10869            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10870            this.autoSizeTabs();
10871         }else{
10872             item.autoSize();
10873         }
10874     },
10875
10876     /**
10877      * Removes a {@link Roo.TabPanelItem}.
10878      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10879      */
10880     removeTab : function(id){
10881         var items = this.items;
10882         var tab = items[id];
10883         if(!tab) { return; }
10884         var index = items.indexOf(tab);
10885         if(this.active == tab && items.length > 1){
10886             var newTab = this.getNextAvailable(index);
10887             if(newTab) {
10888                 newTab.activate();
10889             }
10890         }
10891         this.stripEl.dom.removeChild(tab.pnode.dom);
10892         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10893             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10894         }
10895         items.splice(index, 1);
10896         delete this.items[tab.id];
10897         tab.fireEvent("close", tab);
10898         tab.purgeListeners();
10899         this.autoSizeTabs();
10900     },
10901
10902     getNextAvailable : function(start){
10903         var items = this.items;
10904         var index = start;
10905         // look for a next tab that will slide over to
10906         // replace the one being removed
10907         while(index < items.length){
10908             var item = items[++index];
10909             if(item && !item.isHidden()){
10910                 return item;
10911             }
10912         }
10913         // if one isn't found select the previous tab (on the left)
10914         index = start;
10915         while(index >= 0){
10916             var item = items[--index];
10917             if(item && !item.isHidden()){
10918                 return item;
10919             }
10920         }
10921         return null;
10922     },
10923
10924     /**
10925      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10926      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10927      */
10928     disableTab : function(id){
10929         var tab = this.items[id];
10930         if(tab && this.active != tab){
10931             tab.disable();
10932         }
10933     },
10934
10935     /**
10936      * Enables a {@link Roo.TabPanelItem} that is disabled.
10937      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10938      */
10939     enableTab : function(id){
10940         var tab = this.items[id];
10941         tab.enable();
10942     },
10943
10944     /**
10945      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10946      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10947      * @return {Roo.TabPanelItem} The TabPanelItem.
10948      */
10949     activate : function(id){
10950         var tab = this.items[id];
10951         if(!tab){
10952             return null;
10953         }
10954         if(tab == this.active || tab.disabled){
10955             return tab;
10956         }
10957         var e = {};
10958         this.fireEvent("beforetabchange", this, e, tab);
10959         if(e.cancel !== true && !tab.disabled){
10960             if(this.active){
10961                 this.active.hide();
10962             }
10963             this.active = this.items[id];
10964             this.active.show();
10965             this.fireEvent("tabchange", this, this.active);
10966         }
10967         return tab;
10968     },
10969
10970     /**
10971      * Gets the active {@link Roo.TabPanelItem}.
10972      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10973      */
10974     getActiveTab : function(){
10975         return this.active;
10976     },
10977
10978     /**
10979      * Updates the tab body element to fit the height of the container element
10980      * for overflow scrolling
10981      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10982      */
10983     syncHeight : function(targetHeight){
10984         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10985         var bm = this.bodyEl.getMargins();
10986         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10987         this.bodyEl.setHeight(newHeight);
10988         return newHeight;
10989     },
10990
10991     onResize : function(){
10992         if(this.monitorResize){
10993             this.autoSizeTabs();
10994         }
10995     },
10996
10997     /**
10998      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10999      */
11000     beginUpdate : function(){
11001         this.updating = true;
11002     },
11003
11004     /**
11005      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11006      */
11007     endUpdate : function(){
11008         this.updating = false;
11009         this.autoSizeTabs();
11010     },
11011
11012     /**
11013      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11014      */
11015     autoSizeTabs : function(){
11016         var count = this.items.length;
11017         var vcount = count - this.hiddenCount;
11018         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11019         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11020         var availWidth = Math.floor(w / vcount);
11021         var b = this.stripBody;
11022         if(b.getWidth() > w){
11023             var tabs = this.items;
11024             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11025             if(availWidth < this.minTabWidth){
11026                 /*if(!this.sleft){    // incomplete scrolling code
11027                     this.createScrollButtons();
11028                 }
11029                 this.showScroll();
11030                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11031             }
11032         }else{
11033             if(this.currentTabWidth < this.preferredTabWidth){
11034                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11035             }
11036         }
11037     },
11038
11039     /**
11040      * Returns the number of tabs in this TabPanel.
11041      * @return {Number}
11042      */
11043      getCount : function(){
11044          return this.items.length;
11045      },
11046
11047     /**
11048      * Resizes all the tabs to the passed width
11049      * @param {Number} The new width
11050      */
11051     setTabWidth : function(width){
11052         this.currentTabWidth = width;
11053         for(var i = 0, len = this.items.length; i < len; i++) {
11054                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11055         }
11056     },
11057
11058     /**
11059      * Destroys this TabPanel
11060      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11061      */
11062     destroy : function(removeEl){
11063         Roo.EventManager.removeResizeListener(this.onResize, this);
11064         for(var i = 0, len = this.items.length; i < len; i++){
11065             this.items[i].purgeListeners();
11066         }
11067         if(removeEl === true){
11068             this.el.update("");
11069             this.el.remove();
11070         }
11071     }
11072 });
11073
11074 /**
11075  * @class Roo.TabPanelItem
11076  * @extends Roo.util.Observable
11077  * Represents an individual item (tab plus body) in a TabPanel.
11078  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11079  * @param {String} id The id of this TabPanelItem
11080  * @param {String} text The text for the tab of this TabPanelItem
11081  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11082  */
11083 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11084     /**
11085      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11086      * @type Roo.TabPanel
11087      */
11088     this.tabPanel = tabPanel;
11089     /**
11090      * The id for this TabPanelItem
11091      * @type String
11092      */
11093     this.id = id;
11094     /** @private */
11095     this.disabled = false;
11096     /** @private */
11097     this.text = text;
11098     /** @private */
11099     this.loaded = false;
11100     this.closable = closable;
11101
11102     /**
11103      * The body element for this TabPanelItem.
11104      * @type Roo.Element
11105      */
11106     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11107     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11108     this.bodyEl.setStyle("display", "block");
11109     this.bodyEl.setStyle("zoom", "1");
11110     this.hideAction();
11111
11112     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11113     /** @private */
11114     this.el = Roo.get(els.el, true);
11115     this.inner = Roo.get(els.inner, true);
11116     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11117     this.pnode = Roo.get(els.el.parentNode, true);
11118     this.el.on("mousedown", this.onTabMouseDown, this);
11119     this.el.on("click", this.onTabClick, this);
11120     /** @private */
11121     if(closable){
11122         var c = Roo.get(els.close, true);
11123         c.dom.title = this.closeText;
11124         c.addClassOnOver("close-over");
11125         c.on("click", this.closeClick, this);
11126      }
11127
11128     this.addEvents({
11129          /**
11130          * @event activate
11131          * Fires when this tab becomes the active tab.
11132          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11133          * @param {Roo.TabPanelItem} this
11134          */
11135         "activate": true,
11136         /**
11137          * @event beforeclose
11138          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11139          * @param {Roo.TabPanelItem} this
11140          * @param {Object} e Set cancel to true on this object to cancel the close.
11141          */
11142         "beforeclose": true,
11143         /**
11144          * @event close
11145          * Fires when this tab is closed.
11146          * @param {Roo.TabPanelItem} this
11147          */
11148          "close": true,
11149         /**
11150          * @event deactivate
11151          * Fires when this tab is no longer the active tab.
11152          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11153          * @param {Roo.TabPanelItem} this
11154          */
11155          "deactivate" : true
11156     });
11157     this.hidden = false;
11158
11159     Roo.TabPanelItem.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11163     purgeListeners : function(){
11164        Roo.util.Observable.prototype.purgeListeners.call(this);
11165        this.el.removeAllListeners();
11166     },
11167     /**
11168      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11169      */
11170     show : function(){
11171         this.pnode.addClass("on");
11172         this.showAction();
11173         if(Roo.isOpera){
11174             this.tabPanel.stripWrap.repaint();
11175         }
11176         this.fireEvent("activate", this.tabPanel, this);
11177     },
11178
11179     /**
11180      * Returns true if this tab is the active tab.
11181      * @return {Boolean}
11182      */
11183     isActive : function(){
11184         return this.tabPanel.getActiveTab() == this;
11185     },
11186
11187     /**
11188      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11189      */
11190     hide : function(){
11191         this.pnode.removeClass("on");
11192         this.hideAction();
11193         this.fireEvent("deactivate", this.tabPanel, this);
11194     },
11195
11196     hideAction : function(){
11197         this.bodyEl.hide();
11198         this.bodyEl.setStyle("position", "absolute");
11199         this.bodyEl.setLeft("-20000px");
11200         this.bodyEl.setTop("-20000px");
11201     },
11202
11203     showAction : function(){
11204         this.bodyEl.setStyle("position", "relative");
11205         this.bodyEl.setTop("");
11206         this.bodyEl.setLeft("");
11207         this.bodyEl.show();
11208     },
11209
11210     /**
11211      * Set the tooltip for the tab.
11212      * @param {String} tooltip The tab's tooltip
11213      */
11214     setTooltip : function(text){
11215         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11216             this.textEl.dom.qtip = text;
11217             this.textEl.dom.removeAttribute('title');
11218         }else{
11219             this.textEl.dom.title = text;
11220         }
11221     },
11222
11223     onTabClick : function(e){
11224         e.preventDefault();
11225         this.tabPanel.activate(this.id);
11226     },
11227
11228     onTabMouseDown : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     getWidth : function(){
11234         return this.inner.getWidth();
11235     },
11236
11237     setWidth : function(width){
11238         var iwidth = width - this.pnode.getPadding("lr");
11239         this.inner.setWidth(iwidth);
11240         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11241         this.pnode.setWidth(width);
11242     },
11243
11244     /**
11245      * Show or hide the tab
11246      * @param {Boolean} hidden True to hide or false to show.
11247      */
11248     setHidden : function(hidden){
11249         this.hidden = hidden;
11250         this.pnode.setStyle("display", hidden ? "none" : "");
11251     },
11252
11253     /**
11254      * Returns true if this tab is "hidden"
11255      * @return {Boolean}
11256      */
11257     isHidden : function(){
11258         return this.hidden;
11259     },
11260
11261     /**
11262      * Returns the text for this tab
11263      * @return {String}
11264      */
11265     getText : function(){
11266         return this.text;
11267     },
11268
11269     autoSize : function(){
11270         //this.el.beginMeasure();
11271         this.textEl.setWidth(1);
11272         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11273         //this.el.endMeasure();
11274     },
11275
11276     /**
11277      * Sets the text for the tab (Note: this also sets the tooltip text)
11278      * @param {String} text The tab's text and tooltip
11279      */
11280     setText : function(text){
11281         this.text = text;
11282         this.textEl.update(text);
11283         this.setTooltip(text);
11284         if(!this.tabPanel.resizeTabs){
11285             this.autoSize();
11286         }
11287     },
11288     /**
11289      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11290      */
11291     activate : function(){
11292         this.tabPanel.activate(this.id);
11293     },
11294
11295     /**
11296      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11297      */
11298     disable : function(){
11299         if(this.tabPanel.active != this){
11300             this.disabled = true;
11301             this.pnode.addClass("disabled");
11302         }
11303     },
11304
11305     /**
11306      * Enables this TabPanelItem if it was previously disabled.
11307      */
11308     enable : function(){
11309         this.disabled = false;
11310         this.pnode.removeClass("disabled");
11311     },
11312
11313     /**
11314      * Sets the content for this TabPanelItem.
11315      * @param {String} content The content
11316      * @param {Boolean} loadScripts true to look for and load scripts
11317      */
11318     setContent : function(content, loadScripts){
11319         this.bodyEl.update(content, loadScripts);
11320     },
11321
11322     /**
11323      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11324      * @return {Roo.UpdateManager} The UpdateManager
11325      */
11326     getUpdateManager : function(){
11327         return this.bodyEl.getUpdateManager();
11328     },
11329
11330     /**
11331      * Set a URL to be used to load the content for this TabPanelItem.
11332      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11333      * @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)
11334      * @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)
11335      * @return {Roo.UpdateManager} The UpdateManager
11336      */
11337     setUrl : function(url, params, loadOnce){
11338         if(this.refreshDelegate){
11339             this.un('activate', this.refreshDelegate);
11340         }
11341         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11342         this.on("activate", this.refreshDelegate);
11343         return this.bodyEl.getUpdateManager();
11344     },
11345
11346     /** @private */
11347     _handleRefresh : function(url, params, loadOnce){
11348         if(!loadOnce || !this.loaded){
11349             var updater = this.bodyEl.getUpdateManager();
11350             updater.update(url, params, this._setLoaded.createDelegate(this));
11351         }
11352     },
11353
11354     /**
11355      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11356      *   Will fail silently if the setUrl method has not been called.
11357      *   This does not activate the panel, just updates its content.
11358      */
11359     refresh : function(){
11360         if(this.refreshDelegate){
11361            this.loaded = false;
11362            this.refreshDelegate();
11363         }
11364     },
11365
11366     /** @private */
11367     _setLoaded : function(){
11368         this.loaded = true;
11369     },
11370
11371     /** @private */
11372     closeClick : function(e){
11373         var o = {};
11374         e.stopEvent();
11375         this.fireEvent("beforeclose", this, o);
11376         if(o.cancel !== true){
11377             this.tabPanel.removeTab(this.id);
11378         }
11379     },
11380     /**
11381      * The text displayed in the tooltip for the close icon.
11382      * @type String
11383      */
11384     closeText : "Close this tab"
11385 });
11386
11387 /** @private */
11388 Roo.TabPanel.prototype.createStrip = function(container){
11389     var strip = document.createElement("div");
11390     strip.className = "x-tabs-wrap";
11391     container.appendChild(strip);
11392     return strip;
11393 };
11394 /** @private */
11395 Roo.TabPanel.prototype.createStripList = function(strip){
11396     // div wrapper for retard IE
11397     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>';
11398     return strip.firstChild.firstChild.firstChild.firstChild;
11399 };
11400 /** @private */
11401 Roo.TabPanel.prototype.createBody = function(container){
11402     var body = document.createElement("div");
11403     Roo.id(body, "tab-body");
11404     Roo.fly(body).addClass("x-tabs-body");
11405     container.appendChild(body);
11406     return body;
11407 };
11408 /** @private */
11409 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11410     var body = Roo.getDom(id);
11411     if(!body){
11412         body = document.createElement("div");
11413         body.id = id;
11414     }
11415     Roo.fly(body).addClass("x-tabs-item-body");
11416     bodyEl.insertBefore(body, bodyEl.firstChild);
11417     return body;
11418 };
11419 /** @private */
11420 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11421     var td = document.createElement("td");
11422     stripEl.appendChild(td);
11423     if(closable){
11424         td.className = "x-tabs-closable";
11425         if(!this.closeTpl){
11426             this.closeTpl = new Roo.Template(
11427                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11428                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11429                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11430             );
11431         }
11432         var el = this.closeTpl.overwrite(td, {"text": text});
11433         var close = el.getElementsByTagName("div")[0];
11434         var inner = el.getElementsByTagName("em")[0];
11435         return {"el": el, "close": close, "inner": inner};
11436     } else {
11437         if(!this.tabTpl){
11438             this.tabTpl = new Roo.Template(
11439                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11440                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11441             );
11442         }
11443         var el = this.tabTpl.overwrite(td, {"text": text});
11444         var inner = el.getElementsByTagName("em")[0];
11445         return {"el": el, "inner": inner};
11446     }
11447 };/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458 /**
11459  * @class Roo.Button
11460  * @extends Roo.util.Observable
11461  * Simple Button class
11462  * @cfg {String} text The button text
11463  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11464  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11465  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11466  * @cfg {Object} scope The scope of the handler
11467  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11468  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11469  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11470  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11471  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11472  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11473    applies if enableToggle = true)
11474  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11475  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11476   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11477  * @constructor
11478  * Create a new button
11479  * @param {Object} config The config object
11480  */
11481 Roo.Button = function(renderTo, config)
11482 {
11483     if (!config) {
11484         config = renderTo;
11485         renderTo = config.renderTo || false;
11486     }
11487     
11488     Roo.apply(this, config);
11489     this.addEvents({
11490         /**
11491              * @event click
11492              * Fires when this button is clicked
11493              * @param {Button} this
11494              * @param {EventObject} e The click event
11495              */
11496             "click" : true,
11497         /**
11498              * @event toggle
11499              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11500              * @param {Button} this
11501              * @param {Boolean} pressed
11502              */
11503             "toggle" : true,
11504         /**
11505              * @event mouseover
11506              * Fires when the mouse hovers over the button
11507              * @param {Button} this
11508              * @param {Event} e The event object
11509              */
11510         'mouseover' : true,
11511         /**
11512              * @event mouseout
11513              * Fires when the mouse exits the button
11514              * @param {Button} this
11515              * @param {Event} e The event object
11516              */
11517         'mouseout': true,
11518          /**
11519              * @event render
11520              * Fires when the button is rendered
11521              * @param {Button} this
11522              */
11523         'render': true
11524     });
11525     if(this.menu){
11526         this.menu = Roo.menu.MenuMgr.get(this.menu);
11527     }
11528     // register listeners first!!  - so render can be captured..
11529     Roo.util.Observable.call(this);
11530     if(renderTo){
11531         this.render(renderTo);
11532     }
11533     
11534   
11535 };
11536
11537 Roo.extend(Roo.Button, Roo.util.Observable, {
11538     /**
11539      * 
11540      */
11541     
11542     /**
11543      * Read-only. True if this button is hidden
11544      * @type Boolean
11545      */
11546     hidden : false,
11547     /**
11548      * Read-only. True if this button is disabled
11549      * @type Boolean
11550      */
11551     disabled : false,
11552     /**
11553      * Read-only. True if this button is pressed (only if enableToggle = true)
11554      * @type Boolean
11555      */
11556     pressed : false,
11557
11558     /**
11559      * @cfg {Number} tabIndex 
11560      * The DOM tabIndex for this button (defaults to undefined)
11561      */
11562     tabIndex : undefined,
11563
11564     /**
11565      * @cfg {Boolean} enableToggle
11566      * True to enable pressed/not pressed toggling (defaults to false)
11567      */
11568     enableToggle: false,
11569     /**
11570      * @cfg {Mixed} menu
11571      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11572      */
11573     menu : undefined,
11574     /**
11575      * @cfg {String} menuAlign
11576      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11577      */
11578     menuAlign : "tl-bl?",
11579
11580     /**
11581      * @cfg {String} iconCls
11582      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11583      */
11584     iconCls : undefined,
11585     /**
11586      * @cfg {String} type
11587      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11588      */
11589     type : 'button',
11590
11591     // private
11592     menuClassTarget: 'tr',
11593
11594     /**
11595      * @cfg {String} clickEvent
11596      * The type of event to map to the button's event handler (defaults to 'click')
11597      */
11598     clickEvent : 'click',
11599
11600     /**
11601      * @cfg {Boolean} handleMouseEvents
11602      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11603      */
11604     handleMouseEvents : true,
11605
11606     /**
11607      * @cfg {String} tooltipType
11608      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11609      */
11610     tooltipType : 'qtip',
11611
11612     /**
11613      * @cfg {String} cls
11614      * A CSS class to apply to the button's main element.
11615      */
11616     
11617     /**
11618      * @cfg {Roo.Template} template (Optional)
11619      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11620      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11621      * require code modifications if required elements (e.g. a button) aren't present.
11622      */
11623
11624     // private
11625     render : function(renderTo){
11626         var btn;
11627         if(this.hideParent){
11628             this.parentEl = Roo.get(renderTo);
11629         }
11630         if(!this.dhconfig){
11631             if(!this.template){
11632                 if(!Roo.Button.buttonTemplate){
11633                     // hideous table template
11634                     Roo.Button.buttonTemplate = new Roo.Template(
11635                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11636                         '<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>',
11637                         "</tr></tbody></table>");
11638                 }
11639                 this.template = Roo.Button.buttonTemplate;
11640             }
11641             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11642             var btnEl = btn.child("button:first");
11643             btnEl.on('focus', this.onFocus, this);
11644             btnEl.on('blur', this.onBlur, this);
11645             if(this.cls){
11646                 btn.addClass(this.cls);
11647             }
11648             if(this.icon){
11649                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11650             }
11651             if(this.iconCls){
11652                 btnEl.addClass(this.iconCls);
11653                 if(!this.cls){
11654                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11655                 }
11656             }
11657             if(this.tabIndex !== undefined){
11658                 btnEl.dom.tabIndex = this.tabIndex;
11659             }
11660             if(this.tooltip){
11661                 if(typeof this.tooltip == 'object'){
11662                     Roo.QuickTips.tips(Roo.apply({
11663                           target: btnEl.id
11664                     }, this.tooltip));
11665                 } else {
11666                     btnEl.dom[this.tooltipType] = this.tooltip;
11667                 }
11668             }
11669         }else{
11670             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11671         }
11672         this.el = btn;
11673         if(this.id){
11674             this.el.dom.id = this.el.id = this.id;
11675         }
11676         if(this.menu){
11677             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11678             this.menu.on("show", this.onMenuShow, this);
11679             this.menu.on("hide", this.onMenuHide, this);
11680         }
11681         btn.addClass("x-btn");
11682         if(Roo.isIE && !Roo.isIE7){
11683             this.autoWidth.defer(1, this);
11684         }else{
11685             this.autoWidth();
11686         }
11687         if(this.handleMouseEvents){
11688             btn.on("mouseover", this.onMouseOver, this);
11689             btn.on("mouseout", this.onMouseOut, this);
11690             btn.on("mousedown", this.onMouseDown, this);
11691         }
11692         btn.on(this.clickEvent, this.onClick, this);
11693         //btn.on("mouseup", this.onMouseUp, this);
11694         if(this.hidden){
11695             this.hide();
11696         }
11697         if(this.disabled){
11698             this.disable();
11699         }
11700         Roo.ButtonToggleMgr.register(this);
11701         if(this.pressed){
11702             this.el.addClass("x-btn-pressed");
11703         }
11704         if(this.repeat){
11705             var repeater = new Roo.util.ClickRepeater(btn,
11706                 typeof this.repeat == "object" ? this.repeat : {}
11707             );
11708             repeater.on("click", this.onClick,  this);
11709         }
11710         
11711         this.fireEvent('render', this);
11712         
11713     },
11714     /**
11715      * Returns the button's underlying element
11716      * @return {Roo.Element} The element
11717      */
11718     getEl : function(){
11719         return this.el;  
11720     },
11721     
11722     /**
11723      * Destroys this Button and removes any listeners.
11724      */
11725     destroy : function(){
11726         Roo.ButtonToggleMgr.unregister(this);
11727         this.el.removeAllListeners();
11728         this.purgeListeners();
11729         this.el.remove();
11730     },
11731
11732     // private
11733     autoWidth : function(){
11734         if(this.el){
11735             this.el.setWidth("auto");
11736             if(Roo.isIE7 && Roo.isStrict){
11737                 var ib = this.el.child('button');
11738                 if(ib && ib.getWidth() > 20){
11739                     ib.clip();
11740                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11741                 }
11742             }
11743             if(this.minWidth){
11744                 if(this.hidden){
11745                     this.el.beginMeasure();
11746                 }
11747                 if(this.el.getWidth() < this.minWidth){
11748                     this.el.setWidth(this.minWidth);
11749                 }
11750                 if(this.hidden){
11751                     this.el.endMeasure();
11752                 }
11753             }
11754         }
11755     },
11756
11757     /**
11758      * Assigns this button's click handler
11759      * @param {Function} handler The function to call when the button is clicked
11760      * @param {Object} scope (optional) Scope for the function passed in
11761      */
11762     setHandler : function(handler, scope){
11763         this.handler = handler;
11764         this.scope = scope;  
11765     },
11766     
11767     /**
11768      * Sets this button's text
11769      * @param {String} text The button text
11770      */
11771     setText : function(text){
11772         this.text = text;
11773         if(this.el){
11774             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11775         }
11776         this.autoWidth();
11777     },
11778     
11779     /**
11780      * Gets the text for this button
11781      * @return {String} The button text
11782      */
11783     getText : function(){
11784         return this.text;  
11785     },
11786     
11787     /**
11788      * Show this button
11789      */
11790     show: function(){
11791         this.hidden = false;
11792         if(this.el){
11793             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11794         }
11795     },
11796     
11797     /**
11798      * Hide this button
11799      */
11800     hide: function(){
11801         this.hidden = true;
11802         if(this.el){
11803             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11804         }
11805     },
11806     
11807     /**
11808      * Convenience function for boolean show/hide
11809      * @param {Boolean} visible True to show, false to hide
11810      */
11811     setVisible: function(visible){
11812         if(visible) {
11813             this.show();
11814         }else{
11815             this.hide();
11816         }
11817     },
11818     
11819     /**
11820      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11821      * @param {Boolean} state (optional) Force a particular state
11822      */
11823     toggle : function(state){
11824         state = state === undefined ? !this.pressed : state;
11825         if(state != this.pressed){
11826             if(state){
11827                 this.el.addClass("x-btn-pressed");
11828                 this.pressed = true;
11829                 this.fireEvent("toggle", this, true);
11830             }else{
11831                 this.el.removeClass("x-btn-pressed");
11832                 this.pressed = false;
11833                 this.fireEvent("toggle", this, false);
11834             }
11835             if(this.toggleHandler){
11836                 this.toggleHandler.call(this.scope || this, this, state);
11837             }
11838         }
11839     },
11840     
11841     /**
11842      * Focus the button
11843      */
11844     focus : function(){
11845         this.el.child('button:first').focus();
11846     },
11847     
11848     /**
11849      * Disable this button
11850      */
11851     disable : function(){
11852         if(this.el){
11853             this.el.addClass("x-btn-disabled");
11854         }
11855         this.disabled = true;
11856     },
11857     
11858     /**
11859      * Enable this button
11860      */
11861     enable : function(){
11862         if(this.el){
11863             this.el.removeClass("x-btn-disabled");
11864         }
11865         this.disabled = false;
11866     },
11867
11868     /**
11869      * Convenience function for boolean enable/disable
11870      * @param {Boolean} enabled True to enable, false to disable
11871      */
11872     setDisabled : function(v){
11873         this[v !== true ? "enable" : "disable"]();
11874     },
11875
11876     // private
11877     onClick : function(e){
11878         if(e){
11879             e.preventDefault();
11880         }
11881         if(e.button != 0){
11882             return;
11883         }
11884         if(!this.disabled){
11885             if(this.enableToggle){
11886                 this.toggle();
11887             }
11888             if(this.menu && !this.menu.isVisible()){
11889                 this.menu.show(this.el, this.menuAlign);
11890             }
11891             this.fireEvent("click", this, e);
11892             if(this.handler){
11893                 this.el.removeClass("x-btn-over");
11894                 this.handler.call(this.scope || this, this, e);
11895             }
11896         }
11897     },
11898     // private
11899     onMouseOver : function(e){
11900         if(!this.disabled){
11901             this.el.addClass("x-btn-over");
11902             this.fireEvent('mouseover', this, e);
11903         }
11904     },
11905     // private
11906     onMouseOut : function(e){
11907         if(!e.within(this.el,  true)){
11908             this.el.removeClass("x-btn-over");
11909             this.fireEvent('mouseout', this, e);
11910         }
11911     },
11912     // private
11913     onFocus : function(e){
11914         if(!this.disabled){
11915             this.el.addClass("x-btn-focus");
11916         }
11917     },
11918     // private
11919     onBlur : function(e){
11920         this.el.removeClass("x-btn-focus");
11921     },
11922     // private
11923     onMouseDown : function(e){
11924         if(!this.disabled && e.button == 0){
11925             this.el.addClass("x-btn-click");
11926             Roo.get(document).on('mouseup', this.onMouseUp, this);
11927         }
11928     },
11929     // private
11930     onMouseUp : function(e){
11931         if(e.button == 0){
11932             this.el.removeClass("x-btn-click");
11933             Roo.get(document).un('mouseup', this.onMouseUp, this);
11934         }
11935     },
11936     // private
11937     onMenuShow : function(e){
11938         this.el.addClass("x-btn-menu-active");
11939     },
11940     // private
11941     onMenuHide : function(e){
11942         this.el.removeClass("x-btn-menu-active");
11943     }   
11944 });
11945
11946 // Private utility class used by Button
11947 Roo.ButtonToggleMgr = function(){
11948    var groups = {};
11949    
11950    function toggleGroup(btn, state){
11951        if(state){
11952            var g = groups[btn.toggleGroup];
11953            for(var i = 0, l = g.length; i < l; i++){
11954                if(g[i] != btn){
11955                    g[i].toggle(false);
11956                }
11957            }
11958        }
11959    }
11960    
11961    return {
11962        register : function(btn){
11963            if(!btn.toggleGroup){
11964                return;
11965            }
11966            var g = groups[btn.toggleGroup];
11967            if(!g){
11968                g = groups[btn.toggleGroup] = [];
11969            }
11970            g.push(btn);
11971            btn.on("toggle", toggleGroup);
11972        },
11973        
11974        unregister : function(btn){
11975            if(!btn.toggleGroup){
11976                return;
11977            }
11978            var g = groups[btn.toggleGroup];
11979            if(g){
11980                g.remove(btn);
11981                btn.un("toggle", toggleGroup);
11982            }
11983        }
11984    };
11985 }();/*
11986  * Based on:
11987  * Ext JS Library 1.1.1
11988  * Copyright(c) 2006-2007, Ext JS, LLC.
11989  *
11990  * Originally Released Under LGPL - original licence link has changed is not relivant.
11991  *
11992  * Fork - LGPL
11993  * <script type="text/javascript">
11994  */
11995  
11996 /**
11997  * @class Roo.SplitButton
11998  * @extends Roo.Button
11999  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12000  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12001  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12002  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12003  * @cfg {String} arrowTooltip The title attribute of the arrow
12004  * @constructor
12005  * Create a new menu button
12006  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12007  * @param {Object} config The config object
12008  */
12009 Roo.SplitButton = function(renderTo, config){
12010     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12011     /**
12012      * @event arrowclick
12013      * Fires when this button's arrow is clicked
12014      * @param {SplitButton} this
12015      * @param {EventObject} e The click event
12016      */
12017     this.addEvents({"arrowclick":true});
12018 };
12019
12020 Roo.extend(Roo.SplitButton, Roo.Button, {
12021     render : function(renderTo){
12022         // this is one sweet looking template!
12023         var tpl = new Roo.Template(
12024             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12025             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12026             '<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>',
12027             "</tbody></table></td><td>",
12028             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12029             '<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>',
12030             "</tbody></table></td></tr></table>"
12031         );
12032         var btn = tpl.append(renderTo, [this.text, this.type], true);
12033         var btnEl = btn.child("button");
12034         if(this.cls){
12035             btn.addClass(this.cls);
12036         }
12037         if(this.icon){
12038             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12039         }
12040         if(this.iconCls){
12041             btnEl.addClass(this.iconCls);
12042             if(!this.cls){
12043                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12044             }
12045         }
12046         this.el = btn;
12047         if(this.handleMouseEvents){
12048             btn.on("mouseover", this.onMouseOver, this);
12049             btn.on("mouseout", this.onMouseOut, this);
12050             btn.on("mousedown", this.onMouseDown, this);
12051             btn.on("mouseup", this.onMouseUp, this);
12052         }
12053         btn.on(this.clickEvent, this.onClick, this);
12054         if(this.tooltip){
12055             if(typeof this.tooltip == 'object'){
12056                 Roo.QuickTips.tips(Roo.apply({
12057                       target: btnEl.id
12058                 }, this.tooltip));
12059             } else {
12060                 btnEl.dom[this.tooltipType] = this.tooltip;
12061             }
12062         }
12063         if(this.arrowTooltip){
12064             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12065         }
12066         if(this.hidden){
12067             this.hide();
12068         }
12069         if(this.disabled){
12070             this.disable();
12071         }
12072         if(this.pressed){
12073             this.el.addClass("x-btn-pressed");
12074         }
12075         if(Roo.isIE && !Roo.isIE7){
12076             this.autoWidth.defer(1, this);
12077         }else{
12078             this.autoWidth();
12079         }
12080         if(this.menu){
12081             this.menu.on("show", this.onMenuShow, this);
12082             this.menu.on("hide", this.onMenuHide, this);
12083         }
12084         this.fireEvent('render', this);
12085     },
12086
12087     // private
12088     autoWidth : function(){
12089         if(this.el){
12090             var tbl = this.el.child("table:first");
12091             var tbl2 = this.el.child("table:last");
12092             this.el.setWidth("auto");
12093             tbl.setWidth("auto");
12094             if(Roo.isIE7 && Roo.isStrict){
12095                 var ib = this.el.child('button:first');
12096                 if(ib && ib.getWidth() > 20){
12097                     ib.clip();
12098                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12099                 }
12100             }
12101             if(this.minWidth){
12102                 if(this.hidden){
12103                     this.el.beginMeasure();
12104                 }
12105                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12106                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12107                 }
12108                 if(this.hidden){
12109                     this.el.endMeasure();
12110                 }
12111             }
12112             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12113         } 
12114     },
12115     /**
12116      * Sets this button's click handler
12117      * @param {Function} handler The function to call when the button is clicked
12118      * @param {Object} scope (optional) Scope for the function passed above
12119      */
12120     setHandler : function(handler, scope){
12121         this.handler = handler;
12122         this.scope = scope;  
12123     },
12124     
12125     /**
12126      * Sets this button's arrow click handler
12127      * @param {Function} handler The function to call when the arrow is clicked
12128      * @param {Object} scope (optional) Scope for the function passed above
12129      */
12130     setArrowHandler : function(handler, scope){
12131         this.arrowHandler = handler;
12132         this.scope = scope;  
12133     },
12134     
12135     /**
12136      * Focus the button
12137      */
12138     focus : function(){
12139         if(this.el){
12140             this.el.child("button:first").focus();
12141         }
12142     },
12143
12144     // private
12145     onClick : function(e){
12146         e.preventDefault();
12147         if(!this.disabled){
12148             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12149                 if(this.menu && !this.menu.isVisible()){
12150                     this.menu.show(this.el, this.menuAlign);
12151                 }
12152                 this.fireEvent("arrowclick", this, e);
12153                 if(this.arrowHandler){
12154                     this.arrowHandler.call(this.scope || this, this, e);
12155                 }
12156             }else{
12157                 this.fireEvent("click", this, e);
12158                 if(this.handler){
12159                     this.handler.call(this.scope || this, this, e);
12160                 }
12161             }
12162         }
12163     },
12164     // private
12165     onMouseDown : function(e){
12166         if(!this.disabled){
12167             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12168         }
12169     },
12170     // private
12171     onMouseUp : function(e){
12172         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12173     }   
12174 });
12175
12176
12177 // backwards compat
12178 Roo.MenuButton = Roo.SplitButton;/*
12179  * Based on:
12180  * Ext JS Library 1.1.1
12181  * Copyright(c) 2006-2007, Ext JS, LLC.
12182  *
12183  * Originally Released Under LGPL - original licence link has changed is not relivant.
12184  *
12185  * Fork - LGPL
12186  * <script type="text/javascript">
12187  */
12188
12189 /**
12190  * @class Roo.Toolbar
12191  * Basic Toolbar class.
12192  * @constructor
12193  * Creates a new Toolbar
12194  * @param {Object} config The config object
12195  */ 
12196 Roo.Toolbar = function(container, buttons, config)
12197 {
12198     /// old consturctor format still supported..
12199     if(container instanceof Array){ // omit the container for later rendering
12200         buttons = container;
12201         config = buttons;
12202         container = null;
12203     }
12204     if (typeof(container) == 'object' && container.xtype) {
12205         config = container;
12206         container = config.container;
12207         buttons = config.buttons; // not really - use items!!
12208     }
12209     var xitems = [];
12210     if (config && config.items) {
12211         xitems = config.items;
12212         delete config.items;
12213     }
12214     Roo.apply(this, config);
12215     this.buttons = buttons;
12216     
12217     if(container){
12218         this.render(container);
12219     }
12220     Roo.each(xitems, function(b) {
12221         this.add(b);
12222     }, this);
12223     
12224 };
12225
12226 Roo.Toolbar.prototype = {
12227     /**
12228      * @cfg {Roo.data.Store} items
12229      * array of button configs or elements to add
12230      */
12231     
12232     /**
12233      * @cfg {String/HTMLElement/Element} container
12234      * The id or element that will contain the toolbar
12235      */
12236     // private
12237     render : function(ct){
12238         this.el = Roo.get(ct);
12239         if(this.cls){
12240             this.el.addClass(this.cls);
12241         }
12242         // using a table allows for vertical alignment
12243         // 100% width is needed by Safari...
12244         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12245         this.tr = this.el.child("tr", true);
12246         var autoId = 0;
12247         this.items = new Roo.util.MixedCollection(false, function(o){
12248             return o.id || ("item" + (++autoId));
12249         });
12250         if(this.buttons){
12251             this.add.apply(this, this.buttons);
12252             delete this.buttons;
12253         }
12254     },
12255
12256     /**
12257      * Adds element(s) to the toolbar -- this function takes a variable number of 
12258      * arguments of mixed type and adds them to the toolbar.
12259      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12260      * <ul>
12261      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12262      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12263      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12264      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12265      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12266      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12267      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12268      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12269      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12270      * </ul>
12271      * @param {Mixed} arg2
12272      * @param {Mixed} etc.
12273      */
12274     add : function(){
12275         var a = arguments, l = a.length;
12276         for(var i = 0; i < l; i++){
12277             this._add(a[i]);
12278         }
12279     },
12280     // private..
12281     _add : function(el) {
12282         
12283         if (el.xtype) {
12284             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12285         }
12286         
12287         if (el.applyTo){ // some kind of form field
12288             return this.addField(el);
12289         } 
12290         if (el.render){ // some kind of Toolbar.Item
12291             return this.addItem(el);
12292         }
12293         if (typeof el == "string"){ // string
12294             if(el == "separator" || el == "-"){
12295                 return this.addSeparator();
12296             }
12297             if (el == " "){
12298                 return this.addSpacer();
12299             }
12300             if(el == "->"){
12301                 return this.addFill();
12302             }
12303             return this.addText(el);
12304             
12305         }
12306         if(el.tagName){ // element
12307             return this.addElement(el);
12308         }
12309         if(typeof el == "object"){ // must be button config?
12310             return this.addButton(el);
12311         }
12312         // and now what?!?!
12313         return false;
12314         
12315     },
12316     
12317     /**
12318      * Add an Xtype element
12319      * @param {Object} xtype Xtype Object
12320      * @return {Object} created Object
12321      */
12322     addxtype : function(e){
12323         return this.add(e);  
12324     },
12325     
12326     /**
12327      * Returns the Element for this toolbar.
12328      * @return {Roo.Element}
12329      */
12330     getEl : function(){
12331         return this.el;  
12332     },
12333     
12334     /**
12335      * Adds a separator
12336      * @return {Roo.Toolbar.Item} The separator item
12337      */
12338     addSeparator : function(){
12339         return this.addItem(new Roo.Toolbar.Separator());
12340     },
12341
12342     /**
12343      * Adds a spacer element
12344      * @return {Roo.Toolbar.Spacer} The spacer item
12345      */
12346     addSpacer : function(){
12347         return this.addItem(new Roo.Toolbar.Spacer());
12348     },
12349
12350     /**
12351      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12352      * @return {Roo.Toolbar.Fill} The fill item
12353      */
12354     addFill : function(){
12355         return this.addItem(new Roo.Toolbar.Fill());
12356     },
12357
12358     /**
12359      * Adds any standard HTML element to the toolbar
12360      * @param {String/HTMLElement/Element} el The element or id of the element to add
12361      * @return {Roo.Toolbar.Item} The element's item
12362      */
12363     addElement : function(el){
12364         return this.addItem(new Roo.Toolbar.Item(el));
12365     },
12366     /**
12367      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12368      * @type Roo.util.MixedCollection  
12369      */
12370     items : false,
12371      
12372     /**
12373      * Adds any Toolbar.Item or subclass
12374      * @param {Roo.Toolbar.Item} item
12375      * @return {Roo.Toolbar.Item} The item
12376      */
12377     addItem : function(item){
12378         var td = this.nextBlock();
12379         item.render(td);
12380         this.items.add(item);
12381         return item;
12382     },
12383     
12384     /**
12385      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12386      * @param {Object/Array} config A button config or array of configs
12387      * @return {Roo.Toolbar.Button/Array}
12388      */
12389     addButton : function(config){
12390         if(config instanceof Array){
12391             var buttons = [];
12392             for(var i = 0, len = config.length; i < len; i++) {
12393                 buttons.push(this.addButton(config[i]));
12394             }
12395             return buttons;
12396         }
12397         var b = config;
12398         if(!(config instanceof Roo.Toolbar.Button)){
12399             b = config.split ?
12400                 new Roo.Toolbar.SplitButton(config) :
12401                 new Roo.Toolbar.Button(config);
12402         }
12403         var td = this.nextBlock();
12404         b.render(td);
12405         this.items.add(b);
12406         return b;
12407     },
12408     
12409     /**
12410      * Adds text to the toolbar
12411      * @param {String} text The text to add
12412      * @return {Roo.Toolbar.Item} The element's item
12413      */
12414     addText : function(text){
12415         return this.addItem(new Roo.Toolbar.TextItem(text));
12416     },
12417     
12418     /**
12419      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12420      * @param {Number} index The index where the item is to be inserted
12421      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12422      * @return {Roo.Toolbar.Button/Item}
12423      */
12424     insertButton : function(index, item){
12425         if(item instanceof Array){
12426             var buttons = [];
12427             for(var i = 0, len = item.length; i < len; i++) {
12428                buttons.push(this.insertButton(index + i, item[i]));
12429             }
12430             return buttons;
12431         }
12432         if (!(item instanceof Roo.Toolbar.Button)){
12433            item = new Roo.Toolbar.Button(item);
12434         }
12435         var td = document.createElement("td");
12436         this.tr.insertBefore(td, this.tr.childNodes[index]);
12437         item.render(td);
12438         this.items.insert(index, item);
12439         return item;
12440     },
12441     
12442     /**
12443      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12444      * @param {Object} config
12445      * @return {Roo.Toolbar.Item} The element's item
12446      */
12447     addDom : function(config, returnEl){
12448         var td = this.nextBlock();
12449         Roo.DomHelper.overwrite(td, config);
12450         var ti = new Roo.Toolbar.Item(td.firstChild);
12451         ti.render(td);
12452         this.items.add(ti);
12453         return ti;
12454     },
12455
12456     /**
12457      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12458      * @type Roo.util.MixedCollection  
12459      */
12460     fields : false,
12461     
12462     /**
12463      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12464      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12465      * @param {Roo.form.Field} field
12466      * @return {Roo.ToolbarItem}
12467      */
12468      
12469       
12470     addField : function(field) {
12471         if (!this.fields) {
12472             var autoId = 0;
12473             this.fields = new Roo.util.MixedCollection(false, function(o){
12474                 return o.id || ("item" + (++autoId));
12475             });
12476
12477         }
12478         
12479         var td = this.nextBlock();
12480         field.render(td);
12481         var ti = new Roo.Toolbar.Item(td.firstChild);
12482         ti.render(td);
12483         this.items.add(ti);
12484         this.fields.add(field);
12485         return ti;
12486     },
12487     /**
12488      * Hide the toolbar
12489      * @method hide
12490      */
12491      
12492       
12493     hide : function()
12494     {
12495         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12496         this.el.child('div').hide();
12497     },
12498     /**
12499      * Show the toolbar
12500      * @method show
12501      */
12502     show : function()
12503     {
12504         this.el.child('div').show();
12505     },
12506       
12507     // private
12508     nextBlock : function(){
12509         var td = document.createElement("td");
12510         this.tr.appendChild(td);
12511         return td;
12512     },
12513
12514     // private
12515     destroy : function(){
12516         if(this.items){ // rendered?
12517             Roo.destroy.apply(Roo, this.items.items);
12518         }
12519         if(this.fields){ // rendered?
12520             Roo.destroy.apply(Roo, this.fields.items);
12521         }
12522         Roo.Element.uncache(this.el, this.tr);
12523     }
12524 };
12525
12526 /**
12527  * @class Roo.Toolbar.Item
12528  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12529  * @constructor
12530  * Creates a new Item
12531  * @param {HTMLElement} el 
12532  */
12533 Roo.Toolbar.Item = function(el){
12534     this.el = Roo.getDom(el);
12535     this.id = Roo.id(this.el);
12536     this.hidden = false;
12537 };
12538
12539 Roo.Toolbar.Item.prototype = {
12540     
12541     /**
12542      * Get this item's HTML Element
12543      * @return {HTMLElement}
12544      */
12545     getEl : function(){
12546        return this.el;  
12547     },
12548
12549     // private
12550     render : function(td){
12551         this.td = td;
12552         td.appendChild(this.el);
12553     },
12554     
12555     /**
12556      * Removes and destroys this item.
12557      */
12558     destroy : function(){
12559         this.td.parentNode.removeChild(this.td);
12560     },
12561     
12562     /**
12563      * Shows this item.
12564      */
12565     show: function(){
12566         this.hidden = false;
12567         this.td.style.display = "";
12568     },
12569     
12570     /**
12571      * Hides this item.
12572      */
12573     hide: function(){
12574         this.hidden = true;
12575         this.td.style.display = "none";
12576     },
12577     
12578     /**
12579      * Convenience function for boolean show/hide.
12580      * @param {Boolean} visible true to show/false to hide
12581      */
12582     setVisible: function(visible){
12583         if(visible) {
12584             this.show();
12585         }else{
12586             this.hide();
12587         }
12588     },
12589     
12590     /**
12591      * Try to focus this item.
12592      */
12593     focus : function(){
12594         Roo.fly(this.el).focus();
12595     },
12596     
12597     /**
12598      * Disables this item.
12599      */
12600     disable : function(){
12601         Roo.fly(this.td).addClass("x-item-disabled");
12602         this.disabled = true;
12603         this.el.disabled = true;
12604     },
12605     
12606     /**
12607      * Enables this item.
12608      */
12609     enable : function(){
12610         Roo.fly(this.td).removeClass("x-item-disabled");
12611         this.disabled = false;
12612         this.el.disabled = false;
12613     }
12614 };
12615
12616
12617 /**
12618  * @class Roo.Toolbar.Separator
12619  * @extends Roo.Toolbar.Item
12620  * A simple toolbar separator class
12621  * @constructor
12622  * Creates a new Separator
12623  */
12624 Roo.Toolbar.Separator = function(){
12625     var s = document.createElement("span");
12626     s.className = "ytb-sep";
12627     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12628 };
12629 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12630     enable:Roo.emptyFn,
12631     disable:Roo.emptyFn,
12632     focus:Roo.emptyFn
12633 });
12634
12635 /**
12636  * @class Roo.Toolbar.Spacer
12637  * @extends Roo.Toolbar.Item
12638  * A simple element that adds extra horizontal space to a toolbar.
12639  * @constructor
12640  * Creates a new Spacer
12641  */
12642 Roo.Toolbar.Spacer = function(){
12643     var s = document.createElement("div");
12644     s.className = "ytb-spacer";
12645     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12646 };
12647 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12648     enable:Roo.emptyFn,
12649     disable:Roo.emptyFn,
12650     focus:Roo.emptyFn
12651 });
12652
12653 /**
12654  * @class Roo.Toolbar.Fill
12655  * @extends Roo.Toolbar.Spacer
12656  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12657  * @constructor
12658  * Creates a new Spacer
12659  */
12660 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12661     // private
12662     render : function(td){
12663         td.style.width = '100%';
12664         Roo.Toolbar.Fill.superclass.render.call(this, td);
12665     }
12666 });
12667
12668 /**
12669  * @class Roo.Toolbar.TextItem
12670  * @extends Roo.Toolbar.Item
12671  * A simple class that renders text directly into a toolbar.
12672  * @constructor
12673  * Creates a new TextItem
12674  * @param {String} text
12675  */
12676 Roo.Toolbar.TextItem = function(text){
12677     if (typeof(text) == 'object') {
12678         text = text.text;
12679     }
12680     var s = document.createElement("span");
12681     s.className = "ytb-text";
12682     s.innerHTML = text;
12683     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12684 };
12685 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12686     enable:Roo.emptyFn,
12687     disable:Roo.emptyFn,
12688     focus:Roo.emptyFn
12689 });
12690
12691 /**
12692  * @class Roo.Toolbar.Button
12693  * @extends Roo.Button
12694  * A button that renders into a toolbar.
12695  * @constructor
12696  * Creates a new Button
12697  * @param {Object} config A standard {@link Roo.Button} config object
12698  */
12699 Roo.Toolbar.Button = function(config){
12700     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12701 };
12702 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12703     render : function(td){
12704         this.td = td;
12705         Roo.Toolbar.Button.superclass.render.call(this, td);
12706     },
12707     
12708     /**
12709      * Removes and destroys this button
12710      */
12711     destroy : function(){
12712         Roo.Toolbar.Button.superclass.destroy.call(this);
12713         this.td.parentNode.removeChild(this.td);
12714     },
12715     
12716     /**
12717      * Shows this button
12718      */
12719     show: function(){
12720         this.hidden = false;
12721         this.td.style.display = "";
12722     },
12723     
12724     /**
12725      * Hides this button
12726      */
12727     hide: function(){
12728         this.hidden = true;
12729         this.td.style.display = "none";
12730     },
12731
12732     /**
12733      * Disables this item
12734      */
12735     disable : function(){
12736         Roo.fly(this.td).addClass("x-item-disabled");
12737         this.disabled = true;
12738     },
12739
12740     /**
12741      * Enables this item
12742      */
12743     enable : function(){
12744         Roo.fly(this.td).removeClass("x-item-disabled");
12745         this.disabled = false;
12746     }
12747 });
12748 // backwards compat
12749 Roo.ToolbarButton = Roo.Toolbar.Button;
12750
12751 /**
12752  * @class Roo.Toolbar.SplitButton
12753  * @extends Roo.SplitButton
12754  * A menu button that renders into a toolbar.
12755  * @constructor
12756  * Creates a new SplitButton
12757  * @param {Object} config A standard {@link Roo.SplitButton} config object
12758  */
12759 Roo.Toolbar.SplitButton = function(config){
12760     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12761 };
12762 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12763     render : function(td){
12764         this.td = td;
12765         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12766     },
12767     
12768     /**
12769      * Removes and destroys this button
12770      */
12771     destroy : function(){
12772         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12773         this.td.parentNode.removeChild(this.td);
12774     },
12775     
12776     /**
12777      * Shows this button
12778      */
12779     show: function(){
12780         this.hidden = false;
12781         this.td.style.display = "";
12782     },
12783     
12784     /**
12785      * Hides this button
12786      */
12787     hide: function(){
12788         this.hidden = true;
12789         this.td.style.display = "none";
12790     }
12791 });
12792
12793 // backwards compat
12794 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12795  * Based on:
12796  * Ext JS Library 1.1.1
12797  * Copyright(c) 2006-2007, Ext JS, LLC.
12798  *
12799  * Originally Released Under LGPL - original licence link has changed is not relivant.
12800  *
12801  * Fork - LGPL
12802  * <script type="text/javascript">
12803  */
12804  
12805 /**
12806  * @class Roo.PagingToolbar
12807  * @extends Roo.Toolbar
12808  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12809  * @constructor
12810  * Create a new PagingToolbar
12811  * @param {Object} config The config object
12812  */
12813 Roo.PagingToolbar = function(el, ds, config)
12814 {
12815     // old args format still supported... - xtype is prefered..
12816     if (typeof(el) == 'object' && el.xtype) {
12817         // created from xtype...
12818         config = el;
12819         ds = el.dataSource;
12820         el = config.container;
12821     }
12822     var items = [];
12823     if (config.items) {
12824         items = config.items;
12825         config.items = [];
12826     }
12827     
12828     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12829     this.ds = ds;
12830     this.cursor = 0;
12831     this.renderButtons(this.el);
12832     this.bind(ds);
12833     
12834     // supprot items array.
12835    
12836     Roo.each(items, function(e) {
12837         this.add(Roo.factory(e));
12838     },this);
12839     
12840 };
12841
12842 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12843     /**
12844      * @cfg {Roo.data.Store} dataSource
12845      * The underlying data store providing the paged data
12846      */
12847     /**
12848      * @cfg {String/HTMLElement/Element} container
12849      * container The id or element that will contain the toolbar
12850      */
12851     /**
12852      * @cfg {Boolean} displayInfo
12853      * True to display the displayMsg (defaults to false)
12854      */
12855     /**
12856      * @cfg {Number} pageSize
12857      * The number of records to display per page (defaults to 20)
12858      */
12859     pageSize: 20,
12860     /**
12861      * @cfg {String} displayMsg
12862      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12863      */
12864     displayMsg : 'Displaying {0} - {1} of {2}',
12865     /**
12866      * @cfg {String} emptyMsg
12867      * The message to display when no records are found (defaults to "No data to display")
12868      */
12869     emptyMsg : 'No data to display',
12870     /**
12871      * Customizable piece of the default paging text (defaults to "Page")
12872      * @type String
12873      */
12874     beforePageText : "Page",
12875     /**
12876      * Customizable piece of the default paging text (defaults to "of %0")
12877      * @type String
12878      */
12879     afterPageText : "of {0}",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "First Page")
12882      * @type String
12883      */
12884     firstText : "First Page",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "Previous Page")
12887      * @type String
12888      */
12889     prevText : "Previous Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Next Page")
12892      * @type String
12893      */
12894     nextText : "Next Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Last Page")
12897      * @type String
12898      */
12899     lastText : "Last Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Refresh")
12902      * @type String
12903      */
12904     refreshText : "Refresh",
12905
12906     // private
12907     renderButtons : function(el){
12908         Roo.PagingToolbar.superclass.render.call(this, el);
12909         this.first = this.addButton({
12910             tooltip: this.firstText,
12911             cls: "x-btn-icon x-grid-page-first",
12912             disabled: true,
12913             handler: this.onClick.createDelegate(this, ["first"])
12914         });
12915         this.prev = this.addButton({
12916             tooltip: this.prevText,
12917             cls: "x-btn-icon x-grid-page-prev",
12918             disabled: true,
12919             handler: this.onClick.createDelegate(this, ["prev"])
12920         });
12921         //this.addSeparator();
12922         this.add(this.beforePageText);
12923         this.field = Roo.get(this.addDom({
12924            tag: "input",
12925            type: "text",
12926            size: "3",
12927            value: "1",
12928            cls: "x-grid-page-number"
12929         }).el);
12930         this.field.on("keydown", this.onPagingKeydown, this);
12931         this.field.on("focus", function(){this.dom.select();});
12932         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12933         this.field.setHeight(18);
12934         //this.addSeparator();
12935         this.next = this.addButton({
12936             tooltip: this.nextText,
12937             cls: "x-btn-icon x-grid-page-next",
12938             disabled: true,
12939             handler: this.onClick.createDelegate(this, ["next"])
12940         });
12941         this.last = this.addButton({
12942             tooltip: this.lastText,
12943             cls: "x-btn-icon x-grid-page-last",
12944             disabled: true,
12945             handler: this.onClick.createDelegate(this, ["last"])
12946         });
12947         //this.addSeparator();
12948         this.loading = this.addButton({
12949             tooltip: this.refreshText,
12950             cls: "x-btn-icon x-grid-loading",
12951             handler: this.onClick.createDelegate(this, ["refresh"])
12952         });
12953
12954         if(this.displayInfo){
12955             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12956         }
12957     },
12958
12959     // private
12960     updateInfo : function(){
12961         if(this.displayEl){
12962             var count = this.ds.getCount();
12963             var msg = count == 0 ?
12964                 this.emptyMsg :
12965                 String.format(
12966                     this.displayMsg,
12967                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12968                 );
12969             this.displayEl.update(msg);
12970         }
12971     },
12972
12973     // private
12974     onLoad : function(ds, r, o){
12975        this.cursor = o.params ? o.params.start : 0;
12976        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12977
12978        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12979        this.field.dom.value = ap;
12980        this.first.setDisabled(ap == 1);
12981        this.prev.setDisabled(ap == 1);
12982        this.next.setDisabled(ap == ps);
12983        this.last.setDisabled(ap == ps);
12984        this.loading.enable();
12985        this.updateInfo();
12986     },
12987
12988     // private
12989     getPageData : function(){
12990         var total = this.ds.getTotalCount();
12991         return {
12992             total : total,
12993             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12994             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12995         };
12996     },
12997
12998     // private
12999     onLoadError : function(){
13000         this.loading.enable();
13001     },
13002
13003     // private
13004     onPagingKeydown : function(e){
13005         var k = e.getKey();
13006         var d = this.getPageData();
13007         if(k == e.RETURN){
13008             var v = this.field.dom.value, pageNum;
13009             if(!v || isNaN(pageNum = parseInt(v, 10))){
13010                 this.field.dom.value = d.activePage;
13011                 return;
13012             }
13013             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13014             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13015             e.stopEvent();
13016         }
13017         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))
13018         {
13019           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13020           this.field.dom.value = pageNum;
13021           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13022           e.stopEvent();
13023         }
13024         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13025         {
13026           var v = this.field.dom.value, pageNum; 
13027           var increment = (e.shiftKey) ? 10 : 1;
13028           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13029             increment *= -1;
13030           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13031             this.field.dom.value = d.activePage;
13032             return;
13033           }
13034           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13035           {
13036             this.field.dom.value = parseInt(v, 10) + increment;
13037             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13038             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13039           }
13040           e.stopEvent();
13041         }
13042     },
13043
13044     // private
13045     beforeLoad : function(){
13046         if(this.loading){
13047             this.loading.disable();
13048         }
13049     },
13050
13051     // private
13052     onClick : function(which){
13053         var ds = this.ds;
13054         switch(which){
13055             case "first":
13056                 ds.load({params:{start: 0, limit: this.pageSize}});
13057             break;
13058             case "prev":
13059                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13060             break;
13061             case "next":
13062                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13063             break;
13064             case "last":
13065                 var total = ds.getTotalCount();
13066                 var extra = total % this.pageSize;
13067                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13068                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13069             break;
13070             case "refresh":
13071                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13072             break;
13073         }
13074     },
13075
13076     /**
13077      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13078      * @param {Roo.data.Store} store The data store to unbind
13079      */
13080     unbind : function(ds){
13081         ds.un("beforeload", this.beforeLoad, this);
13082         ds.un("load", this.onLoad, this);
13083         ds.un("loadexception", this.onLoadError, this);
13084         ds.un("remove", this.updateInfo, this);
13085         ds.un("add", this.updateInfo, this);
13086         this.ds = undefined;
13087     },
13088
13089     /**
13090      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13091      * @param {Roo.data.Store} store The data store to bind
13092      */
13093     bind : function(ds){
13094         ds.on("beforeload", this.beforeLoad, this);
13095         ds.on("load", this.onLoad, this);
13096         ds.on("loadexception", this.onLoadError, this);
13097         ds.on("remove", this.updateInfo, this);
13098         ds.on("add", this.updateInfo, this);
13099         this.ds = ds;
13100     }
13101 });/*
13102  * Based on:
13103  * Ext JS Library 1.1.1
13104  * Copyright(c) 2006-2007, Ext JS, LLC.
13105  *
13106  * Originally Released Under LGPL - original licence link has changed is not relivant.
13107  *
13108  * Fork - LGPL
13109  * <script type="text/javascript">
13110  */
13111
13112 /**
13113  * @class Roo.Resizable
13114  * @extends Roo.util.Observable
13115  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13116  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13117  * 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
13118  * the element will be wrapped for you automatically.</p>
13119  * <p>Here is the list of valid resize handles:</p>
13120  * <pre>
13121 Value   Description
13122 ------  -------------------
13123  'n'     north
13124  's'     south
13125  'e'     east
13126  'w'     west
13127  'nw'    northwest
13128  'sw'    southwest
13129  'se'    southeast
13130  'ne'    northeast
13131  'hd'    horizontal drag
13132  'all'   all
13133 </pre>
13134  * <p>Here's an example showing the creation of a typical Resizable:</p>
13135  * <pre><code>
13136 var resizer = new Roo.Resizable("element-id", {
13137     handles: 'all',
13138     minWidth: 200,
13139     minHeight: 100,
13140     maxWidth: 500,
13141     maxHeight: 400,
13142     pinned: true
13143 });
13144 resizer.on("resize", myHandler);
13145 </code></pre>
13146  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13147  * resizer.east.setDisplayed(false);</p>
13148  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13149  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13150  * resize operation's new size (defaults to [0, 0])
13151  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13152  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13153  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13154  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13155  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13156  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13157  * @cfg {Number} width The width of the element in pixels (defaults to null)
13158  * @cfg {Number} height The height of the element in pixels (defaults to null)
13159  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13160  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13161  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13162  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13163  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13164  * in favor of the handles config option (defaults to false)
13165  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13166  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13167  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13168  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13169  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13170  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13171  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13172  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13173  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13174  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13175  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13176  * @constructor
13177  * Create a new resizable component
13178  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13179  * @param {Object} config configuration options
13180   */
13181 Roo.Resizable = function(el, config)
13182 {
13183     this.el = Roo.get(el);
13184
13185     if(config && config.wrap){
13186         config.resizeChild = this.el;
13187         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13188         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13189         this.el.setStyle("overflow", "hidden");
13190         this.el.setPositioning(config.resizeChild.getPositioning());
13191         config.resizeChild.clearPositioning();
13192         if(!config.width || !config.height){
13193             var csize = config.resizeChild.getSize();
13194             this.el.setSize(csize.width, csize.height);
13195         }
13196         if(config.pinned && !config.adjustments){
13197             config.adjustments = "auto";
13198         }
13199     }
13200
13201     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13202     this.proxy.unselectable();
13203     this.proxy.enableDisplayMode('block');
13204
13205     Roo.apply(this, config);
13206
13207     if(this.pinned){
13208         this.disableTrackOver = true;
13209         this.el.addClass("x-resizable-pinned");
13210     }
13211     // if the element isn't positioned, make it relative
13212     var position = this.el.getStyle("position");
13213     if(position != "absolute" && position != "fixed"){
13214         this.el.setStyle("position", "relative");
13215     }
13216     if(!this.handles){ // no handles passed, must be legacy style
13217         this.handles = 's,e,se';
13218         if(this.multiDirectional){
13219             this.handles += ',n,w';
13220         }
13221     }
13222     if(this.handles == "all"){
13223         this.handles = "n s e w ne nw se sw";
13224     }
13225     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13226     var ps = Roo.Resizable.positions;
13227     for(var i = 0, len = hs.length; i < len; i++){
13228         if(hs[i] && ps[hs[i]]){
13229             var pos = ps[hs[i]];
13230             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13231         }
13232     }
13233     // legacy
13234     this.corner = this.southeast;
13235     
13236     // updateBox = the box can move..
13237     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13238         this.updateBox = true;
13239     }
13240
13241     this.activeHandle = null;
13242
13243     if(this.resizeChild){
13244         if(typeof this.resizeChild == "boolean"){
13245             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13246         }else{
13247             this.resizeChild = Roo.get(this.resizeChild, true);
13248         }
13249     }
13250     
13251     if(this.adjustments == "auto"){
13252         var rc = this.resizeChild;
13253         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13254         if(rc && (hw || hn)){
13255             rc.position("relative");
13256             rc.setLeft(hw ? hw.el.getWidth() : 0);
13257             rc.setTop(hn ? hn.el.getHeight() : 0);
13258         }
13259         this.adjustments = [
13260             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13261             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13262         ];
13263     }
13264
13265     if(this.draggable){
13266         this.dd = this.dynamic ?
13267             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13268         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13269     }
13270
13271     // public events
13272     this.addEvents({
13273         /**
13274          * @event beforeresize
13275          * Fired before resize is allowed. Set enabled to false to cancel resize.
13276          * @param {Roo.Resizable} this
13277          * @param {Roo.EventObject} e The mousedown event
13278          */
13279         "beforeresize" : true,
13280         /**
13281          * @event resize
13282          * Fired after a resize.
13283          * @param {Roo.Resizable} this
13284          * @param {Number} width The new width
13285          * @param {Number} height The new height
13286          * @param {Roo.EventObject} e The mouseup event
13287          */
13288         "resize" : true
13289     });
13290
13291     if(this.width !== null && this.height !== null){
13292         this.resizeTo(this.width, this.height);
13293     }else{
13294         this.updateChildSize();
13295     }
13296     if(Roo.isIE){
13297         this.el.dom.style.zoom = 1;
13298     }
13299     Roo.Resizable.superclass.constructor.call(this);
13300 };
13301
13302 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13303         resizeChild : false,
13304         adjustments : [0, 0],
13305         minWidth : 5,
13306         minHeight : 5,
13307         maxWidth : 10000,
13308         maxHeight : 10000,
13309         enabled : true,
13310         animate : false,
13311         duration : .35,
13312         dynamic : false,
13313         handles : false,
13314         multiDirectional : false,
13315         disableTrackOver : false,
13316         easing : 'easeOutStrong',
13317         widthIncrement : 0,
13318         heightIncrement : 0,
13319         pinned : false,
13320         width : null,
13321         height : null,
13322         preserveRatio : false,
13323         transparent: false,
13324         minX: 0,
13325         minY: 0,
13326         draggable: false,
13327
13328         /**
13329          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13330          */
13331         constrainTo: undefined,
13332         /**
13333          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13334          */
13335         resizeRegion: undefined,
13336
13337
13338     /**
13339      * Perform a manual resize
13340      * @param {Number} width
13341      * @param {Number} height
13342      */
13343     resizeTo : function(width, height){
13344         this.el.setSize(width, height);
13345         this.updateChildSize();
13346         this.fireEvent("resize", this, width, height, null);
13347     },
13348
13349     // private
13350     startSizing : function(e, handle){
13351         this.fireEvent("beforeresize", this, e);
13352         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13353
13354             if(!this.overlay){
13355                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13356                 this.overlay.unselectable();
13357                 this.overlay.enableDisplayMode("block");
13358                 this.overlay.on("mousemove", this.onMouseMove, this);
13359                 this.overlay.on("mouseup", this.onMouseUp, this);
13360             }
13361             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13362
13363             this.resizing = true;
13364             this.startBox = this.el.getBox();
13365             this.startPoint = e.getXY();
13366             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13367                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13368
13369             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13370             this.overlay.show();
13371
13372             if(this.constrainTo) {
13373                 var ct = Roo.get(this.constrainTo);
13374                 this.resizeRegion = ct.getRegion().adjust(
13375                     ct.getFrameWidth('t'),
13376                     ct.getFrameWidth('l'),
13377                     -ct.getFrameWidth('b'),
13378                     -ct.getFrameWidth('r')
13379                 );
13380             }
13381
13382             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13383             this.proxy.show();
13384             this.proxy.setBox(this.startBox);
13385             if(!this.dynamic){
13386                 this.proxy.setStyle('visibility', 'visible');
13387             }
13388         }
13389     },
13390
13391     // private
13392     onMouseDown : function(handle, e){
13393         if(this.enabled){
13394             e.stopEvent();
13395             this.activeHandle = handle;
13396             this.startSizing(e, handle);
13397         }
13398     },
13399
13400     // private
13401     onMouseUp : function(e){
13402         var size = this.resizeElement();
13403         this.resizing = false;
13404         this.handleOut();
13405         this.overlay.hide();
13406         this.proxy.hide();
13407         this.fireEvent("resize", this, size.width, size.height, e);
13408     },
13409
13410     // private
13411     updateChildSize : function(){
13412         if(this.resizeChild){
13413             var el = this.el;
13414             var child = this.resizeChild;
13415             var adj = this.adjustments;
13416             if(el.dom.offsetWidth){
13417                 var b = el.getSize(true);
13418                 child.setSize(b.width+adj[0], b.height+adj[1]);
13419             }
13420             // Second call here for IE
13421             // The first call enables instant resizing and
13422             // the second call corrects scroll bars if they
13423             // exist
13424             if(Roo.isIE){
13425                 setTimeout(function(){
13426                     if(el.dom.offsetWidth){
13427                         var b = el.getSize(true);
13428                         child.setSize(b.width+adj[0], b.height+adj[1]);
13429                     }
13430                 }, 10);
13431             }
13432         }
13433     },
13434
13435     // private
13436     snap : function(value, inc, min){
13437         if(!inc || !value) return value;
13438         var newValue = value;
13439         var m = value % inc;
13440         if(m > 0){
13441             if(m > (inc/2)){
13442                 newValue = value + (inc-m);
13443             }else{
13444                 newValue = value - m;
13445             }
13446         }
13447         return Math.max(min, newValue);
13448     },
13449
13450     // private
13451     resizeElement : function(){
13452         var box = this.proxy.getBox();
13453         if(this.updateBox){
13454             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13455         }else{
13456             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13457         }
13458         this.updateChildSize();
13459         if(!this.dynamic){
13460             this.proxy.hide();
13461         }
13462         return box;
13463     },
13464
13465     // private
13466     constrain : function(v, diff, m, mx){
13467         if(v - diff < m){
13468             diff = v - m;
13469         }else if(v - diff > mx){
13470             diff = mx - v;
13471         }
13472         return diff;
13473     },
13474
13475     // private
13476     onMouseMove : function(e){
13477         if(this.enabled){
13478             try{// try catch so if something goes wrong the user doesn't get hung
13479
13480             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13481                 return;
13482             }
13483
13484             //var curXY = this.startPoint;
13485             var curSize = this.curSize || this.startBox;
13486             var x = this.startBox.x, y = this.startBox.y;
13487             var ox = x, oy = y;
13488             var w = curSize.width, h = curSize.height;
13489             var ow = w, oh = h;
13490             var mw = this.minWidth, mh = this.minHeight;
13491             var mxw = this.maxWidth, mxh = this.maxHeight;
13492             var wi = this.widthIncrement;
13493             var hi = this.heightIncrement;
13494
13495             var eventXY = e.getXY();
13496             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13497             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13498
13499             var pos = this.activeHandle.position;
13500
13501             switch(pos){
13502                 case "east":
13503                     w += diffX;
13504                     w = Math.min(Math.max(mw, w), mxw);
13505                     break;
13506              
13507                 case "south":
13508                     h += diffY;
13509                     h = Math.min(Math.max(mh, h), mxh);
13510                     break;
13511                 case "southeast":
13512                     w += diffX;
13513                     h += diffY;
13514                     w = Math.min(Math.max(mw, w), mxw);
13515                     h = Math.min(Math.max(mh, h), mxh);
13516                     break;
13517                 case "north":
13518                     diffY = this.constrain(h, diffY, mh, mxh);
13519                     y += diffY;
13520                     h -= diffY;
13521                     break;
13522                 case "hdrag":
13523                     
13524                     if (wi) {
13525                         var adiffX = Math.abs(diffX);
13526                         var sub = (adiffX % wi); // how much 
13527                         if (sub > (wi/2)) { // far enough to snap
13528                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13529                         } else {
13530                             // remove difference.. 
13531                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13532                         }
13533                     }
13534                     x += diffX;
13535                     x = Math.max(this.minX, x);
13536                     break;
13537                 case "west":
13538                     diffX = this.constrain(w, diffX, mw, mxw);
13539                     x += diffX;
13540                     w -= diffX;
13541                     break;
13542                 case "northeast":
13543                     w += diffX;
13544                     w = Math.min(Math.max(mw, w), mxw);
13545                     diffY = this.constrain(h, diffY, mh, mxh);
13546                     y += diffY;
13547                     h -= diffY;
13548                     break;
13549                 case "northwest":
13550                     diffX = this.constrain(w, diffX, mw, mxw);
13551                     diffY = this.constrain(h, diffY, mh, mxh);
13552                     y += diffY;
13553                     h -= diffY;
13554                     x += diffX;
13555                     w -= diffX;
13556                     break;
13557                case "southwest":
13558                     diffX = this.constrain(w, diffX, mw, mxw);
13559                     h += diffY;
13560                     h = Math.min(Math.max(mh, h), mxh);
13561                     x += diffX;
13562                     w -= diffX;
13563                     break;
13564             }
13565
13566             var sw = this.snap(w, wi, mw);
13567             var sh = this.snap(h, hi, mh);
13568             if(sw != w || sh != h){
13569                 switch(pos){
13570                     case "northeast":
13571                         y -= sh - h;
13572                     break;
13573                     case "north":
13574                         y -= sh - h;
13575                         break;
13576                     case "southwest":
13577                         x -= sw - w;
13578                     break;
13579                     case "west":
13580                         x -= sw - w;
13581                         break;
13582                     case "northwest":
13583                         x -= sw - w;
13584                         y -= sh - h;
13585                     break;
13586                 }
13587                 w = sw;
13588                 h = sh;
13589             }
13590
13591             if(this.preserveRatio){
13592                 switch(pos){
13593                     case "southeast":
13594                     case "east":
13595                         h = oh * (w/ow);
13596                         h = Math.min(Math.max(mh, h), mxh);
13597                         w = ow * (h/oh);
13598                        break;
13599                     case "south":
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                         break;
13604                     case "northeast":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                     break;
13609                     case "north":
13610                         var tw = w;
13611                         w = ow * (h/oh);
13612                         w = Math.min(Math.max(mw, w), mxw);
13613                         h = oh * (w/ow);
13614                         x += (tw - w) / 2;
13615                         break;
13616                     case "southwest":
13617                         h = oh * (w/ow);
13618                         h = Math.min(Math.max(mh, h), mxh);
13619                         var tw = w;
13620                         w = ow * (h/oh);
13621                         x += tw - w;
13622                         break;
13623                     case "west":
13624                         var th = h;
13625                         h = oh * (w/ow);
13626                         h = Math.min(Math.max(mh, h), mxh);
13627                         y += (th - h) / 2;
13628                         var tw = w;
13629                         w = ow * (h/oh);
13630                         x += tw - w;
13631                        break;
13632                     case "northwest":
13633                         var tw = w;
13634                         var th = h;
13635                         h = oh * (w/ow);
13636                         h = Math.min(Math.max(mh, h), mxh);
13637                         w = ow * (h/oh);
13638                         y += th - h;
13639                         x += tw - w;
13640                        break;
13641
13642                 }
13643             }
13644             if (pos == 'hdrag') {
13645                 w = ow;
13646             }
13647             this.proxy.setBounds(x, y, w, h);
13648             if(this.dynamic){
13649                 this.resizeElement();
13650             }
13651             }catch(e){}
13652         }
13653     },
13654
13655     // private
13656     handleOver : function(){
13657         if(this.enabled){
13658             this.el.addClass("x-resizable-over");
13659         }
13660     },
13661
13662     // private
13663     handleOut : function(){
13664         if(!this.resizing){
13665             this.el.removeClass("x-resizable-over");
13666         }
13667     },
13668
13669     /**
13670      * Returns the element this component is bound to.
13671      * @return {Roo.Element}
13672      */
13673     getEl : function(){
13674         return this.el;
13675     },
13676
13677     /**
13678      * Returns the resizeChild element (or null).
13679      * @return {Roo.Element}
13680      */
13681     getResizeChild : function(){
13682         return this.resizeChild;
13683     },
13684
13685     /**
13686      * Destroys this resizable. If the element was wrapped and
13687      * removeEl is not true then the element remains.
13688      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13689      */
13690     destroy : function(removeEl){
13691         this.proxy.remove();
13692         if(this.overlay){
13693             this.overlay.removeAllListeners();
13694             this.overlay.remove();
13695         }
13696         var ps = Roo.Resizable.positions;
13697         for(var k in ps){
13698             if(typeof ps[k] != "function" && this[ps[k]]){
13699                 var h = this[ps[k]];
13700                 h.el.removeAllListeners();
13701                 h.el.remove();
13702             }
13703         }
13704         if(removeEl){
13705             this.el.update("");
13706             this.el.remove();
13707         }
13708     }
13709 });
13710
13711 // private
13712 // hash to map config positions to true positions
13713 Roo.Resizable.positions = {
13714     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13715     hd: "hdrag"
13716 };
13717
13718 // private
13719 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13720     if(!this.tpl){
13721         // only initialize the template if resizable is used
13722         var tpl = Roo.DomHelper.createTemplate(
13723             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13724         );
13725         tpl.compile();
13726         Roo.Resizable.Handle.prototype.tpl = tpl;
13727     }
13728     this.position = pos;
13729     this.rz = rz;
13730     // show north drag fro topdra
13731     var handlepos = pos == 'hdrag' ? 'north' : pos;
13732     
13733     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13734     if (pos == 'hdrag') {
13735         this.el.setStyle('cursor', 'pointer');
13736     }
13737     this.el.unselectable();
13738     if(transparent){
13739         this.el.setOpacity(0);
13740     }
13741     this.el.on("mousedown", this.onMouseDown, this);
13742     if(!disableTrackOver){
13743         this.el.on("mouseover", this.onMouseOver, this);
13744         this.el.on("mouseout", this.onMouseOut, this);
13745     }
13746 };
13747
13748 // private
13749 Roo.Resizable.Handle.prototype = {
13750     afterResize : function(rz){
13751         // do nothing
13752     },
13753     // private
13754     onMouseDown : function(e){
13755         this.rz.onMouseDown(this, e);
13756     },
13757     // private
13758     onMouseOver : function(e){
13759         this.rz.handleOver(this, e);
13760     },
13761     // private
13762     onMouseOut : function(e){
13763         this.rz.handleOut(this, e);
13764     }
13765 };/*
13766  * Based on:
13767  * Ext JS Library 1.1.1
13768  * Copyright(c) 2006-2007, Ext JS, LLC.
13769  *
13770  * Originally Released Under LGPL - original licence link has changed is not relivant.
13771  *
13772  * Fork - LGPL
13773  * <script type="text/javascript">
13774  */
13775
13776 /**
13777  * @class Roo.Editor
13778  * @extends Roo.Component
13779  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13780  * @constructor
13781  * Create a new Editor
13782  * @param {Roo.form.Field} field The Field object (or descendant)
13783  * @param {Object} config The config object
13784  */
13785 Roo.Editor = function(field, config){
13786     Roo.Editor.superclass.constructor.call(this, config);
13787     this.field = field;
13788     this.addEvents({
13789         /**
13790              * @event beforestartedit
13791              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13792              * false from the handler of this event.
13793              * @param {Editor} this
13794              * @param {Roo.Element} boundEl The underlying element bound to this editor
13795              * @param {Mixed} value The field value being set
13796              */
13797         "beforestartedit" : true,
13798         /**
13799              * @event startedit
13800              * Fires when this editor is displayed
13801              * @param {Roo.Element} boundEl The underlying element bound to this editor
13802              * @param {Mixed} value The starting field value
13803              */
13804         "startedit" : true,
13805         /**
13806              * @event beforecomplete
13807              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13808              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13809              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13810              * event will not fire since no edit actually occurred.
13811              * @param {Editor} this
13812              * @param {Mixed} value The current field value
13813              * @param {Mixed} startValue The original field value
13814              */
13815         "beforecomplete" : true,
13816         /**
13817              * @event complete
13818              * Fires after editing is complete and any changed value has been written to the underlying field.
13819              * @param {Editor} this
13820              * @param {Mixed} value The current field value
13821              * @param {Mixed} startValue The original field value
13822              */
13823         "complete" : true,
13824         /**
13825          * @event specialkey
13826          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13827          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13828          * @param {Roo.form.Field} this
13829          * @param {Roo.EventObject} e The event object
13830          */
13831         "specialkey" : true
13832     });
13833 };
13834
13835 Roo.extend(Roo.Editor, Roo.Component, {
13836     /**
13837      * @cfg {Boolean/String} autosize
13838      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13839      * or "height" to adopt the height only (defaults to false)
13840      */
13841     /**
13842      * @cfg {Boolean} revertInvalid
13843      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13844      * validation fails (defaults to true)
13845      */
13846     /**
13847      * @cfg {Boolean} ignoreNoChange
13848      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13849      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13850      * will never be ignored.
13851      */
13852     /**
13853      * @cfg {Boolean} hideEl
13854      * False to keep the bound element visible while the editor is displayed (defaults to true)
13855      */
13856     /**
13857      * @cfg {Mixed} value
13858      * The data value of the underlying field (defaults to "")
13859      */
13860     value : "",
13861     /**
13862      * @cfg {String} alignment
13863      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13864      */
13865     alignment: "c-c?",
13866     /**
13867      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13868      * for bottom-right shadow (defaults to "frame")
13869      */
13870     shadow : "frame",
13871     /**
13872      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13873      */
13874     constrain : false,
13875     /**
13876      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13877      */
13878     completeOnEnter : false,
13879     /**
13880      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13881      */
13882     cancelOnEsc : false,
13883     /**
13884      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13885      */
13886     updateEl : false,
13887
13888     // private
13889     onRender : function(ct, position){
13890         this.el = new Roo.Layer({
13891             shadow: this.shadow,
13892             cls: "x-editor",
13893             parentEl : ct,
13894             shim : this.shim,
13895             shadowOffset:4,
13896             id: this.id,
13897             constrain: this.constrain
13898         });
13899         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13900         if(this.field.msgTarget != 'title'){
13901             this.field.msgTarget = 'qtip';
13902         }
13903         this.field.render(this.el);
13904         if(Roo.isGecko){
13905             this.field.el.dom.setAttribute('autocomplete', 'off');
13906         }
13907         this.field.on("specialkey", this.onSpecialKey, this);
13908         if(this.swallowKeys){
13909             this.field.el.swallowEvent(['keydown','keypress']);
13910         }
13911         this.field.show();
13912         this.field.on("blur", this.onBlur, this);
13913         if(this.field.grow){
13914             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13915         }
13916     },
13917
13918     onSpecialKey : function(field, e){
13919         //Roo.log('editor onSpecialKey');
13920         if(this.completeOnEnter && e.getKey() == e.ENTER){
13921             e.stopEvent();
13922             this.completeEdit();
13923         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13924             this.cancelEdit();
13925         }else{
13926             this.fireEvent('specialkey', field, e);
13927         }
13928     },
13929
13930     /**
13931      * Starts the editing process and shows the editor.
13932      * @param {String/HTMLElement/Element} el The element to edit
13933      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13934       * to the innerHTML of el.
13935      */
13936     startEdit : function(el, value){
13937         if(this.editing){
13938             this.completeEdit();
13939         }
13940         this.boundEl = Roo.get(el);
13941         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13942         if(!this.rendered){
13943             this.render(this.parentEl || document.body);
13944         }
13945         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13946             return;
13947         }
13948         this.startValue = v;
13949         this.field.setValue(v);
13950         if(this.autoSize){
13951             var sz = this.boundEl.getSize();
13952             switch(this.autoSize){
13953                 case "width":
13954                 this.setSize(sz.width,  "");
13955                 break;
13956                 case "height":
13957                 this.setSize("",  sz.height);
13958                 break;
13959                 default:
13960                 this.setSize(sz.width,  sz.height);
13961             }
13962         }
13963         this.el.alignTo(this.boundEl, this.alignment);
13964         this.editing = true;
13965         if(Roo.QuickTips){
13966             Roo.QuickTips.disable();
13967         }
13968         this.show();
13969     },
13970
13971     /**
13972      * Sets the height and width of this editor.
13973      * @param {Number} width The new width
13974      * @param {Number} height The new height
13975      */
13976     setSize : function(w, h){
13977         this.field.setSize(w, h);
13978         if(this.el){
13979             this.el.sync();
13980         }
13981     },
13982
13983     /**
13984      * Realigns the editor to the bound field based on the current alignment config value.
13985      */
13986     realign : function(){
13987         this.el.alignTo(this.boundEl, this.alignment);
13988     },
13989
13990     /**
13991      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13992      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13993      */
13994     completeEdit : function(remainVisible){
13995         if(!this.editing){
13996             return;
13997         }
13998         var v = this.getValue();
13999         if(this.revertInvalid !== false && !this.field.isValid()){
14000             v = this.startValue;
14001             this.cancelEdit(true);
14002         }
14003         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14004             this.editing = false;
14005             this.hide();
14006             return;
14007         }
14008         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14009             this.editing = false;
14010             if(this.updateEl && this.boundEl){
14011                 this.boundEl.update(v);
14012             }
14013             if(remainVisible !== true){
14014                 this.hide();
14015             }
14016             this.fireEvent("complete", this, v, this.startValue);
14017         }
14018     },
14019
14020     // private
14021     onShow : function(){
14022         this.el.show();
14023         if(this.hideEl !== false){
14024             this.boundEl.hide();
14025         }
14026         this.field.show();
14027         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14028             this.fixIEFocus = true;
14029             this.deferredFocus.defer(50, this);
14030         }else{
14031             this.field.focus();
14032         }
14033         this.fireEvent("startedit", this.boundEl, this.startValue);
14034     },
14035
14036     deferredFocus : function(){
14037         if(this.editing){
14038             this.field.focus();
14039         }
14040     },
14041
14042     /**
14043      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14044      * reverted to the original starting value.
14045      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14046      * cancel (defaults to false)
14047      */
14048     cancelEdit : function(remainVisible){
14049         if(this.editing){
14050             this.setValue(this.startValue);
14051             if(remainVisible !== true){
14052                 this.hide();
14053             }
14054         }
14055     },
14056
14057     // private
14058     onBlur : function(){
14059         if(this.allowBlur !== true && this.editing){
14060             this.completeEdit();
14061         }
14062     },
14063
14064     // private
14065     onHide : function(){
14066         if(this.editing){
14067             this.completeEdit();
14068             return;
14069         }
14070         this.field.blur();
14071         if(this.field.collapse){
14072             this.field.collapse();
14073         }
14074         this.el.hide();
14075         if(this.hideEl !== false){
14076             this.boundEl.show();
14077         }
14078         if(Roo.QuickTips){
14079             Roo.QuickTips.enable();
14080         }
14081     },
14082
14083     /**
14084      * Sets the data value of the editor
14085      * @param {Mixed} value Any valid value supported by the underlying field
14086      */
14087     setValue : function(v){
14088         this.field.setValue(v);
14089     },
14090
14091     /**
14092      * Gets the data value of the editor
14093      * @return {Mixed} The data value
14094      */
14095     getValue : function(){
14096         return this.field.getValue();
14097     }
14098 });/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108  
14109 /**
14110  * @class Roo.BasicDialog
14111  * @extends Roo.util.Observable
14112  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14113  * <pre><code>
14114 var dlg = new Roo.BasicDialog("my-dlg", {
14115     height: 200,
14116     width: 300,
14117     minHeight: 100,
14118     minWidth: 150,
14119     modal: true,
14120     proxyDrag: true,
14121     shadow: true
14122 });
14123 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14124 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14125 dlg.addButton('Cancel', dlg.hide, dlg);
14126 dlg.show();
14127 </code></pre>
14128   <b>A Dialog should always be a direct child of the body element.</b>
14129  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14130  * @cfg {String} title Default text to display in the title bar (defaults to null)
14131  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14132  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14133  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14134  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14135  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14136  * (defaults to null with no animation)
14137  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14138  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14139  * property for valid values (defaults to 'all')
14140  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14141  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14142  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14143  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14144  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14145  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14146  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14147  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14148  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14149  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14150  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14151  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14152  * draggable = true (defaults to false)
14153  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14154  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14155  * shadow (defaults to false)
14156  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14157  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14158  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14159  * @cfg {Array} buttons Array of buttons
14160  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14161  * @constructor
14162  * Create a new BasicDialog.
14163  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14164  * @param {Object} config Configuration options
14165  */
14166 Roo.BasicDialog = function(el, config){
14167     this.el = Roo.get(el);
14168     var dh = Roo.DomHelper;
14169     if(!this.el && config && config.autoCreate){
14170         if(typeof config.autoCreate == "object"){
14171             if(!config.autoCreate.id){
14172                 config.autoCreate.id = el;
14173             }
14174             this.el = dh.append(document.body,
14175                         config.autoCreate, true);
14176         }else{
14177             this.el = dh.append(document.body,
14178                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14179         }
14180     }
14181     el = this.el;
14182     el.setDisplayed(true);
14183     el.hide = this.hideAction;
14184     this.id = el.id;
14185     el.addClass("x-dlg");
14186
14187     Roo.apply(this, config);
14188
14189     this.proxy = el.createProxy("x-dlg-proxy");
14190     this.proxy.hide = this.hideAction;
14191     this.proxy.setOpacity(.5);
14192     this.proxy.hide();
14193
14194     if(config.width){
14195         el.setWidth(config.width);
14196     }
14197     if(config.height){
14198         el.setHeight(config.height);
14199     }
14200     this.size = el.getSize();
14201     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14202         this.xy = [config.x,config.y];
14203     }else{
14204         this.xy = el.getCenterXY(true);
14205     }
14206     /** The header element @type Roo.Element */
14207     this.header = el.child("> .x-dlg-hd");
14208     /** The body element @type Roo.Element */
14209     this.body = el.child("> .x-dlg-bd");
14210     /** The footer element @type Roo.Element */
14211     this.footer = el.child("> .x-dlg-ft");
14212
14213     if(!this.header){
14214         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14215     }
14216     if(!this.body){
14217         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14218     }
14219
14220     this.header.unselectable();
14221     if(this.title){
14222         this.header.update(this.title);
14223     }
14224     // this element allows the dialog to be focused for keyboard event
14225     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14226     this.focusEl.swallowEvent("click", true);
14227
14228     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14229
14230     // wrap the body and footer for special rendering
14231     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14232     if(this.footer){
14233         this.bwrap.dom.appendChild(this.footer.dom);
14234     }
14235
14236     this.bg = this.el.createChild({
14237         tag: "div", cls:"x-dlg-bg",
14238         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14239     });
14240     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14241
14242
14243     if(this.autoScroll !== false && !this.autoTabs){
14244         this.body.setStyle("overflow", "auto");
14245     }
14246
14247     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14248
14249     if(this.closable !== false){
14250         this.el.addClass("x-dlg-closable");
14251         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14252         this.close.on("click", this.closeClick, this);
14253         this.close.addClassOnOver("x-dlg-close-over");
14254     }
14255     if(this.collapsible !== false){
14256         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14257         this.collapseBtn.on("click", this.collapseClick, this);
14258         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14259         this.header.on("dblclick", this.collapseClick, this);
14260     }
14261     if(this.resizable !== false){
14262         this.el.addClass("x-dlg-resizable");
14263         this.resizer = new Roo.Resizable(el, {
14264             minWidth: this.minWidth || 80,
14265             minHeight:this.minHeight || 80,
14266             handles: this.resizeHandles || "all",
14267             pinned: true
14268         });
14269         this.resizer.on("beforeresize", this.beforeResize, this);
14270         this.resizer.on("resize", this.onResize, this);
14271     }
14272     if(this.draggable !== false){
14273         el.addClass("x-dlg-draggable");
14274         if (!this.proxyDrag) {
14275             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14276         }
14277         else {
14278             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14279         }
14280         dd.setHandleElId(this.header.id);
14281         dd.endDrag = this.endMove.createDelegate(this);
14282         dd.startDrag = this.startMove.createDelegate(this);
14283         dd.onDrag = this.onDrag.createDelegate(this);
14284         dd.scroll = false;
14285         this.dd = dd;
14286     }
14287     if(this.modal){
14288         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14289         this.mask.enableDisplayMode("block");
14290         this.mask.hide();
14291         this.el.addClass("x-dlg-modal");
14292     }
14293     if(this.shadow){
14294         this.shadow = new Roo.Shadow({
14295             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14296             offset : this.shadowOffset
14297         });
14298     }else{
14299         this.shadowOffset = 0;
14300     }
14301     if(Roo.useShims && this.shim !== false){
14302         this.shim = this.el.createShim();
14303         this.shim.hide = this.hideAction;
14304         this.shim.hide();
14305     }else{
14306         this.shim = false;
14307     }
14308     if(this.autoTabs){
14309         this.initTabs();
14310     }
14311     if (this.buttons) { 
14312         var bts= this.buttons;
14313         this.buttons = [];
14314         Roo.each(bts, function(b) {
14315             this.addButton(b);
14316         }, this);
14317     }
14318     
14319     
14320     this.addEvents({
14321         /**
14322          * @event keydown
14323          * Fires when a key is pressed
14324          * @param {Roo.BasicDialog} this
14325          * @param {Roo.EventObject} e
14326          */
14327         "keydown" : true,
14328         /**
14329          * @event move
14330          * Fires when this dialog is moved by the user.
14331          * @param {Roo.BasicDialog} this
14332          * @param {Number} x The new page X
14333          * @param {Number} y The new page Y
14334          */
14335         "move" : true,
14336         /**
14337          * @event resize
14338          * Fires when this dialog is resized by the user.
14339          * @param {Roo.BasicDialog} this
14340          * @param {Number} width The new width
14341          * @param {Number} height The new height
14342          */
14343         "resize" : true,
14344         /**
14345          * @event beforehide
14346          * Fires before this dialog is hidden.
14347          * @param {Roo.BasicDialog} this
14348          */
14349         "beforehide" : true,
14350         /**
14351          * @event hide
14352          * Fires when this dialog is hidden.
14353          * @param {Roo.BasicDialog} this
14354          */
14355         "hide" : true,
14356         /**
14357          * @event beforeshow
14358          * Fires before this dialog is shown.
14359          * @param {Roo.BasicDialog} this
14360          */
14361         "beforeshow" : true,
14362         /**
14363          * @event show
14364          * Fires when this dialog is shown.
14365          * @param {Roo.BasicDialog} this
14366          */
14367         "show" : true
14368     });
14369     el.on("keydown", this.onKeyDown, this);
14370     el.on("mousedown", this.toFront, this);
14371     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14372     this.el.hide();
14373     Roo.DialogManager.register(this);
14374     Roo.BasicDialog.superclass.constructor.call(this);
14375 };
14376
14377 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14378     shadowOffset: Roo.isIE ? 6 : 5,
14379     minHeight: 80,
14380     minWidth: 200,
14381     minButtonWidth: 75,
14382     defaultButton: null,
14383     buttonAlign: "right",
14384     tabTag: 'div',
14385     firstShow: true,
14386
14387     /**
14388      * Sets the dialog title text
14389      * @param {String} text The title text to display
14390      * @return {Roo.BasicDialog} this
14391      */
14392     setTitle : function(text){
14393         this.header.update(text);
14394         return this;
14395     },
14396
14397     // private
14398     closeClick : function(){
14399         this.hide();
14400     },
14401
14402     // private
14403     collapseClick : function(){
14404         this[this.collapsed ? "expand" : "collapse"]();
14405     },
14406
14407     /**
14408      * Collapses the dialog to its minimized state (only the title bar is visible).
14409      * Equivalent to the user clicking the collapse dialog button.
14410      */
14411     collapse : function(){
14412         if(!this.collapsed){
14413             this.collapsed = true;
14414             this.el.addClass("x-dlg-collapsed");
14415             this.restoreHeight = this.el.getHeight();
14416             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14417         }
14418     },
14419
14420     /**
14421      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14422      * clicking the expand dialog button.
14423      */
14424     expand : function(){
14425         if(this.collapsed){
14426             this.collapsed = false;
14427             this.el.removeClass("x-dlg-collapsed");
14428             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14429         }
14430     },
14431
14432     /**
14433      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14434      * @return {Roo.TabPanel} The tabs component
14435      */
14436     initTabs : function(){
14437         var tabs = this.getTabs();
14438         while(tabs.getTab(0)){
14439             tabs.removeTab(0);
14440         }
14441         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14442             var dom = el.dom;
14443             tabs.addTab(Roo.id(dom), dom.title);
14444             dom.title = "";
14445         });
14446         tabs.activate(0);
14447         return tabs;
14448     },
14449
14450     // private
14451     beforeResize : function(){
14452         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14453     },
14454
14455     // private
14456     onResize : function(){
14457         this.refreshSize();
14458         this.syncBodyHeight();
14459         this.adjustAssets();
14460         this.focus();
14461         this.fireEvent("resize", this, this.size.width, this.size.height);
14462     },
14463
14464     // private
14465     onKeyDown : function(e){
14466         if(this.isVisible()){
14467             this.fireEvent("keydown", this, e);
14468         }
14469     },
14470
14471     /**
14472      * Resizes the dialog.
14473      * @param {Number} width
14474      * @param {Number} height
14475      * @return {Roo.BasicDialog} this
14476      */
14477     resizeTo : function(width, height){
14478         this.el.setSize(width, height);
14479         this.size = {width: width, height: height};
14480         this.syncBodyHeight();
14481         if(this.fixedcenter){
14482             this.center();
14483         }
14484         if(this.isVisible()){
14485             this.constrainXY();
14486             this.adjustAssets();
14487         }
14488         this.fireEvent("resize", this, width, height);
14489         return this;
14490     },
14491
14492
14493     /**
14494      * Resizes the dialog to fit the specified content size.
14495      * @param {Number} width
14496      * @param {Number} height
14497      * @return {Roo.BasicDialog} this
14498      */
14499     setContentSize : function(w, h){
14500         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14501         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14502         //if(!this.el.isBorderBox()){
14503             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14504             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14505         //}
14506         if(this.tabs){
14507             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14508             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14509         }
14510         this.resizeTo(w, h);
14511         return this;
14512     },
14513
14514     /**
14515      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14516      * executed in response to a particular key being pressed while the dialog is active.
14517      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14518      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14519      * @param {Function} fn The function to call
14520      * @param {Object} scope (optional) The scope of the function
14521      * @return {Roo.BasicDialog} this
14522      */
14523     addKeyListener : function(key, fn, scope){
14524         var keyCode, shift, ctrl, alt;
14525         if(typeof key == "object" && !(key instanceof Array)){
14526             keyCode = key["key"];
14527             shift = key["shift"];
14528             ctrl = key["ctrl"];
14529             alt = key["alt"];
14530         }else{
14531             keyCode = key;
14532         }
14533         var handler = function(dlg, e){
14534             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14535                 var k = e.getKey();
14536                 if(keyCode instanceof Array){
14537                     for(var i = 0, len = keyCode.length; i < len; i++){
14538                         if(keyCode[i] == k){
14539                           fn.call(scope || window, dlg, k, e);
14540                           return;
14541                         }
14542                     }
14543                 }else{
14544                     if(k == keyCode){
14545                         fn.call(scope || window, dlg, k, e);
14546                     }
14547                 }
14548             }
14549         };
14550         this.on("keydown", handler);
14551         return this;
14552     },
14553
14554     /**
14555      * Returns the TabPanel component (creates it if it doesn't exist).
14556      * Note: If you wish to simply check for the existence of tabs without creating them,
14557      * check for a null 'tabs' property.
14558      * @return {Roo.TabPanel} The tabs component
14559      */
14560     getTabs : function(){
14561         if(!this.tabs){
14562             this.el.addClass("x-dlg-auto-tabs");
14563             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14564             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14565         }
14566         return this.tabs;
14567     },
14568
14569     /**
14570      * Adds a button to the footer section of the dialog.
14571      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14572      * object or a valid Roo.DomHelper element config
14573      * @param {Function} handler The function called when the button is clicked
14574      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14575      * @return {Roo.Button} The new button
14576      */
14577     addButton : function(config, handler, scope){
14578         var dh = Roo.DomHelper;
14579         if(!this.footer){
14580             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14581         }
14582         if(!this.btnContainer){
14583             var tb = this.footer.createChild({
14584
14585                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14586                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14587             }, null, true);
14588             this.btnContainer = tb.firstChild.firstChild.firstChild;
14589         }
14590         var bconfig = {
14591             handler: handler,
14592             scope: scope,
14593             minWidth: this.minButtonWidth,
14594             hideParent:true
14595         };
14596         if(typeof config == "string"){
14597             bconfig.text = config;
14598         }else{
14599             if(config.tag){
14600                 bconfig.dhconfig = config;
14601             }else{
14602                 Roo.apply(bconfig, config);
14603             }
14604         }
14605         var fc = false;
14606         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14607             bconfig.position = Math.max(0, bconfig.position);
14608             fc = this.btnContainer.childNodes[bconfig.position];
14609         }
14610          
14611         var btn = new Roo.Button(
14612             fc ? 
14613                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14614                 : this.btnContainer.appendChild(document.createElement("td")),
14615             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14616             bconfig
14617         );
14618         this.syncBodyHeight();
14619         if(!this.buttons){
14620             /**
14621              * Array of all the buttons that have been added to this dialog via addButton
14622              * @type Array
14623              */
14624             this.buttons = [];
14625         }
14626         this.buttons.push(btn);
14627         return btn;
14628     },
14629
14630     /**
14631      * Sets the default button to be focused when the dialog is displayed.
14632      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14633      * @return {Roo.BasicDialog} this
14634      */
14635     setDefaultButton : function(btn){
14636         this.defaultButton = btn;
14637         return this;
14638     },
14639
14640     // private
14641     getHeaderFooterHeight : function(safe){
14642         var height = 0;
14643         if(this.header){
14644            height += this.header.getHeight();
14645         }
14646         if(this.footer){
14647            var fm = this.footer.getMargins();
14648             height += (this.footer.getHeight()+fm.top+fm.bottom);
14649         }
14650         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14651         height += this.centerBg.getPadding("tb");
14652         return height;
14653     },
14654
14655     // private
14656     syncBodyHeight : function(){
14657         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14658         var height = this.size.height - this.getHeaderFooterHeight(false);
14659         bd.setHeight(height-bd.getMargins("tb"));
14660         var hh = this.header.getHeight();
14661         var h = this.size.height-hh;
14662         cb.setHeight(h);
14663         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14664         bw.setHeight(h-cb.getPadding("tb"));
14665         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14666         bd.setWidth(bw.getWidth(true));
14667         if(this.tabs){
14668             this.tabs.syncHeight();
14669             if(Roo.isIE){
14670                 this.tabs.el.repaint();
14671             }
14672         }
14673     },
14674
14675     /**
14676      * Restores the previous state of the dialog if Roo.state is configured.
14677      * @return {Roo.BasicDialog} this
14678      */
14679     restoreState : function(){
14680         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14681         if(box && box.width){
14682             this.xy = [box.x, box.y];
14683             this.resizeTo(box.width, box.height);
14684         }
14685         return this;
14686     },
14687
14688     // private
14689     beforeShow : function(){
14690         this.expand();
14691         if(this.fixedcenter){
14692             this.xy = this.el.getCenterXY(true);
14693         }
14694         if(this.modal){
14695             Roo.get(document.body).addClass("x-body-masked");
14696             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14697             this.mask.show();
14698         }
14699         this.constrainXY();
14700     },
14701
14702     // private
14703     animShow : function(){
14704         var b = Roo.get(this.animateTarget).getBox();
14705         this.proxy.setSize(b.width, b.height);
14706         this.proxy.setLocation(b.x, b.y);
14707         this.proxy.show();
14708         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14709                     true, .35, this.showEl.createDelegate(this));
14710     },
14711
14712     /**
14713      * Shows the dialog.
14714      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14715      * @return {Roo.BasicDialog} this
14716      */
14717     show : function(animateTarget){
14718         if (this.fireEvent("beforeshow", this) === false){
14719             return;
14720         }
14721         if(this.syncHeightBeforeShow){
14722             this.syncBodyHeight();
14723         }else if(this.firstShow){
14724             this.firstShow = false;
14725             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14726         }
14727         this.animateTarget = animateTarget || this.animateTarget;
14728         if(!this.el.isVisible()){
14729             this.beforeShow();
14730             if(this.animateTarget && Roo.get(this.animateTarget)){
14731                 this.animShow();
14732             }else{
14733                 this.showEl();
14734             }
14735         }
14736         return this;
14737     },
14738
14739     // private
14740     showEl : function(){
14741         this.proxy.hide();
14742         this.el.setXY(this.xy);
14743         this.el.show();
14744         this.adjustAssets(true);
14745         this.toFront();
14746         this.focus();
14747         // IE peekaboo bug - fix found by Dave Fenwick
14748         if(Roo.isIE){
14749             this.el.repaint();
14750         }
14751         this.fireEvent("show", this);
14752     },
14753
14754     /**
14755      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14756      * dialog itself will receive focus.
14757      */
14758     focus : function(){
14759         if(this.defaultButton){
14760             this.defaultButton.focus();
14761         }else{
14762             this.focusEl.focus();
14763         }
14764     },
14765
14766     // private
14767     constrainXY : function(){
14768         if(this.constraintoviewport !== false){
14769             if(!this.viewSize){
14770                 if(this.container){
14771                     var s = this.container.getSize();
14772                     this.viewSize = [s.width, s.height];
14773                 }else{
14774                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14775                 }
14776             }
14777             var s = Roo.get(this.container||document).getScroll();
14778
14779             var x = this.xy[0], y = this.xy[1];
14780             var w = this.size.width, h = this.size.height;
14781             var vw = this.viewSize[0], vh = this.viewSize[1];
14782             // only move it if it needs it
14783             var moved = false;
14784             // first validate right/bottom
14785             if(x + w > vw+s.left){
14786                 x = vw - w;
14787                 moved = true;
14788             }
14789             if(y + h > vh+s.top){
14790                 y = vh - h;
14791                 moved = true;
14792             }
14793             // then make sure top/left isn't negative
14794             if(x < s.left){
14795                 x = s.left;
14796                 moved = true;
14797             }
14798             if(y < s.top){
14799                 y = s.top;
14800                 moved = true;
14801             }
14802             if(moved){
14803                 // cache xy
14804                 this.xy = [x, y];
14805                 if(this.isVisible()){
14806                     this.el.setLocation(x, y);
14807                     this.adjustAssets();
14808                 }
14809             }
14810         }
14811     },
14812
14813     // private
14814     onDrag : function(){
14815         if(!this.proxyDrag){
14816             this.xy = this.el.getXY();
14817             this.adjustAssets();
14818         }
14819     },
14820
14821     // private
14822     adjustAssets : function(doShow){
14823         var x = this.xy[0], y = this.xy[1];
14824         var w = this.size.width, h = this.size.height;
14825         if(doShow === true){
14826             if(this.shadow){
14827                 this.shadow.show(this.el);
14828             }
14829             if(this.shim){
14830                 this.shim.show();
14831             }
14832         }
14833         if(this.shadow && this.shadow.isVisible()){
14834             this.shadow.show(this.el);
14835         }
14836         if(this.shim && this.shim.isVisible()){
14837             this.shim.setBounds(x, y, w, h);
14838         }
14839     },
14840
14841     // private
14842     adjustViewport : function(w, h){
14843         if(!w || !h){
14844             w = Roo.lib.Dom.getViewWidth();
14845             h = Roo.lib.Dom.getViewHeight();
14846         }
14847         // cache the size
14848         this.viewSize = [w, h];
14849         if(this.modal && this.mask.isVisible()){
14850             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14851             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14852         }
14853         if(this.isVisible()){
14854             this.constrainXY();
14855         }
14856     },
14857
14858     /**
14859      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14860      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14861      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14862      */
14863     destroy : function(removeEl){
14864         if(this.isVisible()){
14865             this.animateTarget = null;
14866             this.hide();
14867         }
14868         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14869         if(this.tabs){
14870             this.tabs.destroy(removeEl);
14871         }
14872         Roo.destroy(
14873              this.shim,
14874              this.proxy,
14875              this.resizer,
14876              this.close,
14877              this.mask
14878         );
14879         if(this.dd){
14880             this.dd.unreg();
14881         }
14882         if(this.buttons){
14883            for(var i = 0, len = this.buttons.length; i < len; i++){
14884                this.buttons[i].destroy();
14885            }
14886         }
14887         this.el.removeAllListeners();
14888         if(removeEl === true){
14889             this.el.update("");
14890             this.el.remove();
14891         }
14892         Roo.DialogManager.unregister(this);
14893     },
14894
14895     // private
14896     startMove : function(){
14897         if(this.proxyDrag){
14898             this.proxy.show();
14899         }
14900         if(this.constraintoviewport !== false){
14901             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14902         }
14903     },
14904
14905     // private
14906     endMove : function(){
14907         if(!this.proxyDrag){
14908             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14909         }else{
14910             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14911             this.proxy.hide();
14912         }
14913         this.refreshSize();
14914         this.adjustAssets();
14915         this.focus();
14916         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14917     },
14918
14919     /**
14920      * Brings this dialog to the front of any other visible dialogs
14921      * @return {Roo.BasicDialog} this
14922      */
14923     toFront : function(){
14924         Roo.DialogManager.bringToFront(this);
14925         return this;
14926     },
14927
14928     /**
14929      * Sends this dialog to the back (under) of any other visible dialogs
14930      * @return {Roo.BasicDialog} this
14931      */
14932     toBack : function(){
14933         Roo.DialogManager.sendToBack(this);
14934         return this;
14935     },
14936
14937     /**
14938      * Centers this dialog in the viewport
14939      * @return {Roo.BasicDialog} this
14940      */
14941     center : function(){
14942         var xy = this.el.getCenterXY(true);
14943         this.moveTo(xy[0], xy[1]);
14944         return this;
14945     },
14946
14947     /**
14948      * Moves the dialog's top-left corner to the specified point
14949      * @param {Number} x
14950      * @param {Number} y
14951      * @return {Roo.BasicDialog} this
14952      */
14953     moveTo : function(x, y){
14954         this.xy = [x,y];
14955         if(this.isVisible()){
14956             this.el.setXY(this.xy);
14957             this.adjustAssets();
14958         }
14959         return this;
14960     },
14961
14962     /**
14963      * Aligns the dialog to the specified element
14964      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14965      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14966      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14967      * @return {Roo.BasicDialog} this
14968      */
14969     alignTo : function(element, position, offsets){
14970         this.xy = this.el.getAlignToXY(element, position, offsets);
14971         if(this.isVisible()){
14972             this.el.setXY(this.xy);
14973             this.adjustAssets();
14974         }
14975         return this;
14976     },
14977
14978     /**
14979      * Anchors an element to another element and realigns it when the window is resized.
14980      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14981      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14982      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14983      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14984      * is a number, it is used as the buffer delay (defaults to 50ms).
14985      * @return {Roo.BasicDialog} this
14986      */
14987     anchorTo : function(el, alignment, offsets, monitorScroll){
14988         var action = function(){
14989             this.alignTo(el, alignment, offsets);
14990         };
14991         Roo.EventManager.onWindowResize(action, this);
14992         var tm = typeof monitorScroll;
14993         if(tm != 'undefined'){
14994             Roo.EventManager.on(window, 'scroll', action, this,
14995                 {buffer: tm == 'number' ? monitorScroll : 50});
14996         }
14997         action.call(this);
14998         return this;
14999     },
15000
15001     /**
15002      * Returns true if the dialog is visible
15003      * @return {Boolean}
15004      */
15005     isVisible : function(){
15006         return this.el.isVisible();
15007     },
15008
15009     // private
15010     animHide : function(callback){
15011         var b = Roo.get(this.animateTarget).getBox();
15012         this.proxy.show();
15013         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15014         this.el.hide();
15015         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15016                     this.hideEl.createDelegate(this, [callback]));
15017     },
15018
15019     /**
15020      * Hides the dialog.
15021      * @param {Function} callback (optional) Function to call when the dialog is hidden
15022      * @return {Roo.BasicDialog} this
15023      */
15024     hide : function(callback){
15025         if (this.fireEvent("beforehide", this) === false){
15026             return;
15027         }
15028         if(this.shadow){
15029             this.shadow.hide();
15030         }
15031         if(this.shim) {
15032           this.shim.hide();
15033         }
15034         // sometimes animateTarget seems to get set.. causing problems...
15035         // this just double checks..
15036         if(this.animateTarget && Roo.get(this.animateTarget)) {
15037            this.animHide(callback);
15038         }else{
15039             this.el.hide();
15040             this.hideEl(callback);
15041         }
15042         return this;
15043     },
15044
15045     // private
15046     hideEl : function(callback){
15047         this.proxy.hide();
15048         if(this.modal){
15049             this.mask.hide();
15050             Roo.get(document.body).removeClass("x-body-masked");
15051         }
15052         this.fireEvent("hide", this);
15053         if(typeof callback == "function"){
15054             callback();
15055         }
15056     },
15057
15058     // private
15059     hideAction : function(){
15060         this.setLeft("-10000px");
15061         this.setTop("-10000px");
15062         this.setStyle("visibility", "hidden");
15063     },
15064
15065     // private
15066     refreshSize : function(){
15067         this.size = this.el.getSize();
15068         this.xy = this.el.getXY();
15069         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15070     },
15071
15072     // private
15073     // z-index is managed by the DialogManager and may be overwritten at any time
15074     setZIndex : function(index){
15075         if(this.modal){
15076             this.mask.setStyle("z-index", index);
15077         }
15078         if(this.shim){
15079             this.shim.setStyle("z-index", ++index);
15080         }
15081         if(this.shadow){
15082             this.shadow.setZIndex(++index);
15083         }
15084         this.el.setStyle("z-index", ++index);
15085         if(this.proxy){
15086             this.proxy.setStyle("z-index", ++index);
15087         }
15088         if(this.resizer){
15089             this.resizer.proxy.setStyle("z-index", ++index);
15090         }
15091
15092         this.lastZIndex = index;
15093     },
15094
15095     /**
15096      * Returns the element for this dialog
15097      * @return {Roo.Element} The underlying dialog Element
15098      */
15099     getEl : function(){
15100         return this.el;
15101     }
15102 });
15103
15104 /**
15105  * @class Roo.DialogManager
15106  * Provides global access to BasicDialogs that have been created and
15107  * support for z-indexing (layering) multiple open dialogs.
15108  */
15109 Roo.DialogManager = function(){
15110     var list = {};
15111     var accessList = [];
15112     var front = null;
15113
15114     // private
15115     var sortDialogs = function(d1, d2){
15116         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15117     };
15118
15119     // private
15120     var orderDialogs = function(){
15121         accessList.sort(sortDialogs);
15122         var seed = Roo.DialogManager.zseed;
15123         for(var i = 0, len = accessList.length; i < len; i++){
15124             var dlg = accessList[i];
15125             if(dlg){
15126                 dlg.setZIndex(seed + (i*10));
15127             }
15128         }
15129     };
15130
15131     return {
15132         /**
15133          * The starting z-index for BasicDialogs (defaults to 9000)
15134          * @type Number The z-index value
15135          */
15136         zseed : 9000,
15137
15138         // private
15139         register : function(dlg){
15140             list[dlg.id] = dlg;
15141             accessList.push(dlg);
15142         },
15143
15144         // private
15145         unregister : function(dlg){
15146             delete list[dlg.id];
15147             var i=0;
15148             var len=0;
15149             if(!accessList.indexOf){
15150                 for(  i = 0, len = accessList.length; i < len; i++){
15151                     if(accessList[i] == dlg){
15152                         accessList.splice(i, 1);
15153                         return;
15154                     }
15155                 }
15156             }else{
15157                  i = accessList.indexOf(dlg);
15158                 if(i != -1){
15159                     accessList.splice(i, 1);
15160                 }
15161             }
15162         },
15163
15164         /**
15165          * Gets a registered dialog by id
15166          * @param {String/Object} id The id of the dialog or a dialog
15167          * @return {Roo.BasicDialog} this
15168          */
15169         get : function(id){
15170             return typeof id == "object" ? id : list[id];
15171         },
15172
15173         /**
15174          * Brings the specified dialog to the front
15175          * @param {String/Object} dlg The id of the dialog or a dialog
15176          * @return {Roo.BasicDialog} this
15177          */
15178         bringToFront : function(dlg){
15179             dlg = this.get(dlg);
15180             if(dlg != front){
15181                 front = dlg;
15182                 dlg._lastAccess = new Date().getTime();
15183                 orderDialogs();
15184             }
15185             return dlg;
15186         },
15187
15188         /**
15189          * Sends the specified dialog to the back
15190          * @param {String/Object} dlg The id of the dialog or a dialog
15191          * @return {Roo.BasicDialog} this
15192          */
15193         sendToBack : function(dlg){
15194             dlg = this.get(dlg);
15195             dlg._lastAccess = -(new Date().getTime());
15196             orderDialogs();
15197             return dlg;
15198         },
15199
15200         /**
15201          * Hides all dialogs
15202          */
15203         hideAll : function(){
15204             for(var id in list){
15205                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15206                     list[id].hide();
15207                 }
15208             }
15209         }
15210     };
15211 }();
15212
15213 /**
15214  * @class Roo.LayoutDialog
15215  * @extends Roo.BasicDialog
15216  * Dialog which provides adjustments for working with a layout in a Dialog.
15217  * Add your necessary layout config options to the dialog's config.<br>
15218  * Example usage (including a nested layout):
15219  * <pre><code>
15220 if(!dialog){
15221     dialog = new Roo.LayoutDialog("download-dlg", {
15222         modal: true,
15223         width:600,
15224         height:450,
15225         shadow:true,
15226         minWidth:500,
15227         minHeight:350,
15228         autoTabs:true,
15229         proxyDrag:true,
15230         // layout config merges with the dialog config
15231         center:{
15232             tabPosition: "top",
15233             alwaysShowTabs: true
15234         }
15235     });
15236     dialog.addKeyListener(27, dialog.hide, dialog);
15237     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15238     dialog.addButton("Build It!", this.getDownload, this);
15239
15240     // we can even add nested layouts
15241     var innerLayout = new Roo.BorderLayout("dl-inner", {
15242         east: {
15243             initialSize: 200,
15244             autoScroll:true,
15245             split:true
15246         },
15247         center: {
15248             autoScroll:true
15249         }
15250     });
15251     innerLayout.beginUpdate();
15252     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15253     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15254     innerLayout.endUpdate(true);
15255
15256     var layout = dialog.getLayout();
15257     layout.beginUpdate();
15258     layout.add("center", new Roo.ContentPanel("standard-panel",
15259                         {title: "Download the Source", fitToFrame:true}));
15260     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15261                {title: "Build your own roo.js"}));
15262     layout.getRegion("center").showPanel(sp);
15263     layout.endUpdate();
15264 }
15265 </code></pre>
15266     * @constructor
15267     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15268     * @param {Object} config configuration options
15269   */
15270 Roo.LayoutDialog = function(el, cfg){
15271     
15272     var config=  cfg;
15273     if (typeof(cfg) == 'undefined') {
15274         config = Roo.apply({}, el);
15275         // not sure why we use documentElement here.. - it should always be body.
15276         // IE7 borks horribly if we use documentElement.
15277         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15278         //config.autoCreate = true;
15279     }
15280     
15281     
15282     config.autoTabs = false;
15283     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15284     this.body.setStyle({overflow:"hidden", position:"relative"});
15285     this.layout = new Roo.BorderLayout(this.body.dom, config);
15286     this.layout.monitorWindowResize = false;
15287     this.el.addClass("x-dlg-auto-layout");
15288     // fix case when center region overwrites center function
15289     this.center = Roo.BasicDialog.prototype.center;
15290     this.on("show", this.layout.layout, this.layout, true);
15291     if (config.items) {
15292         var xitems = config.items;
15293         delete config.items;
15294         Roo.each(xitems, this.addxtype, this);
15295     }
15296     
15297     
15298 };
15299 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15300     /**
15301      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15302      * @deprecated
15303      */
15304     endUpdate : function(){
15305         this.layout.endUpdate();
15306     },
15307
15308     /**
15309      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15310      *  @deprecated
15311      */
15312     beginUpdate : function(){
15313         this.layout.beginUpdate();
15314     },
15315
15316     /**
15317      * Get the BorderLayout for this dialog
15318      * @return {Roo.BorderLayout}
15319      */
15320     getLayout : function(){
15321         return this.layout;
15322     },
15323
15324     showEl : function(){
15325         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15326         if(Roo.isIE7){
15327             this.layout.layout();
15328         }
15329     },
15330
15331     // private
15332     // Use the syncHeightBeforeShow config option to control this automatically
15333     syncBodyHeight : function(){
15334         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15335         if(this.layout){this.layout.layout();}
15336     },
15337     
15338       /**
15339      * Add an xtype element (actually adds to the layout.)
15340      * @return {Object} xdata xtype object data.
15341      */
15342     
15343     addxtype : function(c) {
15344         return this.layout.addxtype(c);
15345     }
15346 });/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356  
15357 /**
15358  * @class Roo.MessageBox
15359  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15360  * Example usage:
15361  *<pre><code>
15362 // Basic alert:
15363 Roo.Msg.alert('Status', 'Changes saved successfully.');
15364
15365 // Prompt for user data:
15366 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15367     if (btn == 'ok'){
15368         // process text value...
15369     }
15370 });
15371
15372 // Show a dialog using config options:
15373 Roo.Msg.show({
15374    title:'Save Changes?',
15375    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15376    buttons: Roo.Msg.YESNOCANCEL,
15377    fn: processResult,
15378    animEl: 'elId'
15379 });
15380 </code></pre>
15381  * @singleton
15382  */
15383 Roo.MessageBox = function(){
15384     var dlg, opt, mask, waitTimer;
15385     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15386     var buttons, activeTextEl, bwidth;
15387
15388     // private
15389     var handleButton = function(button){
15390         dlg.hide();
15391         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15392     };
15393
15394     // private
15395     var handleHide = function(){
15396         if(opt && opt.cls){
15397             dlg.el.removeClass(opt.cls);
15398         }
15399         if(waitTimer){
15400             Roo.TaskMgr.stop(waitTimer);
15401             waitTimer = null;
15402         }
15403     };
15404
15405     // private
15406     var updateButtons = function(b){
15407         var width = 0;
15408         if(!b){
15409             buttons["ok"].hide();
15410             buttons["cancel"].hide();
15411             buttons["yes"].hide();
15412             buttons["no"].hide();
15413             dlg.footer.dom.style.display = 'none';
15414             return width;
15415         }
15416         dlg.footer.dom.style.display = '';
15417         for(var k in buttons){
15418             if(typeof buttons[k] != "function"){
15419                 if(b[k]){
15420                     buttons[k].show();
15421                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15422                     width += buttons[k].el.getWidth()+15;
15423                 }else{
15424                     buttons[k].hide();
15425                 }
15426             }
15427         }
15428         return width;
15429     };
15430
15431     // private
15432     var handleEsc = function(d, k, e){
15433         if(opt && opt.closable !== false){
15434             dlg.hide();
15435         }
15436         if(e){
15437             e.stopEvent();
15438         }
15439     };
15440
15441     return {
15442         /**
15443          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15444          * @return {Roo.BasicDialog} The BasicDialog element
15445          */
15446         getDialog : function(){
15447            if(!dlg){
15448                 dlg = new Roo.BasicDialog("x-msg-box", {
15449                     autoCreate : true,
15450                     shadow: true,
15451                     draggable: true,
15452                     resizable:false,
15453                     constraintoviewport:false,
15454                     fixedcenter:true,
15455                     collapsible : false,
15456                     shim:true,
15457                     modal: true,
15458                     width:400, height:100,
15459                     buttonAlign:"center",
15460                     closeClick : function(){
15461                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15462                             handleButton("no");
15463                         }else{
15464                             handleButton("cancel");
15465                         }
15466                     }
15467                 });
15468                 dlg.on("hide", handleHide);
15469                 mask = dlg.mask;
15470                 dlg.addKeyListener(27, handleEsc);
15471                 buttons = {};
15472                 var bt = this.buttonText;
15473                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15474                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15475                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15476                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15477                 bodyEl = dlg.body.createChild({
15478
15479                     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>'
15480                 });
15481                 msgEl = bodyEl.dom.firstChild;
15482                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15483                 textboxEl.enableDisplayMode();
15484                 textboxEl.addKeyListener([10,13], function(){
15485                     if(dlg.isVisible() && opt && opt.buttons){
15486                         if(opt.buttons.ok){
15487                             handleButton("ok");
15488                         }else if(opt.buttons.yes){
15489                             handleButton("yes");
15490                         }
15491                     }
15492                 });
15493                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15494                 textareaEl.enableDisplayMode();
15495                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15496                 progressEl.enableDisplayMode();
15497                 var pf = progressEl.dom.firstChild;
15498                 if (pf) {
15499                     pp = Roo.get(pf.firstChild);
15500                     pp.setHeight(pf.offsetHeight);
15501                 }
15502                 
15503             }
15504             return dlg;
15505         },
15506
15507         /**
15508          * Updates the message box body text
15509          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15510          * the XHTML-compliant non-breaking space character '&amp;#160;')
15511          * @return {Roo.MessageBox} This message box
15512          */
15513         updateText : function(text){
15514             if(!dlg.isVisible() && !opt.width){
15515                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15516             }
15517             msgEl.innerHTML = text || '&#160;';
15518             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15519                         Math.max(opt.minWidth || this.minWidth, bwidth));
15520             if(opt.prompt){
15521                 activeTextEl.setWidth(w);
15522             }
15523             if(dlg.isVisible()){
15524                 dlg.fixedcenter = false;
15525             }
15526             dlg.setContentSize(w, bodyEl.getHeight());
15527             if(dlg.isVisible()){
15528                 dlg.fixedcenter = true;
15529             }
15530             return this;
15531         },
15532
15533         /**
15534          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15535          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15536          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15537          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15538          * @return {Roo.MessageBox} This message box
15539          */
15540         updateProgress : function(value, text){
15541             if(text){
15542                 this.updateText(text);
15543             }
15544             if (pp) { // weird bug on my firefox - for some reason this is not defined
15545                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15546             }
15547             return this;
15548         },        
15549
15550         /**
15551          * Returns true if the message box is currently displayed
15552          * @return {Boolean} True if the message box is visible, else false
15553          */
15554         isVisible : function(){
15555             return dlg && dlg.isVisible();  
15556         },
15557
15558         /**
15559          * Hides the message box if it is displayed
15560          */
15561         hide : function(){
15562             if(this.isVisible()){
15563                 dlg.hide();
15564             }  
15565         },
15566
15567         /**
15568          * Displays a new message box, or reinitializes an existing message box, based on the config options
15569          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15570          * The following config object properties are supported:
15571          * <pre>
15572 Property    Type             Description
15573 ----------  ---------------  ------------------------------------------------------------------------------------
15574 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15575                                    closes (defaults to undefined)
15576 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15577                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15578 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15579                                    progress and wait dialogs will ignore this property and always hide the
15580                                    close button as they can only be closed programmatically.
15581 cls               String           A custom CSS class to apply to the message box element
15582 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15583                                    displayed (defaults to 75)
15584 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15585                                    function will be btn (the name of the button that was clicked, if applicable,
15586                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15587                                    Progress and wait dialogs will ignore this option since they do not respond to
15588                                    user actions and can only be closed programmatically, so any required function
15589                                    should be called by the same code after it closes the dialog.
15590 icon              String           A CSS class that provides a background image to be used as an icon for
15591                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15592 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15593 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15594 modal             Boolean          False to allow user interaction with the page while the message box is
15595                                    displayed (defaults to true)
15596 msg               String           A string that will replace the existing message box body text (defaults
15597                                    to the XHTML-compliant non-breaking space character '&#160;')
15598 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15599 progress          Boolean          True to display a progress bar (defaults to false)
15600 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15601 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15602 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15603 title             String           The title text
15604 value             String           The string value to set into the active textbox element if displayed
15605 wait              Boolean          True to display a progress bar (defaults to false)
15606 width             Number           The width of the dialog in pixels
15607 </pre>
15608          *
15609          * Example usage:
15610          * <pre><code>
15611 Roo.Msg.show({
15612    title: 'Address',
15613    msg: 'Please enter your address:',
15614    width: 300,
15615    buttons: Roo.MessageBox.OKCANCEL,
15616    multiline: true,
15617    fn: saveAddress,
15618    animEl: 'addAddressBtn'
15619 });
15620 </code></pre>
15621          * @param {Object} config Configuration options
15622          * @return {Roo.MessageBox} This message box
15623          */
15624         show : function(options){
15625             if(this.isVisible()){
15626                 this.hide();
15627             }
15628             var d = this.getDialog();
15629             opt = options;
15630             d.setTitle(opt.title || "&#160;");
15631             d.close.setDisplayed(opt.closable !== false);
15632             activeTextEl = textboxEl;
15633             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15634             if(opt.prompt){
15635                 if(opt.multiline){
15636                     textboxEl.hide();
15637                     textareaEl.show();
15638                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15639                         opt.multiline : this.defaultTextHeight);
15640                     activeTextEl = textareaEl;
15641                 }else{
15642                     textboxEl.show();
15643                     textareaEl.hide();
15644                 }
15645             }else{
15646                 textboxEl.hide();
15647                 textareaEl.hide();
15648             }
15649             progressEl.setDisplayed(opt.progress === true);
15650             this.updateProgress(0);
15651             activeTextEl.dom.value = opt.value || "";
15652             if(opt.prompt){
15653                 dlg.setDefaultButton(activeTextEl);
15654             }else{
15655                 var bs = opt.buttons;
15656                 var db = null;
15657                 if(bs && bs.ok){
15658                     db = buttons["ok"];
15659                 }else if(bs && bs.yes){
15660                     db = buttons["yes"];
15661                 }
15662                 dlg.setDefaultButton(db);
15663             }
15664             bwidth = updateButtons(opt.buttons);
15665             this.updateText(opt.msg);
15666             if(opt.cls){
15667                 d.el.addClass(opt.cls);
15668             }
15669             d.proxyDrag = opt.proxyDrag === true;
15670             d.modal = opt.modal !== false;
15671             d.mask = opt.modal !== false ? mask : false;
15672             if(!d.isVisible()){
15673                 // force it to the end of the z-index stack so it gets a cursor in FF
15674                 document.body.appendChild(dlg.el.dom);
15675                 d.animateTarget = null;
15676                 d.show(options.animEl);
15677             }
15678             return this;
15679         },
15680
15681         /**
15682          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15683          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15684          * and closing the message box when the process is complete.
15685          * @param {String} title The title bar text
15686          * @param {String} msg The message box body text
15687          * @return {Roo.MessageBox} This message box
15688          */
15689         progress : function(title, msg){
15690             this.show({
15691                 title : title,
15692                 msg : msg,
15693                 buttons: false,
15694                 progress:true,
15695                 closable:false,
15696                 minWidth: this.minProgressWidth,
15697                 modal : true
15698             });
15699             return this;
15700         },
15701
15702         /**
15703          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15704          * If a callback function is passed it will be called after the user clicks the button, and the
15705          * id of the button that was clicked will be passed as the only parameter to the callback
15706          * (could also be the top-right close button).
15707          * @param {String} title The title bar text
15708          * @param {String} msg The message box body text
15709          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15710          * @param {Object} scope (optional) The scope of the callback function
15711          * @return {Roo.MessageBox} This message box
15712          */
15713         alert : function(title, msg, fn, scope){
15714             this.show({
15715                 title : title,
15716                 msg : msg,
15717                 buttons: this.OK,
15718                 fn: fn,
15719                 scope : scope,
15720                 modal : true
15721             });
15722             return this;
15723         },
15724
15725         /**
15726          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15727          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15728          * You are responsible for closing the message box when the process is complete.
15729          * @param {String} msg The message box body text
15730          * @param {String} title (optional) The title bar text
15731          * @return {Roo.MessageBox} This message box
15732          */
15733         wait : function(msg, title){
15734             this.show({
15735                 title : title,
15736                 msg : msg,
15737                 buttons: false,
15738                 closable:false,
15739                 progress:true,
15740                 modal:true,
15741                 width:300,
15742                 wait:true
15743             });
15744             waitTimer = Roo.TaskMgr.start({
15745                 run: function(i){
15746                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15747                 },
15748                 interval: 1000
15749             });
15750             return this;
15751         },
15752
15753         /**
15754          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15755          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15756          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15757          * @param {String} title The title bar text
15758          * @param {String} msg The message box body text
15759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15760          * @param {Object} scope (optional) The scope of the callback function
15761          * @return {Roo.MessageBox} This message box
15762          */
15763         confirm : function(title, msg, fn, scope){
15764             this.show({
15765                 title : title,
15766                 msg : msg,
15767                 buttons: this.YESNO,
15768                 fn: fn,
15769                 scope : scope,
15770                 modal : true
15771             });
15772             return this;
15773         },
15774
15775         /**
15776          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15777          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15778          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15779          * (could also be the top-right close button) and the text that was entered will be passed as the two
15780          * parameters to the callback.
15781          * @param {String} title The title bar text
15782          * @param {String} msg The message box body text
15783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15784          * @param {Object} scope (optional) The scope of the callback function
15785          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15786          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15787          * @return {Roo.MessageBox} This message box
15788          */
15789         prompt : function(title, msg, fn, scope, multiline){
15790             this.show({
15791                 title : title,
15792                 msg : msg,
15793                 buttons: this.OKCANCEL,
15794                 fn: fn,
15795                 minWidth:250,
15796                 scope : scope,
15797                 prompt:true,
15798                 multiline: multiline,
15799                 modal : true
15800             });
15801             return this;
15802         },
15803
15804         /**
15805          * Button config that displays a single OK button
15806          * @type Object
15807          */
15808         OK : {ok:true},
15809         /**
15810          * Button config that displays Yes and No buttons
15811          * @type Object
15812          */
15813         YESNO : {yes:true, no:true},
15814         /**
15815          * Button config that displays OK and Cancel buttons
15816          * @type Object
15817          */
15818         OKCANCEL : {ok:true, cancel:true},
15819         /**
15820          * Button config that displays Yes, No and Cancel buttons
15821          * @type Object
15822          */
15823         YESNOCANCEL : {yes:true, no:true, cancel:true},
15824
15825         /**
15826          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15827          * @type Number
15828          */
15829         defaultTextHeight : 75,
15830         /**
15831          * The maximum width in pixels of the message box (defaults to 600)
15832          * @type Number
15833          */
15834         maxWidth : 600,
15835         /**
15836          * The minimum width in pixels of the message box (defaults to 100)
15837          * @type Number
15838          */
15839         minWidth : 100,
15840         /**
15841          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15842          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15843          * @type Number
15844          */
15845         minProgressWidth : 250,
15846         /**
15847          * An object containing the default button text strings that can be overriden for localized language support.
15848          * Supported properties are: ok, cancel, yes and no.
15849          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15850          * @type Object
15851          */
15852         buttonText : {
15853             ok : "OK",
15854             cancel : "Cancel",
15855             yes : "Yes",
15856             no : "No"
15857         }
15858     };
15859 }();
15860
15861 /**
15862  * Shorthand for {@link Roo.MessageBox}
15863  */
15864 Roo.Msg = Roo.MessageBox;/*
15865  * Based on:
15866  * Ext JS Library 1.1.1
15867  * Copyright(c) 2006-2007, Ext JS, LLC.
15868  *
15869  * Originally Released Under LGPL - original licence link has changed is not relivant.
15870  *
15871  * Fork - LGPL
15872  * <script type="text/javascript">
15873  */
15874 /**
15875  * @class Roo.QuickTips
15876  * Provides attractive and customizable tooltips for any element.
15877  * @singleton
15878  */
15879 Roo.QuickTips = function(){
15880     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15881     var ce, bd, xy, dd;
15882     var visible = false, disabled = true, inited = false;
15883     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15884     
15885     var onOver = function(e){
15886         if(disabled){
15887             return;
15888         }
15889         var t = e.getTarget();
15890         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15891             return;
15892         }
15893         if(ce && t == ce.el){
15894             clearTimeout(hideProc);
15895             return;
15896         }
15897         if(t && tagEls[t.id]){
15898             tagEls[t.id].el = t;
15899             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15900             return;
15901         }
15902         var ttp, et = Roo.fly(t);
15903         var ns = cfg.namespace;
15904         if(tm.interceptTitles && t.title){
15905             ttp = t.title;
15906             t.qtip = ttp;
15907             t.removeAttribute("title");
15908             e.preventDefault();
15909         }else{
15910             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15911         }
15912         if(ttp){
15913             showProc = show.defer(tm.showDelay, tm, [{
15914                 el: t, 
15915                 text: ttp, 
15916                 width: et.getAttributeNS(ns, cfg.width),
15917                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15918                 title: et.getAttributeNS(ns, cfg.title),
15919                     cls: et.getAttributeNS(ns, cfg.cls)
15920             }]);
15921         }
15922     };
15923     
15924     var onOut = function(e){
15925         clearTimeout(showProc);
15926         var t = e.getTarget();
15927         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15928             hideProc = setTimeout(hide, tm.hideDelay);
15929         }
15930     };
15931     
15932     var onMove = function(e){
15933         if(disabled){
15934             return;
15935         }
15936         xy = e.getXY();
15937         xy[1] += 18;
15938         if(tm.trackMouse && ce){
15939             el.setXY(xy);
15940         }
15941     };
15942     
15943     var onDown = function(e){
15944         clearTimeout(showProc);
15945         clearTimeout(hideProc);
15946         if(!e.within(el)){
15947             if(tm.hideOnClick){
15948                 hide();
15949                 tm.disable();
15950                 tm.enable.defer(100, tm);
15951             }
15952         }
15953     };
15954     
15955     var getPad = function(){
15956         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15957     };
15958
15959     var show = function(o){
15960         if(disabled){
15961             return;
15962         }
15963         clearTimeout(dismissProc);
15964         ce = o;
15965         if(removeCls){ // in case manually hidden
15966             el.removeClass(removeCls);
15967             removeCls = null;
15968         }
15969         if(ce.cls){
15970             el.addClass(ce.cls);
15971             removeCls = ce.cls;
15972         }
15973         if(ce.title){
15974             tipTitle.update(ce.title);
15975             tipTitle.show();
15976         }else{
15977             tipTitle.update('');
15978             tipTitle.hide();
15979         }
15980         el.dom.style.width  = tm.maxWidth+'px';
15981         //tipBody.dom.style.width = '';
15982         tipBodyText.update(o.text);
15983         var p = getPad(), w = ce.width;
15984         if(!w){
15985             var td = tipBodyText.dom;
15986             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15987             if(aw > tm.maxWidth){
15988                 w = tm.maxWidth;
15989             }else if(aw < tm.minWidth){
15990                 w = tm.minWidth;
15991             }else{
15992                 w = aw;
15993             }
15994         }
15995         //tipBody.setWidth(w);
15996         el.setWidth(parseInt(w, 10) + p);
15997         if(ce.autoHide === false){
15998             close.setDisplayed(true);
15999             if(dd){
16000                 dd.unlock();
16001             }
16002         }else{
16003             close.setDisplayed(false);
16004             if(dd){
16005                 dd.lock();
16006             }
16007         }
16008         if(xy){
16009             el.avoidY = xy[1]-18;
16010             el.setXY(xy);
16011         }
16012         if(tm.animate){
16013             el.setOpacity(.1);
16014             el.setStyle("visibility", "visible");
16015             el.fadeIn({callback: afterShow});
16016         }else{
16017             afterShow();
16018         }
16019     };
16020     
16021     var afterShow = function(){
16022         if(ce){
16023             el.show();
16024             esc.enable();
16025             if(tm.autoDismiss && ce.autoHide !== false){
16026                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16027             }
16028         }
16029     };
16030     
16031     var hide = function(noanim){
16032         clearTimeout(dismissProc);
16033         clearTimeout(hideProc);
16034         ce = null;
16035         if(el.isVisible()){
16036             esc.disable();
16037             if(noanim !== true && tm.animate){
16038                 el.fadeOut({callback: afterHide});
16039             }else{
16040                 afterHide();
16041             } 
16042         }
16043     };
16044     
16045     var afterHide = function(){
16046         el.hide();
16047         if(removeCls){
16048             el.removeClass(removeCls);
16049             removeCls = null;
16050         }
16051     };
16052     
16053     return {
16054         /**
16055         * @cfg {Number} minWidth
16056         * The minimum width of the quick tip (defaults to 40)
16057         */
16058        minWidth : 40,
16059         /**
16060         * @cfg {Number} maxWidth
16061         * The maximum width of the quick tip (defaults to 300)
16062         */
16063        maxWidth : 300,
16064         /**
16065         * @cfg {Boolean} interceptTitles
16066         * True to automatically use the element's DOM title value if available (defaults to false)
16067         */
16068        interceptTitles : false,
16069         /**
16070         * @cfg {Boolean} trackMouse
16071         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16072         */
16073        trackMouse : false,
16074         /**
16075         * @cfg {Boolean} hideOnClick
16076         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16077         */
16078        hideOnClick : true,
16079         /**
16080         * @cfg {Number} showDelay
16081         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16082         */
16083        showDelay : 500,
16084         /**
16085         * @cfg {Number} hideDelay
16086         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16087         */
16088        hideDelay : 200,
16089         /**
16090         * @cfg {Boolean} autoHide
16091         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16092         * Used in conjunction with hideDelay.
16093         */
16094        autoHide : true,
16095         /**
16096         * @cfg {Boolean}
16097         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16098         * (defaults to true).  Used in conjunction with autoDismissDelay.
16099         */
16100        autoDismiss : true,
16101         /**
16102         * @cfg {Number}
16103         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16104         */
16105        autoDismissDelay : 5000,
16106        /**
16107         * @cfg {Boolean} animate
16108         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16109         */
16110        animate : false,
16111
16112        /**
16113         * @cfg {String} title
16114         * Title text to display (defaults to '').  This can be any valid HTML markup.
16115         */
16116         title: '',
16117        /**
16118         * @cfg {String} text
16119         * Body text to display (defaults to '').  This can be any valid HTML markup.
16120         */
16121         text : '',
16122        /**
16123         * @cfg {String} cls
16124         * A CSS class to apply to the base quick tip element (defaults to '').
16125         */
16126         cls : '',
16127        /**
16128         * @cfg {Number} width
16129         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16130         * minWidth or maxWidth.
16131         */
16132         width : null,
16133
16134     /**
16135      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16136      * or display QuickTips in a page.
16137      */
16138        init : function(){
16139           tm = Roo.QuickTips;
16140           cfg = tm.tagConfig;
16141           if(!inited){
16142               if(!Roo.isReady){ // allow calling of init() before onReady
16143                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16144                   return;
16145               }
16146               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16147               el.fxDefaults = {stopFx: true};
16148               // maximum custom styling
16149               //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>');
16150               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>');              
16151               tipTitle = el.child('h3');
16152               tipTitle.enableDisplayMode("block");
16153               tipBody = el.child('div.x-tip-bd');
16154               tipBodyText = el.child('div.x-tip-bd-inner');
16155               //bdLeft = el.child('div.x-tip-bd-left');
16156               //bdRight = el.child('div.x-tip-bd-right');
16157               close = el.child('div.x-tip-close');
16158               close.enableDisplayMode("block");
16159               close.on("click", hide);
16160               var d = Roo.get(document);
16161               d.on("mousedown", onDown);
16162               d.on("mouseover", onOver);
16163               d.on("mouseout", onOut);
16164               d.on("mousemove", onMove);
16165               esc = d.addKeyListener(27, hide);
16166               esc.disable();
16167               if(Roo.dd.DD){
16168                   dd = el.initDD("default", null, {
16169                       onDrag : function(){
16170                           el.sync();  
16171                       }
16172                   });
16173                   dd.setHandleElId(tipTitle.id);
16174                   dd.lock();
16175               }
16176               inited = true;
16177           }
16178           this.enable(); 
16179        },
16180
16181     /**
16182      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16183      * are supported:
16184      * <pre>
16185 Property    Type                   Description
16186 ----------  ---------------------  ------------------------------------------------------------------------
16187 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16188      * </ul>
16189      * @param {Object} config The config object
16190      */
16191        register : function(config){
16192            var cs = config instanceof Array ? config : arguments;
16193            for(var i = 0, len = cs.length; i < len; i++) {
16194                var c = cs[i];
16195                var target = c.target;
16196                if(target){
16197                    if(target instanceof Array){
16198                        for(var j = 0, jlen = target.length; j < jlen; j++){
16199                            tagEls[target[j]] = c;
16200                        }
16201                    }else{
16202                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16203                    }
16204                }
16205            }
16206        },
16207
16208     /**
16209      * Removes this quick tip from its element and destroys it.
16210      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16211      */
16212        unregister : function(el){
16213            delete tagEls[Roo.id(el)];
16214        },
16215
16216     /**
16217      * Enable this quick tip.
16218      */
16219        enable : function(){
16220            if(inited && disabled){
16221                locks.pop();
16222                if(locks.length < 1){
16223                    disabled = false;
16224                }
16225            }
16226        },
16227
16228     /**
16229      * Disable this quick tip.
16230      */
16231        disable : function(){
16232           disabled = true;
16233           clearTimeout(showProc);
16234           clearTimeout(hideProc);
16235           clearTimeout(dismissProc);
16236           if(ce){
16237               hide(true);
16238           }
16239           locks.push(1);
16240        },
16241
16242     /**
16243      * Returns true if the quick tip is enabled, else false.
16244      */
16245        isEnabled : function(){
16246             return !disabled;
16247        },
16248
16249         // private
16250        tagConfig : {
16251            namespace : "ext",
16252            attribute : "qtip",
16253            width : "width",
16254            target : "target",
16255            title : "qtitle",
16256            hide : "hide",
16257            cls : "qclass"
16258        }
16259    };
16260 }();
16261
16262 // backwards compat
16263 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16264  * Based on:
16265  * Ext JS Library 1.1.1
16266  * Copyright(c) 2006-2007, Ext JS, LLC.
16267  *
16268  * Originally Released Under LGPL - original licence link has changed is not relivant.
16269  *
16270  * Fork - LGPL
16271  * <script type="text/javascript">
16272  */
16273  
16274
16275 /**
16276  * @class Roo.tree.TreePanel
16277  * @extends Roo.data.Tree
16278
16279  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16280  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16281  * @cfg {Boolean} enableDD true to enable drag and drop
16282  * @cfg {Boolean} enableDrag true to enable just drag
16283  * @cfg {Boolean} enableDrop true to enable just drop
16284  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16285  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16286  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16287  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16288  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16289  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16290  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16291  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16292  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16293  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16294  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16295  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16296  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16297  * @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>
16298  * @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>
16299  * 
16300  * @constructor
16301  * @param {String/HTMLElement/Element} el The container element
16302  * @param {Object} config
16303  */
16304 Roo.tree.TreePanel = function(el, config){
16305     var root = false;
16306     var loader = false;
16307     if (config.root) {
16308         root = config.root;
16309         delete config.root;
16310     }
16311     if (config.loader) {
16312         loader = config.loader;
16313         delete config.loader;
16314     }
16315     
16316     Roo.apply(this, config);
16317     Roo.tree.TreePanel.superclass.constructor.call(this);
16318     this.el = Roo.get(el);
16319     this.el.addClass('x-tree');
16320     //console.log(root);
16321     if (root) {
16322         this.setRootNode( Roo.factory(root, Roo.tree));
16323     }
16324     if (loader) {
16325         this.loader = Roo.factory(loader, Roo.tree);
16326     }
16327    /**
16328     * Read-only. The id of the container element becomes this TreePanel's id.
16329     */
16330    this.id = this.el.id;
16331    this.addEvents({
16332         /**
16333         * @event beforeload
16334         * Fires before a node is loaded, return false to cancel
16335         * @param {Node} node The node being loaded
16336         */
16337         "beforeload" : true,
16338         /**
16339         * @event load
16340         * Fires when a node is loaded
16341         * @param {Node} node The node that was loaded
16342         */
16343         "load" : true,
16344         /**
16345         * @event textchange
16346         * Fires when the text for a node is changed
16347         * @param {Node} node The node
16348         * @param {String} text The new text
16349         * @param {String} oldText The old text
16350         */
16351         "textchange" : true,
16352         /**
16353         * @event beforeexpand
16354         * Fires before a node is expanded, return false to cancel.
16355         * @param {Node} node The node
16356         * @param {Boolean} deep
16357         * @param {Boolean} anim
16358         */
16359         "beforeexpand" : true,
16360         /**
16361         * @event beforecollapse
16362         * Fires before a node is collapsed, return false to cancel.
16363         * @param {Node} node The node
16364         * @param {Boolean} deep
16365         * @param {Boolean} anim
16366         */
16367         "beforecollapse" : true,
16368         /**
16369         * @event expand
16370         * Fires when a node is expanded
16371         * @param {Node} node The node
16372         */
16373         "expand" : true,
16374         /**
16375         * @event disabledchange
16376         * Fires when the disabled status of a node changes
16377         * @param {Node} node The node
16378         * @param {Boolean} disabled
16379         */
16380         "disabledchange" : true,
16381         /**
16382         * @event collapse
16383         * Fires when a node is collapsed
16384         * @param {Node} node The node
16385         */
16386         "collapse" : true,
16387         /**
16388         * @event beforeclick
16389         * Fires before click processing on a node. Return false to cancel the default action.
16390         * @param {Node} node The node
16391         * @param {Roo.EventObject} e The event object
16392         */
16393         "beforeclick":true,
16394         /**
16395         * @event checkchange
16396         * Fires when a node with a checkbox's checked property changes
16397         * @param {Node} this This node
16398         * @param {Boolean} checked
16399         */
16400         "checkchange":true,
16401         /**
16402         * @event click
16403         * Fires when a node is clicked
16404         * @param {Node} node The node
16405         * @param {Roo.EventObject} e The event object
16406         */
16407         "click":true,
16408         /**
16409         * @event dblclick
16410         * Fires when a node is double clicked
16411         * @param {Node} node The node
16412         * @param {Roo.EventObject} e The event object
16413         */
16414         "dblclick":true,
16415         /**
16416         * @event contextmenu
16417         * Fires when a node is right clicked
16418         * @param {Node} node The node
16419         * @param {Roo.EventObject} e The event object
16420         */
16421         "contextmenu":true,
16422         /**
16423         * @event beforechildrenrendered
16424         * Fires right before the child nodes for a node are rendered
16425         * @param {Node} node The node
16426         */
16427         "beforechildrenrendered":true,
16428        /**
16429              * @event startdrag
16430              * Fires when a node starts being dragged
16431              * @param {Roo.tree.TreePanel} this
16432              * @param {Roo.tree.TreeNode} node
16433              * @param {event} e The raw browser event
16434              */ 
16435             "startdrag" : true,
16436             /**
16437              * @event enddrag
16438              * Fires when a drag operation is complete
16439              * @param {Roo.tree.TreePanel} this
16440              * @param {Roo.tree.TreeNode} node
16441              * @param {event} e The raw browser event
16442              */
16443             "enddrag" : true,
16444             /**
16445              * @event dragdrop
16446              * Fires when a dragged node is dropped on a valid DD target
16447              * @param {Roo.tree.TreePanel} this
16448              * @param {Roo.tree.TreeNode} node
16449              * @param {DD} dd The dd it was dropped on
16450              * @param {event} e The raw browser event
16451              */
16452             "dragdrop" : true,
16453             /**
16454              * @event beforenodedrop
16455              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16456              * passed to handlers has the following properties:<br />
16457              * <ul style="padding:5px;padding-left:16px;">
16458              * <li>tree - The TreePanel</li>
16459              * <li>target - The node being targeted for the drop</li>
16460              * <li>data - The drag data from the drag source</li>
16461              * <li>point - The point of the drop - append, above or below</li>
16462              * <li>source - The drag source</li>
16463              * <li>rawEvent - Raw mouse event</li>
16464              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16465              * to be inserted by setting them on this object.</li>
16466              * <li>cancel - Set this to true to cancel the drop.</li>
16467              * </ul>
16468              * @param {Object} dropEvent
16469              */
16470             "beforenodedrop" : true,
16471             /**
16472              * @event nodedrop
16473              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16474              * passed to handlers has the following properties:<br />
16475              * <ul style="padding:5px;padding-left:16px;">
16476              * <li>tree - The TreePanel</li>
16477              * <li>target - The node being targeted for the drop</li>
16478              * <li>data - The drag data from the drag source</li>
16479              * <li>point - The point of the drop - append, above or below</li>
16480              * <li>source - The drag source</li>
16481              * <li>rawEvent - Raw mouse event</li>
16482              * <li>dropNode - Dropped node(s).</li>
16483              * </ul>
16484              * @param {Object} dropEvent
16485              */
16486             "nodedrop" : true,
16487              /**
16488              * @event nodedragover
16489              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16490              * passed to handlers has the following properties:<br />
16491              * <ul style="padding:5px;padding-left:16px;">
16492              * <li>tree - The TreePanel</li>
16493              * <li>target - The node being targeted for the drop</li>
16494              * <li>data - The drag data from the drag source</li>
16495              * <li>point - The point of the drop - append, above or below</li>
16496              * <li>source - The drag source</li>
16497              * <li>rawEvent - Raw mouse event</li>
16498              * <li>dropNode - Drop node(s) provided by the source.</li>
16499              * <li>cancel - Set this to true to signal drop not allowed.</li>
16500              * </ul>
16501              * @param {Object} dragOverEvent
16502              */
16503             "nodedragover" : true
16504         
16505    });
16506    if(this.singleExpand){
16507        this.on("beforeexpand", this.restrictExpand, this);
16508    }
16509 };
16510 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16511     rootVisible : true,
16512     animate: Roo.enableFx,
16513     lines : true,
16514     enableDD : false,
16515     hlDrop : Roo.enableFx,
16516   
16517     renderer: false,
16518     
16519     rendererTip: false,
16520     // private
16521     restrictExpand : function(node){
16522         var p = node.parentNode;
16523         if(p){
16524             if(p.expandedChild && p.expandedChild.parentNode == p){
16525                 p.expandedChild.collapse();
16526             }
16527             p.expandedChild = node;
16528         }
16529     },
16530
16531     // private override
16532     setRootNode : function(node){
16533         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16534         if(!this.rootVisible){
16535             node.ui = new Roo.tree.RootTreeNodeUI(node);
16536         }
16537         return node;
16538     },
16539
16540     /**
16541      * Returns the container element for this TreePanel
16542      */
16543     getEl : function(){
16544         return this.el;
16545     },
16546
16547     /**
16548      * Returns the default TreeLoader for this TreePanel
16549      */
16550     getLoader : function(){
16551         return this.loader;
16552     },
16553
16554     /**
16555      * Expand all nodes
16556      */
16557     expandAll : function(){
16558         this.root.expand(true);
16559     },
16560
16561     /**
16562      * Collapse all nodes
16563      */
16564     collapseAll : function(){
16565         this.root.collapse(true);
16566     },
16567
16568     /**
16569      * Returns the selection model used by this TreePanel
16570      */
16571     getSelectionModel : function(){
16572         if(!this.selModel){
16573             this.selModel = new Roo.tree.DefaultSelectionModel();
16574         }
16575         return this.selModel;
16576     },
16577
16578     /**
16579      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16580      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16581      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16582      * @return {Array}
16583      */
16584     getChecked : function(a, startNode){
16585         startNode = startNode || this.root;
16586         var r = [];
16587         var f = function(){
16588             if(this.attributes.checked){
16589                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16590             }
16591         }
16592         startNode.cascade(f);
16593         return r;
16594     },
16595
16596     /**
16597      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16598      * @param {String} path
16599      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16600      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16601      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16602      */
16603     expandPath : function(path, attr, callback){
16604         attr = attr || "id";
16605         var keys = path.split(this.pathSeparator);
16606         var curNode = this.root;
16607         if(curNode.attributes[attr] != keys[1]){ // invalid root
16608             if(callback){
16609                 callback(false, null);
16610             }
16611             return;
16612         }
16613         var index = 1;
16614         var f = function(){
16615             if(++index == keys.length){
16616                 if(callback){
16617                     callback(true, curNode);
16618                 }
16619                 return;
16620             }
16621             var c = curNode.findChild(attr, keys[index]);
16622             if(!c){
16623                 if(callback){
16624                     callback(false, curNode);
16625                 }
16626                 return;
16627             }
16628             curNode = c;
16629             c.expand(false, false, f);
16630         };
16631         curNode.expand(false, false, f);
16632     },
16633
16634     /**
16635      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16636      * @param {String} path
16637      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16638      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16639      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16640      */
16641     selectPath : function(path, attr, callback){
16642         attr = attr || "id";
16643         var keys = path.split(this.pathSeparator);
16644         var v = keys.pop();
16645         if(keys.length > 0){
16646             var f = function(success, node){
16647                 if(success && node){
16648                     var n = node.findChild(attr, v);
16649                     if(n){
16650                         n.select();
16651                         if(callback){
16652                             callback(true, n);
16653                         }
16654                     }else if(callback){
16655                         callback(false, n);
16656                     }
16657                 }else{
16658                     if(callback){
16659                         callback(false, n);
16660                     }
16661                 }
16662             };
16663             this.expandPath(keys.join(this.pathSeparator), attr, f);
16664         }else{
16665             this.root.select();
16666             if(callback){
16667                 callback(true, this.root);
16668             }
16669         }
16670     },
16671
16672     getTreeEl : function(){
16673         return this.el;
16674     },
16675
16676     /**
16677      * Trigger rendering of this TreePanel
16678      */
16679     render : function(){
16680         if (this.innerCt) {
16681             return this; // stop it rendering more than once!!
16682         }
16683         
16684         this.innerCt = this.el.createChild({tag:"ul",
16685                cls:"x-tree-root-ct " +
16686                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16687
16688         if(this.containerScroll){
16689             Roo.dd.ScrollManager.register(this.el);
16690         }
16691         if((this.enableDD || this.enableDrop) && !this.dropZone){
16692            /**
16693             * The dropZone used by this tree if drop is enabled
16694             * @type Roo.tree.TreeDropZone
16695             */
16696              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16697                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16698            });
16699         }
16700         if((this.enableDD || this.enableDrag) && !this.dragZone){
16701            /**
16702             * The dragZone used by this tree if drag is enabled
16703             * @type Roo.tree.TreeDragZone
16704             */
16705             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16706                ddGroup: this.ddGroup || "TreeDD",
16707                scroll: this.ddScroll
16708            });
16709         }
16710         this.getSelectionModel().init(this);
16711         if (!this.root) {
16712             console.log("ROOT not set in tree");
16713             return;
16714         }
16715         this.root.render();
16716         if(!this.rootVisible){
16717             this.root.renderChildren();
16718         }
16719         return this;
16720     }
16721 });/*
16722  * Based on:
16723  * Ext JS Library 1.1.1
16724  * Copyright(c) 2006-2007, Ext JS, LLC.
16725  *
16726  * Originally Released Under LGPL - original licence link has changed is not relivant.
16727  *
16728  * Fork - LGPL
16729  * <script type="text/javascript">
16730  */
16731  
16732
16733 /**
16734  * @class Roo.tree.DefaultSelectionModel
16735  * @extends Roo.util.Observable
16736  * The default single selection for a TreePanel.
16737  */
16738 Roo.tree.DefaultSelectionModel = function(){
16739    this.selNode = null;
16740    
16741    this.addEvents({
16742        /**
16743         * @event selectionchange
16744         * Fires when the selected node changes
16745         * @param {DefaultSelectionModel} this
16746         * @param {TreeNode} node the new selection
16747         */
16748        "selectionchange" : true,
16749
16750        /**
16751         * @event beforeselect
16752         * Fires before the selected node changes, return false to cancel the change
16753         * @param {DefaultSelectionModel} this
16754         * @param {TreeNode} node the new selection
16755         * @param {TreeNode} node the old selection
16756         */
16757        "beforeselect" : true
16758    });
16759 };
16760
16761 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16762     init : function(tree){
16763         this.tree = tree;
16764         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16765         tree.on("click", this.onNodeClick, this);
16766     },
16767     
16768     onNodeClick : function(node, e){
16769         if (e.ctrlKey && this.selNode == node)  {
16770             this.unselect(node);
16771             return;
16772         }
16773         this.select(node);
16774     },
16775     
16776     /**
16777      * Select a node.
16778      * @param {TreeNode} node The node to select
16779      * @return {TreeNode} The selected node
16780      */
16781     select : function(node){
16782         var last = this.selNode;
16783         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16784             if(last){
16785                 last.ui.onSelectedChange(false);
16786             }
16787             this.selNode = node;
16788             node.ui.onSelectedChange(true);
16789             this.fireEvent("selectionchange", this, node, last);
16790         }
16791         return node;
16792     },
16793     
16794     /**
16795      * Deselect a node.
16796      * @param {TreeNode} node The node to unselect
16797      */
16798     unselect : function(node){
16799         if(this.selNode == node){
16800             this.clearSelections();
16801         }    
16802     },
16803     
16804     /**
16805      * Clear all selections
16806      */
16807     clearSelections : function(){
16808         var n = this.selNode;
16809         if(n){
16810             n.ui.onSelectedChange(false);
16811             this.selNode = null;
16812             this.fireEvent("selectionchange", this, null);
16813         }
16814         return n;
16815     },
16816     
16817     /**
16818      * Get the selected node
16819      * @return {TreeNode} The selected node
16820      */
16821     getSelectedNode : function(){
16822         return this.selNode;    
16823     },
16824     
16825     /**
16826      * Returns true if the node is selected
16827      * @param {TreeNode} node The node to check
16828      * @return {Boolean}
16829      */
16830     isSelected : function(node){
16831         return this.selNode == node;  
16832     },
16833
16834     /**
16835      * Selects the node above the selected node in the tree, intelligently walking the nodes
16836      * @return TreeNode The new selection
16837      */
16838     selectPrevious : function(){
16839         var s = this.selNode || this.lastSelNode;
16840         if(!s){
16841             return null;
16842         }
16843         var ps = s.previousSibling;
16844         if(ps){
16845             if(!ps.isExpanded() || ps.childNodes.length < 1){
16846                 return this.select(ps);
16847             } else{
16848                 var lc = ps.lastChild;
16849                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16850                     lc = lc.lastChild;
16851                 }
16852                 return this.select(lc);
16853             }
16854         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16855             return this.select(s.parentNode);
16856         }
16857         return null;
16858     },
16859
16860     /**
16861      * Selects the node above the selected node in the tree, intelligently walking the nodes
16862      * @return TreeNode The new selection
16863      */
16864     selectNext : function(){
16865         var s = this.selNode || this.lastSelNode;
16866         if(!s){
16867             return null;
16868         }
16869         if(s.firstChild && s.isExpanded()){
16870              return this.select(s.firstChild);
16871          }else if(s.nextSibling){
16872              return this.select(s.nextSibling);
16873          }else if(s.parentNode){
16874             var newS = null;
16875             s.parentNode.bubble(function(){
16876                 if(this.nextSibling){
16877                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16878                     return false;
16879                 }
16880             });
16881             return newS;
16882          }
16883         return null;
16884     },
16885
16886     onKeyDown : function(e){
16887         var s = this.selNode || this.lastSelNode;
16888         // undesirable, but required
16889         var sm = this;
16890         if(!s){
16891             return;
16892         }
16893         var k = e.getKey();
16894         switch(k){
16895              case e.DOWN:
16896                  e.stopEvent();
16897                  this.selectNext();
16898              break;
16899              case e.UP:
16900                  e.stopEvent();
16901                  this.selectPrevious();
16902              break;
16903              case e.RIGHT:
16904                  e.preventDefault();
16905                  if(s.hasChildNodes()){
16906                      if(!s.isExpanded()){
16907                          s.expand();
16908                      }else if(s.firstChild){
16909                          this.select(s.firstChild, e);
16910                      }
16911                  }
16912              break;
16913              case e.LEFT:
16914                  e.preventDefault();
16915                  if(s.hasChildNodes() && s.isExpanded()){
16916                      s.collapse();
16917                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16918                      this.select(s.parentNode, e);
16919                  }
16920              break;
16921         };
16922     }
16923 });
16924
16925 /**
16926  * @class Roo.tree.MultiSelectionModel
16927  * @extends Roo.util.Observable
16928  * Multi selection for a TreePanel.
16929  */
16930 Roo.tree.MultiSelectionModel = function(){
16931    this.selNodes = [];
16932    this.selMap = {};
16933    this.addEvents({
16934        /**
16935         * @event selectionchange
16936         * Fires when the selected nodes change
16937         * @param {MultiSelectionModel} this
16938         * @param {Array} nodes Array of the selected nodes
16939         */
16940        "selectionchange" : true
16941    });
16942 };
16943
16944 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16945     init : function(tree){
16946         this.tree = tree;
16947         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16948         tree.on("click", this.onNodeClick, this);
16949     },
16950     
16951     onNodeClick : function(node, e){
16952         this.select(node, e, e.ctrlKey);
16953     },
16954     
16955     /**
16956      * Select a node.
16957      * @param {TreeNode} node The node to select
16958      * @param {EventObject} e (optional) An event associated with the selection
16959      * @param {Boolean} keepExisting True to retain existing selections
16960      * @return {TreeNode} The selected node
16961      */
16962     select : function(node, e, keepExisting){
16963         if(keepExisting !== true){
16964             this.clearSelections(true);
16965         }
16966         if(this.isSelected(node)){
16967             this.lastSelNode = node;
16968             return node;
16969         }
16970         this.selNodes.push(node);
16971         this.selMap[node.id] = node;
16972         this.lastSelNode = node;
16973         node.ui.onSelectedChange(true);
16974         this.fireEvent("selectionchange", this, this.selNodes);
16975         return node;
16976     },
16977     
16978     /**
16979      * Deselect a node.
16980      * @param {TreeNode} node The node to unselect
16981      */
16982     unselect : function(node){
16983         if(this.selMap[node.id]){
16984             node.ui.onSelectedChange(false);
16985             var sn = this.selNodes;
16986             var index = -1;
16987             if(sn.indexOf){
16988                 index = sn.indexOf(node);
16989             }else{
16990                 for(var i = 0, len = sn.length; i < len; i++){
16991                     if(sn[i] == node){
16992                         index = i;
16993                         break;
16994                     }
16995                 }
16996             }
16997             if(index != -1){
16998                 this.selNodes.splice(index, 1);
16999             }
17000             delete this.selMap[node.id];
17001             this.fireEvent("selectionchange", this, this.selNodes);
17002         }
17003     },
17004     
17005     /**
17006      * Clear all selections
17007      */
17008     clearSelections : function(suppressEvent){
17009         var sn = this.selNodes;
17010         if(sn.length > 0){
17011             for(var i = 0, len = sn.length; i < len; i++){
17012                 sn[i].ui.onSelectedChange(false);
17013             }
17014             this.selNodes = [];
17015             this.selMap = {};
17016             if(suppressEvent !== true){
17017                 this.fireEvent("selectionchange", this, this.selNodes);
17018             }
17019         }
17020     },
17021     
17022     /**
17023      * Returns true if the node is selected
17024      * @param {TreeNode} node The node to check
17025      * @return {Boolean}
17026      */
17027     isSelected : function(node){
17028         return this.selMap[node.id] ? true : false;  
17029     },
17030     
17031     /**
17032      * Returns an array of the selected nodes
17033      * @return {Array}
17034      */
17035     getSelectedNodes : function(){
17036         return this.selNodes;    
17037     },
17038
17039     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17040
17041     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17042
17043     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17044 });/*
17045  * Based on:
17046  * Ext JS Library 1.1.1
17047  * Copyright(c) 2006-2007, Ext JS, LLC.
17048  *
17049  * Originally Released Under LGPL - original licence link has changed is not relivant.
17050  *
17051  * Fork - LGPL
17052  * <script type="text/javascript">
17053  */
17054  
17055 /**
17056  * @class Roo.tree.TreeNode
17057  * @extends Roo.data.Node
17058  * @cfg {String} text The text for this node
17059  * @cfg {Boolean} expanded true to start the node expanded
17060  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17061  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17062  * @cfg {Boolean} disabled true to start the node disabled
17063  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17064  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17065  * @cfg {String} cls A css class to be added to the node
17066  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17067  * @cfg {String} href URL of the link used for the node (defaults to #)
17068  * @cfg {String} hrefTarget target frame for the link
17069  * @cfg {String} qtip An Ext QuickTip for the node
17070  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17071  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17072  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17073  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17074  * (defaults to undefined with no checkbox rendered)
17075  * @constructor
17076  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17077  */
17078 Roo.tree.TreeNode = function(attributes){
17079     attributes = attributes || {};
17080     if(typeof attributes == "string"){
17081         attributes = {text: attributes};
17082     }
17083     this.childrenRendered = false;
17084     this.rendered = false;
17085     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17086     this.expanded = attributes.expanded === true;
17087     this.isTarget = attributes.isTarget !== false;
17088     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17089     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17090
17091     /**
17092      * Read-only. The text for this node. To change it use setText().
17093      * @type String
17094      */
17095     this.text = attributes.text;
17096     /**
17097      * True if this node is disabled.
17098      * @type Boolean
17099      */
17100     this.disabled = attributes.disabled === true;
17101
17102     this.addEvents({
17103         /**
17104         * @event textchange
17105         * Fires when the text for this node is changed
17106         * @param {Node} this This node
17107         * @param {String} text The new text
17108         * @param {String} oldText The old text
17109         */
17110         "textchange" : true,
17111         /**
17112         * @event beforeexpand
17113         * Fires before this node is expanded, return false to cancel.
17114         * @param {Node} this This node
17115         * @param {Boolean} deep
17116         * @param {Boolean} anim
17117         */
17118         "beforeexpand" : true,
17119         /**
17120         * @event beforecollapse
17121         * Fires before this node is collapsed, return false to cancel.
17122         * @param {Node} this This node
17123         * @param {Boolean} deep
17124         * @param {Boolean} anim
17125         */
17126         "beforecollapse" : true,
17127         /**
17128         * @event expand
17129         * Fires when this node is expanded
17130         * @param {Node} this This node
17131         */
17132         "expand" : true,
17133         /**
17134         * @event disabledchange
17135         * Fires when the disabled status of this node changes
17136         * @param {Node} this This node
17137         * @param {Boolean} disabled
17138         */
17139         "disabledchange" : true,
17140         /**
17141         * @event collapse
17142         * Fires when this node is collapsed
17143         * @param {Node} this This node
17144         */
17145         "collapse" : true,
17146         /**
17147         * @event beforeclick
17148         * Fires before click processing. Return false to cancel the default action.
17149         * @param {Node} this This node
17150         * @param {Roo.EventObject} e The event object
17151         */
17152         "beforeclick":true,
17153         /**
17154         * @event checkchange
17155         * Fires when a node with a checkbox's checked property changes
17156         * @param {Node} this This node
17157         * @param {Boolean} checked
17158         */
17159         "checkchange":true,
17160         /**
17161         * @event click
17162         * Fires when this node is clicked
17163         * @param {Node} this This node
17164         * @param {Roo.EventObject} e The event object
17165         */
17166         "click":true,
17167         /**
17168         * @event dblclick
17169         * Fires when this node is double clicked
17170         * @param {Node} this This node
17171         * @param {Roo.EventObject} e The event object
17172         */
17173         "dblclick":true,
17174         /**
17175         * @event contextmenu
17176         * Fires when this node is right clicked
17177         * @param {Node} this This node
17178         * @param {Roo.EventObject} e The event object
17179         */
17180         "contextmenu":true,
17181         /**
17182         * @event beforechildrenrendered
17183         * Fires right before the child nodes for this node are rendered
17184         * @param {Node} this This node
17185         */
17186         "beforechildrenrendered":true
17187     });
17188
17189     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17190
17191     /**
17192      * Read-only. The UI for this node
17193      * @type TreeNodeUI
17194      */
17195     this.ui = new uiClass(this);
17196 };
17197 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17198     preventHScroll: true,
17199     /**
17200      * Returns true if this node is expanded
17201      * @return {Boolean}
17202      */
17203     isExpanded : function(){
17204         return this.expanded;
17205     },
17206
17207     /**
17208      * Returns the UI object for this node
17209      * @return {TreeNodeUI}
17210      */
17211     getUI : function(){
17212         return this.ui;
17213     },
17214
17215     // private override
17216     setFirstChild : function(node){
17217         var of = this.firstChild;
17218         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17219         if(this.childrenRendered && of && node != of){
17220             of.renderIndent(true, true);
17221         }
17222         if(this.rendered){
17223             this.renderIndent(true, true);
17224         }
17225     },
17226
17227     // private override
17228     setLastChild : function(node){
17229         var ol = this.lastChild;
17230         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17231         if(this.childrenRendered && ol && node != ol){
17232             ol.renderIndent(true, true);
17233         }
17234         if(this.rendered){
17235             this.renderIndent(true, true);
17236         }
17237     },
17238
17239     // these methods are overridden to provide lazy rendering support
17240     // private override
17241     appendChild : function(){
17242         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17243         if(node && this.childrenRendered){
17244             node.render();
17245         }
17246         this.ui.updateExpandIcon();
17247         return node;
17248     },
17249
17250     // private override
17251     removeChild : function(node){
17252         this.ownerTree.getSelectionModel().unselect(node);
17253         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17254         // if it's been rendered remove dom node
17255         if(this.childrenRendered){
17256             node.ui.remove();
17257         }
17258         if(this.childNodes.length < 1){
17259             this.collapse(false, false);
17260         }else{
17261             this.ui.updateExpandIcon();
17262         }
17263         if(!this.firstChild) {
17264             this.childrenRendered = false;
17265         }
17266         return node;
17267     },
17268
17269     // private override
17270     insertBefore : function(node, refNode){
17271         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17272         if(newNode && refNode && this.childrenRendered){
17273             node.render();
17274         }
17275         this.ui.updateExpandIcon();
17276         return newNode;
17277     },
17278
17279     /**
17280      * Sets the text for this node
17281      * @param {String} text
17282      */
17283     setText : function(text){
17284         var oldText = this.text;
17285         this.text = text;
17286         this.attributes.text = text;
17287         if(this.rendered){ // event without subscribing
17288             this.ui.onTextChange(this, text, oldText);
17289         }
17290         this.fireEvent("textchange", this, text, oldText);
17291     },
17292
17293     /**
17294      * Triggers selection of this node
17295      */
17296     select : function(){
17297         this.getOwnerTree().getSelectionModel().select(this);
17298     },
17299
17300     /**
17301      * Triggers deselection of this node
17302      */
17303     unselect : function(){
17304         this.getOwnerTree().getSelectionModel().unselect(this);
17305     },
17306
17307     /**
17308      * Returns true if this node is selected
17309      * @return {Boolean}
17310      */
17311     isSelected : function(){
17312         return this.getOwnerTree().getSelectionModel().isSelected(this);
17313     },
17314
17315     /**
17316      * Expand this node.
17317      * @param {Boolean} deep (optional) True to expand all children as well
17318      * @param {Boolean} anim (optional) false to cancel the default animation
17319      * @param {Function} callback (optional) A callback to be called when
17320      * expanding this node completes (does not wait for deep expand to complete).
17321      * Called with 1 parameter, this node.
17322      */
17323     expand : function(deep, anim, callback){
17324         if(!this.expanded){
17325             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17326                 return;
17327             }
17328             if(!this.childrenRendered){
17329                 this.renderChildren();
17330             }
17331             this.expanded = true;
17332             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17333                 this.ui.animExpand(function(){
17334                     this.fireEvent("expand", this);
17335                     if(typeof callback == "function"){
17336                         callback(this);
17337                     }
17338                     if(deep === true){
17339                         this.expandChildNodes(true);
17340                     }
17341                 }.createDelegate(this));
17342                 return;
17343             }else{
17344                 this.ui.expand();
17345                 this.fireEvent("expand", this);
17346                 if(typeof callback == "function"){
17347                     callback(this);
17348                 }
17349             }
17350         }else{
17351            if(typeof callback == "function"){
17352                callback(this);
17353            }
17354         }
17355         if(deep === true){
17356             this.expandChildNodes(true);
17357         }
17358     },
17359
17360     isHiddenRoot : function(){
17361         return this.isRoot && !this.getOwnerTree().rootVisible;
17362     },
17363
17364     /**
17365      * Collapse this node.
17366      * @param {Boolean} deep (optional) True to collapse all children as well
17367      * @param {Boolean} anim (optional) false to cancel the default animation
17368      */
17369     collapse : function(deep, anim){
17370         if(this.expanded && !this.isHiddenRoot()){
17371             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17372                 return;
17373             }
17374             this.expanded = false;
17375             if((this.getOwnerTree().animate && anim !== false) || anim){
17376                 this.ui.animCollapse(function(){
17377                     this.fireEvent("collapse", this);
17378                     if(deep === true){
17379                         this.collapseChildNodes(true);
17380                     }
17381                 }.createDelegate(this));
17382                 return;
17383             }else{
17384                 this.ui.collapse();
17385                 this.fireEvent("collapse", this);
17386             }
17387         }
17388         if(deep === true){
17389             var cs = this.childNodes;
17390             for(var i = 0, len = cs.length; i < len; i++) {
17391                 cs[i].collapse(true, false);
17392             }
17393         }
17394     },
17395
17396     // private
17397     delayedExpand : function(delay){
17398         if(!this.expandProcId){
17399             this.expandProcId = this.expand.defer(delay, this);
17400         }
17401     },
17402
17403     // private
17404     cancelExpand : function(){
17405         if(this.expandProcId){
17406             clearTimeout(this.expandProcId);
17407         }
17408         this.expandProcId = false;
17409     },
17410
17411     /**
17412      * Toggles expanded/collapsed state of the node
17413      */
17414     toggle : function(){
17415         if(this.expanded){
17416             this.collapse();
17417         }else{
17418             this.expand();
17419         }
17420     },
17421
17422     /**
17423      * Ensures all parent nodes are expanded
17424      */
17425     ensureVisible : function(callback){
17426         var tree = this.getOwnerTree();
17427         tree.expandPath(this.parentNode.getPath(), false, function(){
17428             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17429             Roo.callback(callback);
17430         }.createDelegate(this));
17431     },
17432
17433     /**
17434      * Expand all child nodes
17435      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17436      */
17437     expandChildNodes : function(deep){
17438         var cs = this.childNodes;
17439         for(var i = 0, len = cs.length; i < len; i++) {
17440                 cs[i].expand(deep);
17441         }
17442     },
17443
17444     /**
17445      * Collapse all child nodes
17446      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17447      */
17448     collapseChildNodes : function(deep){
17449         var cs = this.childNodes;
17450         for(var i = 0, len = cs.length; i < len; i++) {
17451                 cs[i].collapse(deep);
17452         }
17453     },
17454
17455     /**
17456      * Disables this node
17457      */
17458     disable : function(){
17459         this.disabled = true;
17460         this.unselect();
17461         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17462             this.ui.onDisableChange(this, true);
17463         }
17464         this.fireEvent("disabledchange", this, true);
17465     },
17466
17467     /**
17468      * Enables this node
17469      */
17470     enable : function(){
17471         this.disabled = false;
17472         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17473             this.ui.onDisableChange(this, false);
17474         }
17475         this.fireEvent("disabledchange", this, false);
17476     },
17477
17478     // private
17479     renderChildren : function(suppressEvent){
17480         if(suppressEvent !== false){
17481             this.fireEvent("beforechildrenrendered", this);
17482         }
17483         var cs = this.childNodes;
17484         for(var i = 0, len = cs.length; i < len; i++){
17485             cs[i].render(true);
17486         }
17487         this.childrenRendered = true;
17488     },
17489
17490     // private
17491     sort : function(fn, scope){
17492         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17493         if(this.childrenRendered){
17494             var cs = this.childNodes;
17495             for(var i = 0, len = cs.length; i < len; i++){
17496                 cs[i].render(true);
17497             }
17498         }
17499     },
17500
17501     // private
17502     render : function(bulkRender){
17503         this.ui.render(bulkRender);
17504         if(!this.rendered){
17505             this.rendered = true;
17506             if(this.expanded){
17507                 this.expanded = false;
17508                 this.expand(false, false);
17509             }
17510         }
17511     },
17512
17513     // private
17514     renderIndent : function(deep, refresh){
17515         if(refresh){
17516             this.ui.childIndent = null;
17517         }
17518         this.ui.renderIndent();
17519         if(deep === true && this.childrenRendered){
17520             var cs = this.childNodes;
17521             for(var i = 0, len = cs.length; i < len; i++){
17522                 cs[i].renderIndent(true, refresh);
17523             }
17524         }
17525     }
17526 });/*
17527  * Based on:
17528  * Ext JS Library 1.1.1
17529  * Copyright(c) 2006-2007, Ext JS, LLC.
17530  *
17531  * Originally Released Under LGPL - original licence link has changed is not relivant.
17532  *
17533  * Fork - LGPL
17534  * <script type="text/javascript">
17535  */
17536  
17537 /**
17538  * @class Roo.tree.AsyncTreeNode
17539  * @extends Roo.tree.TreeNode
17540  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17541  * @constructor
17542  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17543  */
17544  Roo.tree.AsyncTreeNode = function(config){
17545     this.loaded = false;
17546     this.loading = false;
17547     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17548     /**
17549     * @event beforeload
17550     * Fires before this node is loaded, return false to cancel
17551     * @param {Node} this This node
17552     */
17553     this.addEvents({'beforeload':true, 'load': true});
17554     /**
17555     * @event load
17556     * Fires when this node is loaded
17557     * @param {Node} this This node
17558     */
17559     /**
17560      * The loader used by this node (defaults to using the tree's defined loader)
17561      * @type TreeLoader
17562      * @property loader
17563      */
17564 };
17565 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17566     expand : function(deep, anim, callback){
17567         if(this.loading){ // if an async load is already running, waiting til it's done
17568             var timer;
17569             var f = function(){
17570                 if(!this.loading){ // done loading
17571                     clearInterval(timer);
17572                     this.expand(deep, anim, callback);
17573                 }
17574             }.createDelegate(this);
17575             timer = setInterval(f, 200);
17576             return;
17577         }
17578         if(!this.loaded){
17579             if(this.fireEvent("beforeload", this) === false){
17580                 return;
17581             }
17582             this.loading = true;
17583             this.ui.beforeLoad(this);
17584             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17585             if(loader){
17586                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17587                 return;
17588             }
17589         }
17590         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17591     },
17592     
17593     /**
17594      * Returns true if this node is currently loading
17595      * @return {Boolean}
17596      */
17597     isLoading : function(){
17598         return this.loading;  
17599     },
17600     
17601     loadComplete : function(deep, anim, callback){
17602         this.loading = false;
17603         this.loaded = true;
17604         this.ui.afterLoad(this);
17605         this.fireEvent("load", this);
17606         this.expand(deep, anim, callback);
17607     },
17608     
17609     /**
17610      * Returns true if this node has been loaded
17611      * @return {Boolean}
17612      */
17613     isLoaded : function(){
17614         return this.loaded;
17615     },
17616     
17617     hasChildNodes : function(){
17618         if(!this.isLeaf() && !this.loaded){
17619             return true;
17620         }else{
17621             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17622         }
17623     },
17624
17625     /**
17626      * Trigger a reload for this node
17627      * @param {Function} callback
17628      */
17629     reload : function(callback){
17630         this.collapse(false, false);
17631         while(this.firstChild){
17632             this.removeChild(this.firstChild);
17633         }
17634         this.childrenRendered = false;
17635         this.loaded = false;
17636         if(this.isHiddenRoot()){
17637             this.expanded = false;
17638         }
17639         this.expand(false, false, callback);
17640     }
17641 });/*
17642  * Based on:
17643  * Ext JS Library 1.1.1
17644  * Copyright(c) 2006-2007, Ext JS, LLC.
17645  *
17646  * Originally Released Under LGPL - original licence link has changed is not relivant.
17647  *
17648  * Fork - LGPL
17649  * <script type="text/javascript">
17650  */
17651  
17652 /**
17653  * @class Roo.tree.TreeNodeUI
17654  * @constructor
17655  * @param {Object} node The node to render
17656  * The TreeNode UI implementation is separate from the
17657  * tree implementation. Unless you are customizing the tree UI,
17658  * you should never have to use this directly.
17659  */
17660 Roo.tree.TreeNodeUI = function(node){
17661     this.node = node;
17662     this.rendered = false;
17663     this.animating = false;
17664     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17665 };
17666
17667 Roo.tree.TreeNodeUI.prototype = {
17668     removeChild : function(node){
17669         if(this.rendered){
17670             this.ctNode.removeChild(node.ui.getEl());
17671         }
17672     },
17673
17674     beforeLoad : function(){
17675          this.addClass("x-tree-node-loading");
17676     },
17677
17678     afterLoad : function(){
17679          this.removeClass("x-tree-node-loading");
17680     },
17681
17682     onTextChange : function(node, text, oldText){
17683         if(this.rendered){
17684             this.textNode.innerHTML = text;
17685         }
17686     },
17687
17688     onDisableChange : function(node, state){
17689         this.disabled = state;
17690         if(state){
17691             this.addClass("x-tree-node-disabled");
17692         }else{
17693             this.removeClass("x-tree-node-disabled");
17694         }
17695     },
17696
17697     onSelectedChange : function(state){
17698         if(state){
17699             this.focus();
17700             this.addClass("x-tree-selected");
17701         }else{
17702             //this.blur();
17703             this.removeClass("x-tree-selected");
17704         }
17705     },
17706
17707     onMove : function(tree, node, oldParent, newParent, index, refNode){
17708         this.childIndent = null;
17709         if(this.rendered){
17710             var targetNode = newParent.ui.getContainer();
17711             if(!targetNode){//target not rendered
17712                 this.holder = document.createElement("div");
17713                 this.holder.appendChild(this.wrap);
17714                 return;
17715             }
17716             var insertBefore = refNode ? refNode.ui.getEl() : null;
17717             if(insertBefore){
17718                 targetNode.insertBefore(this.wrap, insertBefore);
17719             }else{
17720                 targetNode.appendChild(this.wrap);
17721             }
17722             this.node.renderIndent(true);
17723         }
17724     },
17725
17726     addClass : function(cls){
17727         if(this.elNode){
17728             Roo.fly(this.elNode).addClass(cls);
17729         }
17730     },
17731
17732     removeClass : function(cls){
17733         if(this.elNode){
17734             Roo.fly(this.elNode).removeClass(cls);
17735         }
17736     },
17737
17738     remove : function(){
17739         if(this.rendered){
17740             this.holder = document.createElement("div");
17741             this.holder.appendChild(this.wrap);
17742         }
17743     },
17744
17745     fireEvent : function(){
17746         return this.node.fireEvent.apply(this.node, arguments);
17747     },
17748
17749     initEvents : function(){
17750         this.node.on("move", this.onMove, this);
17751         var E = Roo.EventManager;
17752         var a = this.anchor;
17753
17754         var el = Roo.fly(a, '_treeui');
17755
17756         if(Roo.isOpera){ // opera render bug ignores the CSS
17757             el.setStyle("text-decoration", "none");
17758         }
17759
17760         el.on("click", this.onClick, this);
17761         el.on("dblclick", this.onDblClick, this);
17762
17763         if(this.checkbox){
17764             Roo.EventManager.on(this.checkbox,
17765                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17766         }
17767
17768         el.on("contextmenu", this.onContextMenu, this);
17769
17770         var icon = Roo.fly(this.iconNode);
17771         icon.on("click", this.onClick, this);
17772         icon.on("dblclick", this.onDblClick, this);
17773         icon.on("contextmenu", this.onContextMenu, this);
17774         E.on(this.ecNode, "click", this.ecClick, this, true);
17775
17776         if(this.node.disabled){
17777             this.addClass("x-tree-node-disabled");
17778         }
17779         if(this.node.hidden){
17780             this.addClass("x-tree-node-disabled");
17781         }
17782         var ot = this.node.getOwnerTree();
17783         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17784         if(dd && (!this.node.isRoot || ot.rootVisible)){
17785             Roo.dd.Registry.register(this.elNode, {
17786                 node: this.node,
17787                 handles: this.getDDHandles(),
17788                 isHandle: false
17789             });
17790         }
17791     },
17792
17793     getDDHandles : function(){
17794         return [this.iconNode, this.textNode];
17795     },
17796
17797     hide : function(){
17798         if(this.rendered){
17799             this.wrap.style.display = "none";
17800         }
17801     },
17802
17803     show : function(){
17804         if(this.rendered){
17805             this.wrap.style.display = "";
17806         }
17807     },
17808
17809     onContextMenu : function(e){
17810         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17811             e.preventDefault();
17812             this.focus();
17813             this.fireEvent("contextmenu", this.node, e);
17814         }
17815     },
17816
17817     onClick : function(e){
17818         if(this.dropping){
17819             e.stopEvent();
17820             return;
17821         }
17822         if(this.fireEvent("beforeclick", this.node, e) !== false){
17823             if(!this.disabled && this.node.attributes.href){
17824                 this.fireEvent("click", this.node, e);
17825                 return;
17826             }
17827             e.preventDefault();
17828             if(this.disabled){
17829                 return;
17830             }
17831
17832             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17833                 this.node.toggle();
17834             }
17835
17836             this.fireEvent("click", this.node, e);
17837         }else{
17838             e.stopEvent();
17839         }
17840     },
17841
17842     onDblClick : function(e){
17843         e.preventDefault();
17844         if(this.disabled){
17845             return;
17846         }
17847         if(this.checkbox){
17848             this.toggleCheck();
17849         }
17850         if(!this.animating && this.node.hasChildNodes()){
17851             this.node.toggle();
17852         }
17853         this.fireEvent("dblclick", this.node, e);
17854     },
17855
17856     onCheckChange : function(){
17857         var checked = this.checkbox.checked;
17858         this.node.attributes.checked = checked;
17859         this.fireEvent('checkchange', this.node, checked);
17860     },
17861
17862     ecClick : function(e){
17863         if(!this.animating && this.node.hasChildNodes()){
17864             this.node.toggle();
17865         }
17866     },
17867
17868     startDrop : function(){
17869         this.dropping = true;
17870     },
17871
17872     // delayed drop so the click event doesn't get fired on a drop
17873     endDrop : function(){
17874        setTimeout(function(){
17875            this.dropping = false;
17876        }.createDelegate(this), 50);
17877     },
17878
17879     expand : function(){
17880         this.updateExpandIcon();
17881         this.ctNode.style.display = "";
17882     },
17883
17884     focus : function(){
17885         if(!this.node.preventHScroll){
17886             try{this.anchor.focus();
17887             }catch(e){}
17888         }else if(!Roo.isIE){
17889             try{
17890                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17891                 var l = noscroll.scrollLeft;
17892                 this.anchor.focus();
17893                 noscroll.scrollLeft = l;
17894             }catch(e){}
17895         }
17896     },
17897
17898     toggleCheck : function(value){
17899         var cb = this.checkbox;
17900         if(cb){
17901             cb.checked = (value === undefined ? !cb.checked : value);
17902         }
17903     },
17904
17905     blur : function(){
17906         try{
17907             this.anchor.blur();
17908         }catch(e){}
17909     },
17910
17911     animExpand : function(callback){
17912         var ct = Roo.get(this.ctNode);
17913         ct.stopFx();
17914         if(!this.node.hasChildNodes()){
17915             this.updateExpandIcon();
17916             this.ctNode.style.display = "";
17917             Roo.callback(callback);
17918             return;
17919         }
17920         this.animating = true;
17921         this.updateExpandIcon();
17922
17923         ct.slideIn('t', {
17924            callback : function(){
17925                this.animating = false;
17926                Roo.callback(callback);
17927             },
17928             scope: this,
17929             duration: this.node.ownerTree.duration || .25
17930         });
17931     },
17932
17933     highlight : function(){
17934         var tree = this.node.getOwnerTree();
17935         Roo.fly(this.wrap).highlight(
17936             tree.hlColor || "C3DAF9",
17937             {endColor: tree.hlBaseColor}
17938         );
17939     },
17940
17941     collapse : function(){
17942         this.updateExpandIcon();
17943         this.ctNode.style.display = "none";
17944     },
17945
17946     animCollapse : function(callback){
17947         var ct = Roo.get(this.ctNode);
17948         ct.enableDisplayMode('block');
17949         ct.stopFx();
17950
17951         this.animating = true;
17952         this.updateExpandIcon();
17953
17954         ct.slideOut('t', {
17955             callback : function(){
17956                this.animating = false;
17957                Roo.callback(callback);
17958             },
17959             scope: this,
17960             duration: this.node.ownerTree.duration || .25
17961         });
17962     },
17963
17964     getContainer : function(){
17965         return this.ctNode;
17966     },
17967
17968     getEl : function(){
17969         return this.wrap;
17970     },
17971
17972     appendDDGhost : function(ghostNode){
17973         ghostNode.appendChild(this.elNode.cloneNode(true));
17974     },
17975
17976     getDDRepairXY : function(){
17977         return Roo.lib.Dom.getXY(this.iconNode);
17978     },
17979
17980     onRender : function(){
17981         this.render();
17982     },
17983
17984     render : function(bulkRender){
17985         var n = this.node, a = n.attributes;
17986         var targetNode = n.parentNode ?
17987               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17988
17989         if(!this.rendered){
17990             this.rendered = true;
17991
17992             this.renderElements(n, a, targetNode, bulkRender);
17993
17994             if(a.qtip){
17995                if(this.textNode.setAttributeNS){
17996                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17997                    if(a.qtipTitle){
17998                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17999                    }
18000                }else{
18001                    this.textNode.setAttribute("ext:qtip", a.qtip);
18002                    if(a.qtipTitle){
18003                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18004                    }
18005                }
18006             }else if(a.qtipCfg){
18007                 a.qtipCfg.target = Roo.id(this.textNode);
18008                 Roo.QuickTips.register(a.qtipCfg);
18009             }
18010             this.initEvents();
18011             if(!this.node.expanded){
18012                 this.updateExpandIcon();
18013             }
18014         }else{
18015             if(bulkRender === true) {
18016                 targetNode.appendChild(this.wrap);
18017             }
18018         }
18019     },
18020
18021     renderElements : function(n, a, targetNode, bulkRender){
18022         // add some indent caching, this helps performance when rendering a large tree
18023         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18024         var t = n.getOwnerTree();
18025         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18026         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18027         var cb = typeof a.checked == 'boolean';
18028         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18029         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18030             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18031             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18032             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18033             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18034             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18035              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18036                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18037             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18038             "</li>"];
18039
18040         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18041             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18042                                 n.nextSibling.ui.getEl(), buf.join(""));
18043         }else{
18044             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18045         }
18046
18047         this.elNode = this.wrap.childNodes[0];
18048         this.ctNode = this.wrap.childNodes[1];
18049         var cs = this.elNode.childNodes;
18050         this.indentNode = cs[0];
18051         this.ecNode = cs[1];
18052         this.iconNode = cs[2];
18053         var index = 3;
18054         if(cb){
18055             this.checkbox = cs[3];
18056             index++;
18057         }
18058         this.anchor = cs[index];
18059         this.textNode = cs[index].firstChild;
18060     },
18061
18062     getAnchor : function(){
18063         return this.anchor;
18064     },
18065
18066     getTextEl : function(){
18067         return this.textNode;
18068     },
18069
18070     getIconEl : function(){
18071         return this.iconNode;
18072     },
18073
18074     isChecked : function(){
18075         return this.checkbox ? this.checkbox.checked : false;
18076     },
18077
18078     updateExpandIcon : function(){
18079         if(this.rendered){
18080             var n = this.node, c1, c2;
18081             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18082             var hasChild = n.hasChildNodes();
18083             if(hasChild){
18084                 if(n.expanded){
18085                     cls += "-minus";
18086                     c1 = "x-tree-node-collapsed";
18087                     c2 = "x-tree-node-expanded";
18088                 }else{
18089                     cls += "-plus";
18090                     c1 = "x-tree-node-expanded";
18091                     c2 = "x-tree-node-collapsed";
18092                 }
18093                 if(this.wasLeaf){
18094                     this.removeClass("x-tree-node-leaf");
18095                     this.wasLeaf = false;
18096                 }
18097                 if(this.c1 != c1 || this.c2 != c2){
18098                     Roo.fly(this.elNode).replaceClass(c1, c2);
18099                     this.c1 = c1; this.c2 = c2;
18100                 }
18101             }else{
18102                 if(!this.wasLeaf){
18103                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18104                     delete this.c1;
18105                     delete this.c2;
18106                     this.wasLeaf = true;
18107                 }
18108             }
18109             var ecc = "x-tree-ec-icon "+cls;
18110             if(this.ecc != ecc){
18111                 this.ecNode.className = ecc;
18112                 this.ecc = ecc;
18113             }
18114         }
18115     },
18116
18117     getChildIndent : function(){
18118         if(!this.childIndent){
18119             var buf = [];
18120             var p = this.node;
18121             while(p){
18122                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18123                     if(!p.isLast()) {
18124                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18125                     } else {
18126                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18127                     }
18128                 }
18129                 p = p.parentNode;
18130             }
18131             this.childIndent = buf.join("");
18132         }
18133         return this.childIndent;
18134     },
18135
18136     renderIndent : function(){
18137         if(this.rendered){
18138             var indent = "";
18139             var p = this.node.parentNode;
18140             if(p){
18141                 indent = p.ui.getChildIndent();
18142             }
18143             if(this.indentMarkup != indent){ // don't rerender if not required
18144                 this.indentNode.innerHTML = indent;
18145                 this.indentMarkup = indent;
18146             }
18147             this.updateExpandIcon();
18148         }
18149     }
18150 };
18151
18152 Roo.tree.RootTreeNodeUI = function(){
18153     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18154 };
18155 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18156     render : function(){
18157         if(!this.rendered){
18158             var targetNode = this.node.ownerTree.innerCt.dom;
18159             this.node.expanded = true;
18160             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18161             this.wrap = this.ctNode = targetNode.firstChild;
18162         }
18163     },
18164     collapse : function(){
18165     },
18166     expand : function(){
18167     }
18168 });/*
18169  * Based on:
18170  * Ext JS Library 1.1.1
18171  * Copyright(c) 2006-2007, Ext JS, LLC.
18172  *
18173  * Originally Released Under LGPL - original licence link has changed is not relivant.
18174  *
18175  * Fork - LGPL
18176  * <script type="text/javascript">
18177  */
18178 /**
18179  * @class Roo.tree.TreeLoader
18180  * @extends Roo.util.Observable
18181  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18182  * nodes from a specified URL. The response must be a javascript Array definition
18183  * who's elements are node definition objects. eg:
18184  * <pre><code>
18185    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18186     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18187 </code></pre>
18188  * <br><br>
18189  * A server request is sent, and child nodes are loaded only when a node is expanded.
18190  * The loading node's id is passed to the server under the parameter name "node" to
18191  * enable the server to produce the correct child nodes.
18192  * <br><br>
18193  * To pass extra parameters, an event handler may be attached to the "beforeload"
18194  * event, and the parameters specified in the TreeLoader's baseParams property:
18195  * <pre><code>
18196     myTreeLoader.on("beforeload", function(treeLoader, node) {
18197         this.baseParams.category = node.attributes.category;
18198     }, this);
18199 </code></pre><
18200  * This would pass an HTTP parameter called "category" to the server containing
18201  * the value of the Node's "category" attribute.
18202  * @constructor
18203  * Creates a new Treeloader.
18204  * @param {Object} config A config object containing config properties.
18205  */
18206 Roo.tree.TreeLoader = function(config){
18207     this.baseParams = {};
18208     this.requestMethod = "POST";
18209     Roo.apply(this, config);
18210
18211     this.addEvents({
18212     
18213         /**
18214          * @event beforeload
18215          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18216          * @param {Object} This TreeLoader object.
18217          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18218          * @param {Object} callback The callback function specified in the {@link #load} call.
18219          */
18220         beforeload : true,
18221         /**
18222          * @event load
18223          * Fires when the node has been successfuly loaded.
18224          * @param {Object} This TreeLoader object.
18225          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18226          * @param {Object} response The response object containing the data from the server.
18227          */
18228         load : true,
18229         /**
18230          * @event loadexception
18231          * Fires if the network request failed.
18232          * @param {Object} This TreeLoader object.
18233          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18234          * @param {Object} response The response object containing the data from the server.
18235          */
18236         loadexception : true,
18237         /**
18238          * @event create
18239          * Fires before a node is created, enabling you to return custom Node types 
18240          * @param {Object} This TreeLoader object.
18241          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18242          */
18243         create : true
18244     });
18245
18246     Roo.tree.TreeLoader.superclass.constructor.call(this);
18247 };
18248
18249 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18250     /**
18251     * @cfg {String} dataUrl The URL from which to request a Json string which
18252     * specifies an array of node definition object representing the child nodes
18253     * to be loaded.
18254     */
18255     /**
18256     * @cfg {Object} baseParams (optional) An object containing properties which
18257     * specify HTTP parameters to be passed to each request for child nodes.
18258     */
18259     /**
18260     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18261     * created by this loader. If the attributes sent by the server have an attribute in this object,
18262     * they take priority.
18263     */
18264     /**
18265     * @cfg {Object} uiProviders (optional) An object containing properties which
18266     * 
18267     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18268     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18269     * <i>uiProvider</i> attribute of a returned child node is a string rather
18270     * than a reference to a TreeNodeUI implementation, this that string value
18271     * is used as a property name in the uiProviders object. You can define the provider named
18272     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18273     */
18274     uiProviders : {},
18275
18276     /**
18277     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18278     * child nodes before loading.
18279     */
18280     clearOnLoad : true,
18281
18282     /**
18283     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18284     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18285     * Grid query { data : [ .....] }
18286     */
18287     
18288     root : false,
18289      /**
18290     * @cfg {String} queryParam (optional) 
18291     * Name of the query as it will be passed on the querystring (defaults to 'node')
18292     * eg. the request will be ?node=[id]
18293     */
18294     
18295     
18296     queryParam: false,
18297     
18298     /**
18299      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18300      * This is called automatically when a node is expanded, but may be used to reload
18301      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18302      * @param {Roo.tree.TreeNode} node
18303      * @param {Function} callback
18304      */
18305     load : function(node, callback){
18306         if(this.clearOnLoad){
18307             while(node.firstChild){
18308                 node.removeChild(node.firstChild);
18309             }
18310         }
18311         if(node.attributes.children){ // preloaded json children
18312             var cs = node.attributes.children;
18313             for(var i = 0, len = cs.length; i < len; i++){
18314                 node.appendChild(this.createNode(cs[i]));
18315             }
18316             if(typeof callback == "function"){
18317                 callback();
18318             }
18319         }else if(this.dataUrl){
18320             this.requestData(node, callback);
18321         }
18322     },
18323
18324     getParams: function(node){
18325         var buf = [], bp = this.baseParams;
18326         for(var key in bp){
18327             if(typeof bp[key] != "function"){
18328                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18329             }
18330         }
18331         var n = this.queryParam === false ? 'node' : this.queryParam;
18332         buf.push(n + "=", encodeURIComponent(node.id));
18333         return buf.join("");
18334     },
18335
18336     requestData : function(node, callback){
18337         if(this.fireEvent("beforeload", this, node, callback) !== false){
18338             this.transId = Roo.Ajax.request({
18339                 method:this.requestMethod,
18340                 url: this.dataUrl||this.url,
18341                 success: this.handleResponse,
18342                 failure: this.handleFailure,
18343                 scope: this,
18344                 argument: {callback: callback, node: node},
18345                 params: this.getParams(node)
18346             });
18347         }else{
18348             // if the load is cancelled, make sure we notify
18349             // the node that we are done
18350             if(typeof callback == "function"){
18351                 callback();
18352             }
18353         }
18354     },
18355
18356     isLoading : function(){
18357         return this.transId ? true : false;
18358     },
18359
18360     abort : function(){
18361         if(this.isLoading()){
18362             Roo.Ajax.abort(this.transId);
18363         }
18364     },
18365
18366     // private
18367     createNode : function(attr){
18368         // apply baseAttrs, nice idea Corey!
18369         if(this.baseAttrs){
18370             Roo.applyIf(attr, this.baseAttrs);
18371         }
18372         if(this.applyLoader !== false){
18373             attr.loader = this;
18374         }
18375         // uiProvider = depreciated..
18376         
18377         if(typeof(attr.uiProvider) == 'string'){
18378            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18379                 /**  eval:var:attr */ eval(attr.uiProvider);
18380         }
18381         if(typeof(this.uiProviders['default']) != 'undefined') {
18382             attr.uiProvider = this.uiProviders['default'];
18383         }
18384         
18385         this.fireEvent('create', this, attr);
18386         
18387         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18388         return(attr.leaf ?
18389                         new Roo.tree.TreeNode(attr) :
18390                         new Roo.tree.AsyncTreeNode(attr));
18391     },
18392
18393     processResponse : function(response, node, callback){
18394         var json = response.responseText;
18395         try {
18396             
18397             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18398             if (this.root !== false) {
18399                 o = o[this.root];
18400             }
18401             
18402             for(var i = 0, len = o.length; i < len; i++){
18403                 var n = this.createNode(o[i]);
18404                 if(n){
18405                     node.appendChild(n);
18406                 }
18407             }
18408             if(typeof callback == "function"){
18409                 callback(this, node);
18410             }
18411         }catch(e){
18412             this.handleFailure(response);
18413         }
18414     },
18415
18416     handleResponse : function(response){
18417         this.transId = false;
18418         var a = response.argument;
18419         this.processResponse(response, a.node, a.callback);
18420         this.fireEvent("load", this, a.node, response);
18421     },
18422
18423     handleFailure : function(response){
18424         this.transId = false;
18425         var a = response.argument;
18426         this.fireEvent("loadexception", this, a.node, response);
18427         if(typeof a.callback == "function"){
18428             a.callback(this, a.node);
18429         }
18430     }
18431 });/*
18432  * Based on:
18433  * Ext JS Library 1.1.1
18434  * Copyright(c) 2006-2007, Ext JS, LLC.
18435  *
18436  * Originally Released Under LGPL - original licence link has changed is not relivant.
18437  *
18438  * Fork - LGPL
18439  * <script type="text/javascript">
18440  */
18441
18442 /**
18443 * @class Roo.tree.TreeFilter
18444 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18445 * @param {TreePanel} tree
18446 * @param {Object} config (optional)
18447  */
18448 Roo.tree.TreeFilter = function(tree, config){
18449     this.tree = tree;
18450     this.filtered = {};
18451     Roo.apply(this, config);
18452 };
18453
18454 Roo.tree.TreeFilter.prototype = {
18455     clearBlank:false,
18456     reverse:false,
18457     autoClear:false,
18458     remove:false,
18459
18460      /**
18461      * Filter the data by a specific attribute.
18462      * @param {String/RegExp} value Either string that the attribute value
18463      * should start with or a RegExp to test against the attribute
18464      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18465      * @param {TreeNode} startNode (optional) The node to start the filter at.
18466      */
18467     filter : function(value, attr, startNode){
18468         attr = attr || "text";
18469         var f;
18470         if(typeof value == "string"){
18471             var vlen = value.length;
18472             // auto clear empty filter
18473             if(vlen == 0 && this.clearBlank){
18474                 this.clear();
18475                 return;
18476             }
18477             value = value.toLowerCase();
18478             f = function(n){
18479                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18480             };
18481         }else if(value.exec){ // regex?
18482             f = function(n){
18483                 return value.test(n.attributes[attr]);
18484             };
18485         }else{
18486             throw 'Illegal filter type, must be string or regex';
18487         }
18488         this.filterBy(f, null, startNode);
18489         },
18490
18491     /**
18492      * Filter by a function. The passed function will be called with each
18493      * node in the tree (or from the startNode). If the function returns true, the node is kept
18494      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18495      * @param {Function} fn The filter function
18496      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18497      */
18498     filterBy : function(fn, scope, startNode){
18499         startNode = startNode || this.tree.root;
18500         if(this.autoClear){
18501             this.clear();
18502         }
18503         var af = this.filtered, rv = this.reverse;
18504         var f = function(n){
18505             if(n == startNode){
18506                 return true;
18507             }
18508             if(af[n.id]){
18509                 return false;
18510             }
18511             var m = fn.call(scope || n, n);
18512             if(!m || rv){
18513                 af[n.id] = n;
18514                 n.ui.hide();
18515                 return false;
18516             }
18517             return true;
18518         };
18519         startNode.cascade(f);
18520         if(this.remove){
18521            for(var id in af){
18522                if(typeof id != "function"){
18523                    var n = af[id];
18524                    if(n && n.parentNode){
18525                        n.parentNode.removeChild(n);
18526                    }
18527                }
18528            }
18529         }
18530     },
18531
18532     /**
18533      * Clears the current filter. Note: with the "remove" option
18534      * set a filter cannot be cleared.
18535      */
18536     clear : function(){
18537         var t = this.tree;
18538         var af = this.filtered;
18539         for(var id in af){
18540             if(typeof id != "function"){
18541                 var n = af[id];
18542                 if(n){
18543                     n.ui.show();
18544                 }
18545             }
18546         }
18547         this.filtered = {};
18548     }
18549 };
18550 /*
18551  * Based on:
18552  * Ext JS Library 1.1.1
18553  * Copyright(c) 2006-2007, Ext JS, LLC.
18554  *
18555  * Originally Released Under LGPL - original licence link has changed is not relivant.
18556  *
18557  * Fork - LGPL
18558  * <script type="text/javascript">
18559  */
18560  
18561
18562 /**
18563  * @class Roo.tree.TreeSorter
18564  * Provides sorting of nodes in a TreePanel
18565  * 
18566  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18567  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18568  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18569  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18570  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18571  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18572  * @constructor
18573  * @param {TreePanel} tree
18574  * @param {Object} config
18575  */
18576 Roo.tree.TreeSorter = function(tree, config){
18577     Roo.apply(this, config);
18578     tree.on("beforechildrenrendered", this.doSort, this);
18579     tree.on("append", this.updateSort, this);
18580     tree.on("insert", this.updateSort, this);
18581     
18582     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18583     var p = this.property || "text";
18584     var sortType = this.sortType;
18585     var fs = this.folderSort;
18586     var cs = this.caseSensitive === true;
18587     var leafAttr = this.leafAttr || 'leaf';
18588
18589     this.sortFn = function(n1, n2){
18590         if(fs){
18591             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18592                 return 1;
18593             }
18594             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18595                 return -1;
18596             }
18597         }
18598         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18599         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18600         if(v1 < v2){
18601                         return dsc ? +1 : -1;
18602                 }else if(v1 > v2){
18603                         return dsc ? -1 : +1;
18604         }else{
18605                 return 0;
18606         }
18607     };
18608 };
18609
18610 Roo.tree.TreeSorter.prototype = {
18611     doSort : function(node){
18612         node.sort(this.sortFn);
18613     },
18614     
18615     compareNodes : function(n1, n2){
18616         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18617     },
18618     
18619     updateSort : function(tree, node){
18620         if(node.childrenRendered){
18621             this.doSort.defer(1, this, [node]);
18622         }
18623     }
18624 };/*
18625  * Based on:
18626  * Ext JS Library 1.1.1
18627  * Copyright(c) 2006-2007, Ext JS, LLC.
18628  *
18629  * Originally Released Under LGPL - original licence link has changed is not relivant.
18630  *
18631  * Fork - LGPL
18632  * <script type="text/javascript">
18633  */
18634
18635 if(Roo.dd.DropZone){
18636     
18637 Roo.tree.TreeDropZone = function(tree, config){
18638     this.allowParentInsert = false;
18639     this.allowContainerDrop = false;
18640     this.appendOnly = false;
18641     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18642     this.tree = tree;
18643     this.lastInsertClass = "x-tree-no-status";
18644     this.dragOverData = {};
18645 };
18646
18647 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18648     ddGroup : "TreeDD",
18649     
18650     expandDelay : 1000,
18651     
18652     expandNode : function(node){
18653         if(node.hasChildNodes() && !node.isExpanded()){
18654             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18655         }
18656     },
18657     
18658     queueExpand : function(node){
18659         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18660     },
18661     
18662     cancelExpand : function(){
18663         if(this.expandProcId){
18664             clearTimeout(this.expandProcId);
18665             this.expandProcId = false;
18666         }
18667     },
18668     
18669     isValidDropPoint : function(n, pt, dd, e, data){
18670         if(!n || !data){ return false; }
18671         var targetNode = n.node;
18672         var dropNode = data.node;
18673         // default drop rules
18674         if(!(targetNode && targetNode.isTarget && pt)){
18675             return false;
18676         }
18677         if(pt == "append" && targetNode.allowChildren === false){
18678             return false;
18679         }
18680         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18681             return false;
18682         }
18683         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18684             return false;
18685         }
18686         // reuse the object
18687         var overEvent = this.dragOverData;
18688         overEvent.tree = this.tree;
18689         overEvent.target = targetNode;
18690         overEvent.data = data;
18691         overEvent.point = pt;
18692         overEvent.source = dd;
18693         overEvent.rawEvent = e;
18694         overEvent.dropNode = dropNode;
18695         overEvent.cancel = false;  
18696         var result = this.tree.fireEvent("nodedragover", overEvent);
18697         return overEvent.cancel === false && result !== false;
18698     },
18699     
18700     getDropPoint : function(e, n, dd){
18701         var tn = n.node;
18702         if(tn.isRoot){
18703             return tn.allowChildren !== false ? "append" : false; // always append for root
18704         }
18705         var dragEl = n.ddel;
18706         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18707         var y = Roo.lib.Event.getPageY(e);
18708         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18709         
18710         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18711         var noAppend = tn.allowChildren === false;
18712         if(this.appendOnly || tn.parentNode.allowChildren === false){
18713             return noAppend ? false : "append";
18714         }
18715         var noBelow = false;
18716         if(!this.allowParentInsert){
18717             noBelow = tn.hasChildNodes() && tn.isExpanded();
18718         }
18719         var q = (b - t) / (noAppend ? 2 : 3);
18720         if(y >= t && y < (t + q)){
18721             return "above";
18722         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18723             return "below";
18724         }else{
18725             return "append";
18726         }
18727     },
18728     
18729     onNodeEnter : function(n, dd, e, data){
18730         this.cancelExpand();
18731     },
18732     
18733     onNodeOver : function(n, dd, e, data){
18734         var pt = this.getDropPoint(e, n, dd);
18735         var node = n.node;
18736         
18737         // auto node expand check
18738         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18739             this.queueExpand(node);
18740         }else if(pt != "append"){
18741             this.cancelExpand();
18742         }
18743         
18744         // set the insert point style on the target node
18745         var returnCls = this.dropNotAllowed;
18746         if(this.isValidDropPoint(n, pt, dd, e, data)){
18747            if(pt){
18748                var el = n.ddel;
18749                var cls;
18750                if(pt == "above"){
18751                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18752                    cls = "x-tree-drag-insert-above";
18753                }else if(pt == "below"){
18754                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18755                    cls = "x-tree-drag-insert-below";
18756                }else{
18757                    returnCls = "x-tree-drop-ok-append";
18758                    cls = "x-tree-drag-append";
18759                }
18760                if(this.lastInsertClass != cls){
18761                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18762                    this.lastInsertClass = cls;
18763                }
18764            }
18765        }
18766        return returnCls;
18767     },
18768     
18769     onNodeOut : function(n, dd, e, data){
18770         this.cancelExpand();
18771         this.removeDropIndicators(n);
18772     },
18773     
18774     onNodeDrop : function(n, dd, e, data){
18775         var point = this.getDropPoint(e, n, dd);
18776         var targetNode = n.node;
18777         targetNode.ui.startDrop();
18778         if(!this.isValidDropPoint(n, point, dd, e, data)){
18779             targetNode.ui.endDrop();
18780             return false;
18781         }
18782         // first try to find the drop node
18783         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18784         var dropEvent = {
18785             tree : this.tree,
18786             target: targetNode,
18787             data: data,
18788             point: point,
18789             source: dd,
18790             rawEvent: e,
18791             dropNode: dropNode,
18792             cancel: !dropNode   
18793         };
18794         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18795         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18796             targetNode.ui.endDrop();
18797             return false;
18798         }
18799         // allow target changing
18800         targetNode = dropEvent.target;
18801         if(point == "append" && !targetNode.isExpanded()){
18802             targetNode.expand(false, null, function(){
18803                 this.completeDrop(dropEvent);
18804             }.createDelegate(this));
18805         }else{
18806             this.completeDrop(dropEvent);
18807         }
18808         return true;
18809     },
18810     
18811     completeDrop : function(de){
18812         var ns = de.dropNode, p = de.point, t = de.target;
18813         if(!(ns instanceof Array)){
18814             ns = [ns];
18815         }
18816         var n;
18817         for(var i = 0, len = ns.length; i < len; i++){
18818             n = ns[i];
18819             if(p == "above"){
18820                 t.parentNode.insertBefore(n, t);
18821             }else if(p == "below"){
18822                 t.parentNode.insertBefore(n, t.nextSibling);
18823             }else{
18824                 t.appendChild(n);
18825             }
18826         }
18827         n.ui.focus();
18828         if(this.tree.hlDrop){
18829             n.ui.highlight();
18830         }
18831         t.ui.endDrop();
18832         this.tree.fireEvent("nodedrop", de);
18833     },
18834     
18835     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18836         if(this.tree.hlDrop){
18837             dropNode.ui.focus();
18838             dropNode.ui.highlight();
18839         }
18840         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18841     },
18842     
18843     getTree : function(){
18844         return this.tree;
18845     },
18846     
18847     removeDropIndicators : function(n){
18848         if(n && n.ddel){
18849             var el = n.ddel;
18850             Roo.fly(el).removeClass([
18851                     "x-tree-drag-insert-above",
18852                     "x-tree-drag-insert-below",
18853                     "x-tree-drag-append"]);
18854             this.lastInsertClass = "_noclass";
18855         }
18856     },
18857     
18858     beforeDragDrop : function(target, e, id){
18859         this.cancelExpand();
18860         return true;
18861     },
18862     
18863     afterRepair : function(data){
18864         if(data && Roo.enableFx){
18865             data.node.ui.highlight();
18866         }
18867         this.hideProxy();
18868     }    
18869 });
18870
18871 }
18872 /*
18873  * Based on:
18874  * Ext JS Library 1.1.1
18875  * Copyright(c) 2006-2007, Ext JS, LLC.
18876  *
18877  * Originally Released Under LGPL - original licence link has changed is not relivant.
18878  *
18879  * Fork - LGPL
18880  * <script type="text/javascript">
18881  */
18882  
18883
18884 if(Roo.dd.DragZone){
18885 Roo.tree.TreeDragZone = function(tree, config){
18886     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18887     this.tree = tree;
18888 };
18889
18890 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18891     ddGroup : "TreeDD",
18892     
18893     onBeforeDrag : function(data, e){
18894         var n = data.node;
18895         return n && n.draggable && !n.disabled;
18896     },
18897     
18898     onInitDrag : function(e){
18899         var data = this.dragData;
18900         this.tree.getSelectionModel().select(data.node);
18901         this.proxy.update("");
18902         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18903         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18904     },
18905     
18906     getRepairXY : function(e, data){
18907         return data.node.ui.getDDRepairXY();
18908     },
18909     
18910     onEndDrag : function(data, e){
18911         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18912     },
18913     
18914     onValidDrop : function(dd, e, id){
18915         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18916         this.hideProxy();
18917     },
18918     
18919     beforeInvalidDrop : function(e, id){
18920         // this scrolls the original position back into view
18921         var sm = this.tree.getSelectionModel();
18922         sm.clearSelections();
18923         sm.select(this.dragData.node);
18924     }
18925 });
18926 }/*
18927  * Based on:
18928  * Ext JS Library 1.1.1
18929  * Copyright(c) 2006-2007, Ext JS, LLC.
18930  *
18931  * Originally Released Under LGPL - original licence link has changed is not relivant.
18932  *
18933  * Fork - LGPL
18934  * <script type="text/javascript">
18935  */
18936 /**
18937  * @class Roo.tree.TreeEditor
18938  * @extends Roo.Editor
18939  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18940  * as the editor field.
18941  * @constructor
18942  * @param {TreePanel} tree
18943  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18944  */
18945 Roo.tree.TreeEditor = function(tree, config){
18946     config = config || {};
18947     var field = config.events ? config : new Roo.form.TextField(config);
18948     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18949
18950     this.tree = tree;
18951
18952     tree.on('beforeclick', this.beforeNodeClick, this);
18953     tree.getTreeEl().on('mousedown', this.hide, this);
18954     this.on('complete', this.updateNode, this);
18955     this.on('beforestartedit', this.fitToTree, this);
18956     this.on('startedit', this.bindScroll, this, {delay:10});
18957     this.on('specialkey', this.onSpecialKey, this);
18958 };
18959
18960 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18961     /**
18962      * @cfg {String} alignment
18963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18964      */
18965     alignment: "l-l",
18966     // inherit
18967     autoSize: false,
18968     /**
18969      * @cfg {Boolean} hideEl
18970      * True to hide the bound element while the editor is displayed (defaults to false)
18971      */
18972     hideEl : false,
18973     /**
18974      * @cfg {String} cls
18975      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18976      */
18977     cls: "x-small-editor x-tree-editor",
18978     /**
18979      * @cfg {Boolean} shim
18980      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18981      */
18982     shim:false,
18983     // inherit
18984     shadow:"frame",
18985     /**
18986      * @cfg {Number} maxWidth
18987      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18988      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18989      * scroll and client offsets into account prior to each edit.
18990      */
18991     maxWidth: 250,
18992
18993     editDelay : 350,
18994
18995     // private
18996     fitToTree : function(ed, el){
18997         var td = this.tree.getTreeEl().dom, nd = el.dom;
18998         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18999             td.scrollLeft = nd.offsetLeft;
19000         }
19001         var w = Math.min(
19002                 this.maxWidth,
19003                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19004         this.setSize(w, '');
19005     },
19006
19007     // private
19008     triggerEdit : function(node){
19009         this.completeEdit();
19010         this.editNode = node;
19011         this.startEdit(node.ui.textNode, node.text);
19012     },
19013
19014     // private
19015     bindScroll : function(){
19016         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19017     },
19018
19019     // private
19020     beforeNodeClick : function(node, e){
19021         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19022         this.lastClick = new Date();
19023         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19024             e.stopEvent();
19025             this.triggerEdit(node);
19026             return false;
19027         }
19028     },
19029
19030     // private
19031     updateNode : function(ed, value){
19032         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19033         this.editNode.setText(value);
19034     },
19035
19036     // private
19037     onHide : function(){
19038         Roo.tree.TreeEditor.superclass.onHide.call(this);
19039         if(this.editNode){
19040             this.editNode.ui.focus();
19041         }
19042     },
19043
19044     // private
19045     onSpecialKey : function(field, e){
19046         var k = e.getKey();
19047         if(k == e.ESC){
19048             e.stopEvent();
19049             this.cancelEdit();
19050         }else if(k == e.ENTER && !e.hasModifier()){
19051             e.stopEvent();
19052             this.completeEdit();
19053         }
19054     }
19055 });//<Script type="text/javascript">
19056 /*
19057  * Based on:
19058  * Ext JS Library 1.1.1
19059  * Copyright(c) 2006-2007, Ext JS, LLC.
19060  *
19061  * Originally Released Under LGPL - original licence link has changed is not relivant.
19062  *
19063  * Fork - LGPL
19064  * <script type="text/javascript">
19065  */
19066  
19067 /**
19068  * Not documented??? - probably should be...
19069  */
19070
19071 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19072     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19073     
19074     renderElements : function(n, a, targetNode, bulkRender){
19075         //consel.log("renderElements?");
19076         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19077
19078         var t = n.getOwnerTree();
19079         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19080         
19081         var cols = t.columns;
19082         var bw = t.borderWidth;
19083         var c = cols[0];
19084         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19085          var cb = typeof a.checked == "boolean";
19086         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19087         var colcls = 'x-t-' + tid + '-c0';
19088         var buf = [
19089             '<li class="x-tree-node">',
19090             
19091                 
19092                 '<div class="x-tree-node-el ', a.cls,'">',
19093                     // extran...
19094                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19095                 
19096                 
19097                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19098                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19099                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19100                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19101                            (a.iconCls ? ' '+a.iconCls : ''),
19102                            '" unselectable="on" />',
19103                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19104                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19105                              
19106                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19107                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19108                             '<span unselectable="on" qtip="' + tx + '">',
19109                              tx,
19110                              '</span></a>' ,
19111                     '</div>',
19112                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19113                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19114                  ];
19115         for(var i = 1, len = cols.length; i < len; i++){
19116             c = cols[i];
19117             colcls = 'x-t-' + tid + '-c' +i;
19118             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19119             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19120                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19121                       "</div>");
19122          }
19123          
19124          buf.push(
19125             '</a>',
19126             '<div class="x-clear"></div></div>',
19127             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19128             "</li>");
19129         
19130         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19131             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19132                                 n.nextSibling.ui.getEl(), buf.join(""));
19133         }else{
19134             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19135         }
19136         var el = this.wrap.firstChild;
19137         this.elRow = el;
19138         this.elNode = el.firstChild;
19139         this.ranchor = el.childNodes[1];
19140         this.ctNode = this.wrap.childNodes[1];
19141         var cs = el.firstChild.childNodes;
19142         this.indentNode = cs[0];
19143         this.ecNode = cs[1];
19144         this.iconNode = cs[2];
19145         var index = 3;
19146         if(cb){
19147             this.checkbox = cs[3];
19148             index++;
19149         }
19150         this.anchor = cs[index];
19151         
19152         this.textNode = cs[index].firstChild;
19153         
19154         //el.on("click", this.onClick, this);
19155         //el.on("dblclick", this.onDblClick, this);
19156         
19157         
19158        // console.log(this);
19159     },
19160     initEvents : function(){
19161         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19162         
19163             
19164         var a = this.ranchor;
19165
19166         var el = Roo.get(a);
19167
19168         if(Roo.isOpera){ // opera render bug ignores the CSS
19169             el.setStyle("text-decoration", "none");
19170         }
19171
19172         el.on("click", this.onClick, this);
19173         el.on("dblclick", this.onDblClick, this);
19174         el.on("contextmenu", this.onContextMenu, this);
19175         
19176     },
19177     
19178     /*onSelectedChange : function(state){
19179         if(state){
19180             this.focus();
19181             this.addClass("x-tree-selected");
19182         }else{
19183             //this.blur();
19184             this.removeClass("x-tree-selected");
19185         }
19186     },*/
19187     addClass : function(cls){
19188         if(this.elRow){
19189             Roo.fly(this.elRow).addClass(cls);
19190         }
19191         
19192     },
19193     
19194     
19195     removeClass : function(cls){
19196         if(this.elRow){
19197             Roo.fly(this.elRow).removeClass(cls);
19198         }
19199     }
19200
19201     
19202     
19203 });//<Script type="text/javascript">
19204
19205 /*
19206  * Based on:
19207  * Ext JS Library 1.1.1
19208  * Copyright(c) 2006-2007, Ext JS, LLC.
19209  *
19210  * Originally Released Under LGPL - original licence link has changed is not relivant.
19211  *
19212  * Fork - LGPL
19213  * <script type="text/javascript">
19214  */
19215  
19216
19217 /**
19218  * @class Roo.tree.ColumnTree
19219  * @extends Roo.data.TreePanel
19220  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19221  * @cfg {int} borderWidth  compined right/left border allowance
19222  * @constructor
19223  * @param {String/HTMLElement/Element} el The container element
19224  * @param {Object} config
19225  */
19226 Roo.tree.ColumnTree =  function(el, config)
19227 {
19228    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19229    this.addEvents({
19230         /**
19231         * @event resize
19232         * Fire this event on a container when it resizes
19233         * @param {int} w Width
19234         * @param {int} h Height
19235         */
19236        "resize" : true
19237     });
19238     this.on('resize', this.onResize, this);
19239 };
19240
19241 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19242     //lines:false,
19243     
19244     
19245     borderWidth: Roo.isBorderBox ? 0 : 2, 
19246     headEls : false,
19247     
19248     render : function(){
19249         // add the header.....
19250        
19251         Roo.tree.ColumnTree.superclass.render.apply(this);
19252         
19253         this.el.addClass('x-column-tree');
19254         
19255         this.headers = this.el.createChild(
19256             {cls:'x-tree-headers'},this.innerCt.dom);
19257    
19258         var cols = this.columns, c;
19259         var totalWidth = 0;
19260         this.headEls = [];
19261         var  len = cols.length;
19262         for(var i = 0; i < len; i++){
19263              c = cols[i];
19264              totalWidth += c.width;
19265             this.headEls.push(this.headers.createChild({
19266                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19267                  cn: {
19268                      cls:'x-tree-hd-text',
19269                      html: c.header
19270                  },
19271                  style:'width:'+(c.width-this.borderWidth)+'px;'
19272              }));
19273         }
19274         this.headers.createChild({cls:'x-clear'});
19275         // prevent floats from wrapping when clipped
19276         this.headers.setWidth(totalWidth);
19277         //this.innerCt.setWidth(totalWidth);
19278         this.innerCt.setStyle({ overflow: 'auto' });
19279         this.onResize(this.width, this.height);
19280              
19281         
19282     },
19283     onResize : function(w,h)
19284     {
19285         this.height = h;
19286         this.width = w;
19287         // resize cols..
19288         this.innerCt.setWidth(this.width);
19289         this.innerCt.setHeight(this.height-20);
19290         
19291         // headers...
19292         var cols = this.columns, c;
19293         var totalWidth = 0;
19294         var expEl = false;
19295         var len = cols.length;
19296         for(var i = 0; i < len; i++){
19297             c = cols[i];
19298             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19299                 // it's the expander..
19300                 expEl  = this.headEls[i];
19301                 continue;
19302             }
19303             totalWidth += c.width;
19304             
19305         }
19306         if (expEl) {
19307             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19308         }
19309         this.headers.setWidth(w-20);
19310
19311         
19312         
19313         
19314     }
19315 });
19316 /*
19317  * Based on:
19318  * Ext JS Library 1.1.1
19319  * Copyright(c) 2006-2007, Ext JS, LLC.
19320  *
19321  * Originally Released Under LGPL - original licence link has changed is not relivant.
19322  *
19323  * Fork - LGPL
19324  * <script type="text/javascript">
19325  */
19326  
19327 /**
19328  * @class Roo.menu.Menu
19329  * @extends Roo.util.Observable
19330  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19331  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19332  * @constructor
19333  * Creates a new Menu
19334  * @param {Object} config Configuration options
19335  */
19336 Roo.menu.Menu = function(config){
19337     Roo.apply(this, config);
19338     this.id = this.id || Roo.id();
19339     this.addEvents({
19340         /**
19341          * @event beforeshow
19342          * Fires before this menu is displayed
19343          * @param {Roo.menu.Menu} this
19344          */
19345         beforeshow : true,
19346         /**
19347          * @event beforehide
19348          * Fires before this menu is hidden
19349          * @param {Roo.menu.Menu} this
19350          */
19351         beforehide : true,
19352         /**
19353          * @event show
19354          * Fires after this menu is displayed
19355          * @param {Roo.menu.Menu} this
19356          */
19357         show : true,
19358         /**
19359          * @event hide
19360          * Fires after this menu is hidden
19361          * @param {Roo.menu.Menu} this
19362          */
19363         hide : true,
19364         /**
19365          * @event click
19366          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19367          * @param {Roo.menu.Menu} this
19368          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19369          * @param {Roo.EventObject} e
19370          */
19371         click : true,
19372         /**
19373          * @event mouseover
19374          * Fires when the mouse is hovering over this menu
19375          * @param {Roo.menu.Menu} this
19376          * @param {Roo.EventObject} e
19377          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19378          */
19379         mouseover : true,
19380         /**
19381          * @event mouseout
19382          * Fires when the mouse exits this menu
19383          * @param {Roo.menu.Menu} this
19384          * @param {Roo.EventObject} e
19385          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19386          */
19387         mouseout : true,
19388         /**
19389          * @event itemclick
19390          * Fires when a menu item contained in this menu is clicked
19391          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19392          * @param {Roo.EventObject} e
19393          */
19394         itemclick: true
19395     });
19396     if (this.registerMenu) {
19397         Roo.menu.MenuMgr.register(this);
19398     }
19399     
19400     var mis = this.items;
19401     this.items = new Roo.util.MixedCollection();
19402     if(mis){
19403         this.add.apply(this, mis);
19404     }
19405 };
19406
19407 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19408     /**
19409      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19410      */
19411     minWidth : 120,
19412     /**
19413      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19414      * for bottom-right shadow (defaults to "sides")
19415      */
19416     shadow : "sides",
19417     /**
19418      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19419      * this menu (defaults to "tl-tr?")
19420      */
19421     subMenuAlign : "tl-tr?",
19422     /**
19423      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19424      * relative to its element of origin (defaults to "tl-bl?")
19425      */
19426     defaultAlign : "tl-bl?",
19427     /**
19428      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19429      */
19430     allowOtherMenus : false,
19431     /**
19432      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19433      */
19434     registerMenu : true,
19435
19436     hidden:true,
19437
19438     // private
19439     render : function(){
19440         if(this.el){
19441             return;
19442         }
19443         var el = this.el = new Roo.Layer({
19444             cls: "x-menu",
19445             shadow:this.shadow,
19446             constrain: false,
19447             parentEl: this.parentEl || document.body,
19448             zindex:15000
19449         });
19450
19451         this.keyNav = new Roo.menu.MenuNav(this);
19452
19453         if(this.plain){
19454             el.addClass("x-menu-plain");
19455         }
19456         if(this.cls){
19457             el.addClass(this.cls);
19458         }
19459         // generic focus element
19460         this.focusEl = el.createChild({
19461             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19462         });
19463         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19464         ul.on("click", this.onClick, this);
19465         ul.on("mouseover", this.onMouseOver, this);
19466         ul.on("mouseout", this.onMouseOut, this);
19467         this.items.each(function(item){
19468             var li = document.createElement("li");
19469             li.className = "x-menu-list-item";
19470             ul.dom.appendChild(li);
19471             item.render(li, this);
19472         }, this);
19473         this.ul = ul;
19474         this.autoWidth();
19475     },
19476
19477     // private
19478     autoWidth : function(){
19479         var el = this.el, ul = this.ul;
19480         if(!el){
19481             return;
19482         }
19483         var w = this.width;
19484         if(w){
19485             el.setWidth(w);
19486         }else if(Roo.isIE){
19487             el.setWidth(this.minWidth);
19488             var t = el.dom.offsetWidth; // force recalc
19489             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19490         }
19491     },
19492
19493     // private
19494     delayAutoWidth : function(){
19495         if(this.rendered){
19496             if(!this.awTask){
19497                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19498             }
19499             this.awTask.delay(20);
19500         }
19501     },
19502
19503     // private
19504     findTargetItem : function(e){
19505         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19506         if(t && t.menuItemId){
19507             return this.items.get(t.menuItemId);
19508         }
19509     },
19510
19511     // private
19512     onClick : function(e){
19513         var t;
19514         if(t = this.findTargetItem(e)){
19515             t.onClick(e);
19516             this.fireEvent("click", this, t, e);
19517         }
19518     },
19519
19520     // private
19521     setActiveItem : function(item, autoExpand){
19522         if(item != this.activeItem){
19523             if(this.activeItem){
19524                 this.activeItem.deactivate();
19525             }
19526             this.activeItem = item;
19527             item.activate(autoExpand);
19528         }else if(autoExpand){
19529             item.expandMenu();
19530         }
19531     },
19532
19533     // private
19534     tryActivate : function(start, step){
19535         var items = this.items;
19536         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19537             var item = items.get(i);
19538             if(!item.disabled && item.canActivate){
19539                 this.setActiveItem(item, false);
19540                 return item;
19541             }
19542         }
19543         return false;
19544     },
19545
19546     // private
19547     onMouseOver : function(e){
19548         var t;
19549         if(t = this.findTargetItem(e)){
19550             if(t.canActivate && !t.disabled){
19551                 this.setActiveItem(t, true);
19552             }
19553         }
19554         this.fireEvent("mouseover", this, e, t);
19555     },
19556
19557     // private
19558     onMouseOut : function(e){
19559         var t;
19560         if(t = this.findTargetItem(e)){
19561             if(t == this.activeItem && t.shouldDeactivate(e)){
19562                 this.activeItem.deactivate();
19563                 delete this.activeItem;
19564             }
19565         }
19566         this.fireEvent("mouseout", this, e, t);
19567     },
19568
19569     /**
19570      * Read-only.  Returns true if the menu is currently displayed, else false.
19571      * @type Boolean
19572      */
19573     isVisible : function(){
19574         return this.el && !this.hidden;
19575     },
19576
19577     /**
19578      * Displays this menu relative to another element
19579      * @param {String/HTMLElement/Roo.Element} element The element to align to
19580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19581      * the element (defaults to this.defaultAlign)
19582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19583      */
19584     show : function(el, pos, parentMenu){
19585         this.parentMenu = parentMenu;
19586         if(!this.el){
19587             this.render();
19588         }
19589         this.fireEvent("beforeshow", this);
19590         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19591     },
19592
19593     /**
19594      * Displays this menu at a specific xy position
19595      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19597      */
19598     showAt : function(xy, parentMenu, /* private: */_e){
19599         this.parentMenu = parentMenu;
19600         if(!this.el){
19601             this.render();
19602         }
19603         if(_e !== false){
19604             this.fireEvent("beforeshow", this);
19605             xy = this.el.adjustForConstraints(xy);
19606         }
19607         this.el.setXY(xy);
19608         this.el.show();
19609         this.hidden = false;
19610         this.focus();
19611         this.fireEvent("show", this);
19612     },
19613
19614     focus : function(){
19615         if(!this.hidden){
19616             this.doFocus.defer(50, this);
19617         }
19618     },
19619
19620     doFocus : function(){
19621         if(!this.hidden){
19622             this.focusEl.focus();
19623         }
19624     },
19625
19626     /**
19627      * Hides this menu and optionally all parent menus
19628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19629      */
19630     hide : function(deep){
19631         if(this.el && this.isVisible()){
19632             this.fireEvent("beforehide", this);
19633             if(this.activeItem){
19634                 this.activeItem.deactivate();
19635                 this.activeItem = null;
19636             }
19637             this.el.hide();
19638             this.hidden = true;
19639             this.fireEvent("hide", this);
19640         }
19641         if(deep === true && this.parentMenu){
19642             this.parentMenu.hide(true);
19643         }
19644     },
19645
19646     /**
19647      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19648      * Any of the following are valid:
19649      * <ul>
19650      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19651      * <li>An HTMLElement object which will be converted to a menu item</li>
19652      * <li>A menu item config object that will be created as a new menu item</li>
19653      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19654      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19655      * </ul>
19656      * Usage:
19657      * <pre><code>
19658 // Create the menu
19659 var menu = new Roo.menu.Menu();
19660
19661 // Create a menu item to add by reference
19662 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19663
19664 // Add a bunch of items at once using different methods.
19665 // Only the last item added will be returned.
19666 var item = menu.add(
19667     menuItem,                // add existing item by ref
19668     'Dynamic Item',          // new TextItem
19669     '-',                     // new separator
19670     { text: 'Config Item' }  // new item by config
19671 );
19672 </code></pre>
19673      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19674      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19675      */
19676     add : function(){
19677         var a = arguments, l = a.length, item;
19678         for(var i = 0; i < l; i++){
19679             var el = a[i];
19680             if ((typeof(el) == "object") && el.xtype && el.xns) {
19681                 el = Roo.factory(el, Roo.menu);
19682             }
19683             
19684             if(el.render){ // some kind of Item
19685                 item = this.addItem(el);
19686             }else if(typeof el == "string"){ // string
19687                 if(el == "separator" || el == "-"){
19688                     item = this.addSeparator();
19689                 }else{
19690                     item = this.addText(el);
19691                 }
19692             }else if(el.tagName || el.el){ // element
19693                 item = this.addElement(el);
19694             }else if(typeof el == "object"){ // must be menu item config?
19695                 item = this.addMenuItem(el);
19696             }
19697         }
19698         return item;
19699     },
19700
19701     /**
19702      * Returns this menu's underlying {@link Roo.Element} object
19703      * @return {Roo.Element} The element
19704      */
19705     getEl : function(){
19706         if(!this.el){
19707             this.render();
19708         }
19709         return this.el;
19710     },
19711
19712     /**
19713      * Adds a separator bar to the menu
19714      * @return {Roo.menu.Item} The menu item that was added
19715      */
19716     addSeparator : function(){
19717         return this.addItem(new Roo.menu.Separator());
19718     },
19719
19720     /**
19721      * Adds an {@link Roo.Element} object to the menu
19722      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19723      * @return {Roo.menu.Item} The menu item that was added
19724      */
19725     addElement : function(el){
19726         return this.addItem(new Roo.menu.BaseItem(el));
19727     },
19728
19729     /**
19730      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19731      * @param {Roo.menu.Item} item The menu item to add
19732      * @return {Roo.menu.Item} The menu item that was added
19733      */
19734     addItem : function(item){
19735         this.items.add(item);
19736         if(this.ul){
19737             var li = document.createElement("li");
19738             li.className = "x-menu-list-item";
19739             this.ul.dom.appendChild(li);
19740             item.render(li, this);
19741             this.delayAutoWidth();
19742         }
19743         return item;
19744     },
19745
19746     /**
19747      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19748      * @param {Object} config A MenuItem config object
19749      * @return {Roo.menu.Item} The menu item that was added
19750      */
19751     addMenuItem : function(config){
19752         if(!(config instanceof Roo.menu.Item)){
19753             if(typeof config.checked == "boolean"){ // must be check menu item config?
19754                 config = new Roo.menu.CheckItem(config);
19755             }else{
19756                 config = new Roo.menu.Item(config);
19757             }
19758         }
19759         return this.addItem(config);
19760     },
19761
19762     /**
19763      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19764      * @param {String} text The text to display in the menu item
19765      * @return {Roo.menu.Item} The menu item that was added
19766      */
19767     addText : function(text){
19768         return this.addItem(new Roo.menu.TextItem({ text : text }));
19769     },
19770
19771     /**
19772      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19773      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19774      * @param {Roo.menu.Item} item The menu item to add
19775      * @return {Roo.menu.Item} The menu item that was added
19776      */
19777     insert : function(index, item){
19778         this.items.insert(index, item);
19779         if(this.ul){
19780             var li = document.createElement("li");
19781             li.className = "x-menu-list-item";
19782             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19783             item.render(li, this);
19784             this.delayAutoWidth();
19785         }
19786         return item;
19787     },
19788
19789     /**
19790      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19791      * @param {Roo.menu.Item} item The menu item to remove
19792      */
19793     remove : function(item){
19794         this.items.removeKey(item.id);
19795         item.destroy();
19796     },
19797
19798     /**
19799      * Removes and destroys all items in the menu
19800      */
19801     removeAll : function(){
19802         var f;
19803         while(f = this.items.first()){
19804             this.remove(f);
19805         }
19806     }
19807 });
19808
19809 // MenuNav is a private utility class used internally by the Menu
19810 Roo.menu.MenuNav = function(menu){
19811     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19812     this.scope = this.menu = menu;
19813 };
19814
19815 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19816     doRelay : function(e, h){
19817         var k = e.getKey();
19818         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19819             this.menu.tryActivate(0, 1);
19820             return false;
19821         }
19822         return h.call(this.scope || this, e, this.menu);
19823     },
19824
19825     up : function(e, m){
19826         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19827             m.tryActivate(m.items.length-1, -1);
19828         }
19829     },
19830
19831     down : function(e, m){
19832         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19833             m.tryActivate(0, 1);
19834         }
19835     },
19836
19837     right : function(e, m){
19838         if(m.activeItem){
19839             m.activeItem.expandMenu(true);
19840         }
19841     },
19842
19843     left : function(e, m){
19844         m.hide();
19845         if(m.parentMenu && m.parentMenu.activeItem){
19846             m.parentMenu.activeItem.activate();
19847         }
19848     },
19849
19850     enter : function(e, m){
19851         if(m.activeItem){
19852             e.stopPropagation();
19853             m.activeItem.onClick(e);
19854             m.fireEvent("click", this, m.activeItem);
19855             return true;
19856         }
19857     }
19858 });/*
19859  * Based on:
19860  * Ext JS Library 1.1.1
19861  * Copyright(c) 2006-2007, Ext JS, LLC.
19862  *
19863  * Originally Released Under LGPL - original licence link has changed is not relivant.
19864  *
19865  * Fork - LGPL
19866  * <script type="text/javascript">
19867  */
19868  
19869 /**
19870  * @class Roo.menu.MenuMgr
19871  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19872  * @singleton
19873  */
19874 Roo.menu.MenuMgr = function(){
19875    var menus, active, groups = {}, attached = false, lastShow = new Date();
19876
19877    // private - called when first menu is created
19878    function init(){
19879        menus = {};
19880        active = new Roo.util.MixedCollection();
19881        Roo.get(document).addKeyListener(27, function(){
19882            if(active.length > 0){
19883                hideAll();
19884            }
19885        });
19886    }
19887
19888    // private
19889    function hideAll(){
19890        if(active && active.length > 0){
19891            var c = active.clone();
19892            c.each(function(m){
19893                m.hide();
19894            });
19895        }
19896    }
19897
19898    // private
19899    function onHide(m){
19900        active.remove(m);
19901        if(active.length < 1){
19902            Roo.get(document).un("mousedown", onMouseDown);
19903            attached = false;
19904        }
19905    }
19906
19907    // private
19908    function onShow(m){
19909        var last = active.last();
19910        lastShow = new Date();
19911        active.add(m);
19912        if(!attached){
19913            Roo.get(document).on("mousedown", onMouseDown);
19914            attached = true;
19915        }
19916        if(m.parentMenu){
19917           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19918           m.parentMenu.activeChild = m;
19919        }else if(last && last.isVisible()){
19920           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19921        }
19922    }
19923
19924    // private
19925    function onBeforeHide(m){
19926        if(m.activeChild){
19927            m.activeChild.hide();
19928        }
19929        if(m.autoHideTimer){
19930            clearTimeout(m.autoHideTimer);
19931            delete m.autoHideTimer;
19932        }
19933    }
19934
19935    // private
19936    function onBeforeShow(m){
19937        var pm = m.parentMenu;
19938        if(!pm && !m.allowOtherMenus){
19939            hideAll();
19940        }else if(pm && pm.activeChild && active != m){
19941            pm.activeChild.hide();
19942        }
19943    }
19944
19945    // private
19946    function onMouseDown(e){
19947        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19948            hideAll();
19949        }
19950    }
19951
19952    // private
19953    function onBeforeCheck(mi, state){
19954        if(state){
19955            var g = groups[mi.group];
19956            for(var i = 0, l = g.length; i < l; i++){
19957                if(g[i] != mi){
19958                    g[i].setChecked(false);
19959                }
19960            }
19961        }
19962    }
19963
19964    return {
19965
19966        /**
19967         * Hides all menus that are currently visible
19968         */
19969        hideAll : function(){
19970             hideAll();  
19971        },
19972
19973        // private
19974        register : function(menu){
19975            if(!menus){
19976                init();
19977            }
19978            menus[menu.id] = menu;
19979            menu.on("beforehide", onBeforeHide);
19980            menu.on("hide", onHide);
19981            menu.on("beforeshow", onBeforeShow);
19982            menu.on("show", onShow);
19983            var g = menu.group;
19984            if(g && menu.events["checkchange"]){
19985                if(!groups[g]){
19986                    groups[g] = [];
19987                }
19988                groups[g].push(menu);
19989                menu.on("checkchange", onCheck);
19990            }
19991        },
19992
19993         /**
19994          * Returns a {@link Roo.menu.Menu} object
19995          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19996          * be used to generate and return a new Menu instance.
19997          */
19998        get : function(menu){
19999            if(typeof menu == "string"){ // menu id
20000                return menus[menu];
20001            }else if(menu.events){  // menu instance
20002                return menu;
20003            }else if(typeof menu.length == 'number'){ // array of menu items?
20004                return new Roo.menu.Menu({items:menu});
20005            }else{ // otherwise, must be a config
20006                return new Roo.menu.Menu(menu);
20007            }
20008        },
20009
20010        // private
20011        unregister : function(menu){
20012            delete menus[menu.id];
20013            menu.un("beforehide", onBeforeHide);
20014            menu.un("hide", onHide);
20015            menu.un("beforeshow", onBeforeShow);
20016            menu.un("show", onShow);
20017            var g = menu.group;
20018            if(g && menu.events["checkchange"]){
20019                groups[g].remove(menu);
20020                menu.un("checkchange", onCheck);
20021            }
20022        },
20023
20024        // private
20025        registerCheckable : function(menuItem){
20026            var g = menuItem.group;
20027            if(g){
20028                if(!groups[g]){
20029                    groups[g] = [];
20030                }
20031                groups[g].push(menuItem);
20032                menuItem.on("beforecheckchange", onBeforeCheck);
20033            }
20034        },
20035
20036        // private
20037        unregisterCheckable : function(menuItem){
20038            var g = menuItem.group;
20039            if(g){
20040                groups[g].remove(menuItem);
20041                menuItem.un("beforecheckchange", onBeforeCheck);
20042            }
20043        }
20044    };
20045 }();/*
20046  * Based on:
20047  * Ext JS Library 1.1.1
20048  * Copyright(c) 2006-2007, Ext JS, LLC.
20049  *
20050  * Originally Released Under LGPL - original licence link has changed is not relivant.
20051  *
20052  * Fork - LGPL
20053  * <script type="text/javascript">
20054  */
20055  
20056
20057 /**
20058  * @class Roo.menu.BaseItem
20059  * @extends Roo.Component
20060  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20061  * management and base configuration options shared by all menu components.
20062  * @constructor
20063  * Creates a new BaseItem
20064  * @param {Object} config Configuration options
20065  */
20066 Roo.menu.BaseItem = function(config){
20067     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20068
20069     this.addEvents({
20070         /**
20071          * @event click
20072          * Fires when this item is clicked
20073          * @param {Roo.menu.BaseItem} this
20074          * @param {Roo.EventObject} e
20075          */
20076         click: true,
20077         /**
20078          * @event activate
20079          * Fires when this item is activated
20080          * @param {Roo.menu.BaseItem} this
20081          */
20082         activate : true,
20083         /**
20084          * @event deactivate
20085          * Fires when this item is deactivated
20086          * @param {Roo.menu.BaseItem} this
20087          */
20088         deactivate : true
20089     });
20090
20091     if(this.handler){
20092         this.on("click", this.handler, this.scope, true);
20093     }
20094 };
20095
20096 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20097     /**
20098      * @cfg {Function} handler
20099      * A function that will handle the click event of this menu item (defaults to undefined)
20100      */
20101     /**
20102      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20103      */
20104     canActivate : false,
20105     /**
20106      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20107      */
20108     activeClass : "x-menu-item-active",
20109     /**
20110      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20111      */
20112     hideOnClick : true,
20113     /**
20114      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20115      */
20116     hideDelay : 100,
20117
20118     // private
20119     ctype: "Roo.menu.BaseItem",
20120
20121     // private
20122     actionMode : "container",
20123
20124     // private
20125     render : function(container, parentMenu){
20126         this.parentMenu = parentMenu;
20127         Roo.menu.BaseItem.superclass.render.call(this, container);
20128         this.container.menuItemId = this.id;
20129     },
20130
20131     // private
20132     onRender : function(container, position){
20133         this.el = Roo.get(this.el);
20134         container.dom.appendChild(this.el.dom);
20135     },
20136
20137     // private
20138     onClick : function(e){
20139         if(!this.disabled && this.fireEvent("click", this, e) !== false
20140                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20141             this.handleClick(e);
20142         }else{
20143             e.stopEvent();
20144         }
20145     },
20146
20147     // private
20148     activate : function(){
20149         if(this.disabled){
20150             return false;
20151         }
20152         var li = this.container;
20153         li.addClass(this.activeClass);
20154         this.region = li.getRegion().adjust(2, 2, -2, -2);
20155         this.fireEvent("activate", this);
20156         return true;
20157     },
20158
20159     // private
20160     deactivate : function(){
20161         this.container.removeClass(this.activeClass);
20162         this.fireEvent("deactivate", this);
20163     },
20164
20165     // private
20166     shouldDeactivate : function(e){
20167         return !this.region || !this.region.contains(e.getPoint());
20168     },
20169
20170     // private
20171     handleClick : function(e){
20172         if(this.hideOnClick){
20173             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20174         }
20175     },
20176
20177     // private
20178     expandMenu : function(autoActivate){
20179         // do nothing
20180     },
20181
20182     // private
20183     hideMenu : function(){
20184         // do nothing
20185     }
20186 });/*
20187  * Based on:
20188  * Ext JS Library 1.1.1
20189  * Copyright(c) 2006-2007, Ext JS, LLC.
20190  *
20191  * Originally Released Under LGPL - original licence link has changed is not relivant.
20192  *
20193  * Fork - LGPL
20194  * <script type="text/javascript">
20195  */
20196  
20197 /**
20198  * @class Roo.menu.Adapter
20199  * @extends Roo.menu.BaseItem
20200  * 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.
20201  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20202  * @constructor
20203  * Creates a new Adapter
20204  * @param {Object} config Configuration options
20205  */
20206 Roo.menu.Adapter = function(component, config){
20207     Roo.menu.Adapter.superclass.constructor.call(this, config);
20208     this.component = component;
20209 };
20210 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20211     // private
20212     canActivate : true,
20213
20214     // private
20215     onRender : function(container, position){
20216         this.component.render(container);
20217         this.el = this.component.getEl();
20218     },
20219
20220     // private
20221     activate : function(){
20222         if(this.disabled){
20223             return false;
20224         }
20225         this.component.focus();
20226         this.fireEvent("activate", this);
20227         return true;
20228     },
20229
20230     // private
20231     deactivate : function(){
20232         this.fireEvent("deactivate", this);
20233     },
20234
20235     // private
20236     disable : function(){
20237         this.component.disable();
20238         Roo.menu.Adapter.superclass.disable.call(this);
20239     },
20240
20241     // private
20242     enable : function(){
20243         this.component.enable();
20244         Roo.menu.Adapter.superclass.enable.call(this);
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256
20257 /**
20258  * @class Roo.menu.TextItem
20259  * @extends Roo.menu.BaseItem
20260  * Adds a static text string to a menu, usually used as either a heading or group separator.
20261  * Note: old style constructor with text is still supported.
20262  * 
20263  * @constructor
20264  * Creates a new TextItem
20265  * @param {Object} cfg Configuration
20266  */
20267 Roo.menu.TextItem = function(cfg){
20268     if (typeof(cfg) == 'string') {
20269         this.text = cfg;
20270     } else {
20271         Roo.apply(this,cfg);
20272     }
20273     
20274     Roo.menu.TextItem.superclass.constructor.call(this);
20275 };
20276
20277 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20278     /**
20279      * @cfg {Boolean} text Text to show on item.
20280      */
20281     text : '',
20282     
20283     /**
20284      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20285      */
20286     hideOnClick : false,
20287     /**
20288      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20289      */
20290     itemCls : "x-menu-text",
20291
20292     // private
20293     onRender : function(){
20294         var s = document.createElement("span");
20295         s.className = this.itemCls;
20296         s.innerHTML = this.text;
20297         this.el = s;
20298         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20299     }
20300 });/*
20301  * Based on:
20302  * Ext JS Library 1.1.1
20303  * Copyright(c) 2006-2007, Ext JS, LLC.
20304  *
20305  * Originally Released Under LGPL - original licence link has changed is not relivant.
20306  *
20307  * Fork - LGPL
20308  * <script type="text/javascript">
20309  */
20310
20311 /**
20312  * @class Roo.menu.Separator
20313  * @extends Roo.menu.BaseItem
20314  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20315  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20316  * @constructor
20317  * @param {Object} config Configuration options
20318  */
20319 Roo.menu.Separator = function(config){
20320     Roo.menu.Separator.superclass.constructor.call(this, config);
20321 };
20322
20323 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20324     /**
20325      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20326      */
20327     itemCls : "x-menu-sep",
20328     /**
20329      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20330      */
20331     hideOnClick : false,
20332
20333     // private
20334     onRender : function(li){
20335         var s = document.createElement("span");
20336         s.className = this.itemCls;
20337         s.innerHTML = "&#160;";
20338         this.el = s;
20339         li.addClass("x-menu-sep-li");
20340         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20341     }
20342 });/*
20343  * Based on:
20344  * Ext JS Library 1.1.1
20345  * Copyright(c) 2006-2007, Ext JS, LLC.
20346  *
20347  * Originally Released Under LGPL - original licence link has changed is not relivant.
20348  *
20349  * Fork - LGPL
20350  * <script type="text/javascript">
20351  */
20352 /**
20353  * @class Roo.menu.Item
20354  * @extends Roo.menu.BaseItem
20355  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20356  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20357  * activation and click handling.
20358  * @constructor
20359  * Creates a new Item
20360  * @param {Object} config Configuration options
20361  */
20362 Roo.menu.Item = function(config){
20363     Roo.menu.Item.superclass.constructor.call(this, config);
20364     if(this.menu){
20365         this.menu = Roo.menu.MenuMgr.get(this.menu);
20366     }
20367 };
20368 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20369     
20370     /**
20371      * @cfg {String} text
20372      * The text to show on the menu item.
20373      */
20374     text: '',
20375      /**
20376      * @cfg {String} HTML to render in menu
20377      * The text to show on the menu item (HTML version).
20378      */
20379     html: '',
20380     /**
20381      * @cfg {String} icon
20382      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20383      */
20384     icon: undefined,
20385     /**
20386      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20387      */
20388     itemCls : "x-menu-item",
20389     /**
20390      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20391      */
20392     canActivate : true,
20393     /**
20394      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20395      */
20396     showDelay: 200,
20397     // doc'd in BaseItem
20398     hideDelay: 200,
20399
20400     // private
20401     ctype: "Roo.menu.Item",
20402     
20403     // private
20404     onRender : function(container, position){
20405         var el = document.createElement("a");
20406         el.hideFocus = true;
20407         el.unselectable = "on";
20408         el.href = this.href || "#";
20409         if(this.hrefTarget){
20410             el.target = this.hrefTarget;
20411         }
20412         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20413         
20414         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20415         
20416         el.innerHTML = String.format(
20417                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20418                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20419         this.el = el;
20420         Roo.menu.Item.superclass.onRender.call(this, container, position);
20421     },
20422
20423     /**
20424      * Sets the text to display in this menu item
20425      * @param {String} text The text to display
20426      * @param {Boolean} isHTML true to indicate text is pure html.
20427      */
20428     setText : function(text, isHTML){
20429         if (isHTML) {
20430             this.html = text;
20431         } else {
20432             this.text = text;
20433             this.html = '';
20434         }
20435         if(this.rendered){
20436             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20437      
20438             this.el.update(String.format(
20439                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20440                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20441             this.parentMenu.autoWidth();
20442         }
20443     },
20444
20445     // private
20446     handleClick : function(e){
20447         if(!this.href){ // if no link defined, stop the event automatically
20448             e.stopEvent();
20449         }
20450         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20451     },
20452
20453     // private
20454     activate : function(autoExpand){
20455         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20456             this.focus();
20457             if(autoExpand){
20458                 this.expandMenu();
20459             }
20460         }
20461         return true;
20462     },
20463
20464     // private
20465     shouldDeactivate : function(e){
20466         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20467             if(this.menu && this.menu.isVisible()){
20468                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20469             }
20470             return true;
20471         }
20472         return false;
20473     },
20474
20475     // private
20476     deactivate : function(){
20477         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20478         this.hideMenu();
20479     },
20480
20481     // private
20482     expandMenu : function(autoActivate){
20483         if(!this.disabled && this.menu){
20484             clearTimeout(this.hideTimer);
20485             delete this.hideTimer;
20486             if(!this.menu.isVisible() && !this.showTimer){
20487                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20488             }else if (this.menu.isVisible() && autoActivate){
20489                 this.menu.tryActivate(0, 1);
20490             }
20491         }
20492     },
20493
20494     // private
20495     deferExpand : function(autoActivate){
20496         delete this.showTimer;
20497         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20498         if(autoActivate){
20499             this.menu.tryActivate(0, 1);
20500         }
20501     },
20502
20503     // private
20504     hideMenu : function(){
20505         clearTimeout(this.showTimer);
20506         delete this.showTimer;
20507         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20508             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20509         }
20510     },
20511
20512     // private
20513     deferHide : function(){
20514         delete this.hideTimer;
20515         this.menu.hide();
20516     }
20517 });/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527  
20528 /**
20529  * @class Roo.menu.CheckItem
20530  * @extends Roo.menu.Item
20531  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20532  * @constructor
20533  * Creates a new CheckItem
20534  * @param {Object} config Configuration options
20535  */
20536 Roo.menu.CheckItem = function(config){
20537     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20538     this.addEvents({
20539         /**
20540          * @event beforecheckchange
20541          * Fires before the checked value is set, providing an opportunity to cancel if needed
20542          * @param {Roo.menu.CheckItem} this
20543          * @param {Boolean} checked The new checked value that will be set
20544          */
20545         "beforecheckchange" : true,
20546         /**
20547          * @event checkchange
20548          * Fires after the checked value has been set
20549          * @param {Roo.menu.CheckItem} this
20550          * @param {Boolean} checked The checked value that was set
20551          */
20552         "checkchange" : true
20553     });
20554     if(this.checkHandler){
20555         this.on('checkchange', this.checkHandler, this.scope);
20556     }
20557 };
20558 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20559     /**
20560      * @cfg {String} group
20561      * All check items with the same group name will automatically be grouped into a single-select
20562      * radio button group (defaults to '')
20563      */
20564     /**
20565      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20566      */
20567     itemCls : "x-menu-item x-menu-check-item",
20568     /**
20569      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20570      */
20571     groupClass : "x-menu-group-item",
20572
20573     /**
20574      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20575      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20576      * initialized with checked = true will be rendered as checked.
20577      */
20578     checked: false,
20579
20580     // private
20581     ctype: "Roo.menu.CheckItem",
20582
20583     // private
20584     onRender : function(c){
20585         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20586         if(this.group){
20587             this.el.addClass(this.groupClass);
20588         }
20589         Roo.menu.MenuMgr.registerCheckable(this);
20590         if(this.checked){
20591             this.checked = false;
20592             this.setChecked(true, true);
20593         }
20594     },
20595
20596     // private
20597     destroy : function(){
20598         if(this.rendered){
20599             Roo.menu.MenuMgr.unregisterCheckable(this);
20600         }
20601         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20602     },
20603
20604     /**
20605      * Set the checked state of this item
20606      * @param {Boolean} checked The new checked value
20607      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20608      */
20609     setChecked : function(state, suppressEvent){
20610         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20611             if(this.container){
20612                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20613             }
20614             this.checked = state;
20615             if(suppressEvent !== true){
20616                 this.fireEvent("checkchange", this, state);
20617             }
20618         }
20619     },
20620
20621     // private
20622     handleClick : function(e){
20623        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20624            this.setChecked(!this.checked);
20625        }
20626        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20627     }
20628 });/*
20629  * Based on:
20630  * Ext JS Library 1.1.1
20631  * Copyright(c) 2006-2007, Ext JS, LLC.
20632  *
20633  * Originally Released Under LGPL - original licence link has changed is not relivant.
20634  *
20635  * Fork - LGPL
20636  * <script type="text/javascript">
20637  */
20638  
20639 /**
20640  * @class Roo.menu.DateItem
20641  * @extends Roo.menu.Adapter
20642  * A menu item that wraps the {@link Roo.DatPicker} component.
20643  * @constructor
20644  * Creates a new DateItem
20645  * @param {Object} config Configuration options
20646  */
20647 Roo.menu.DateItem = function(config){
20648     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20649     /** The Roo.DatePicker object @type Roo.DatePicker */
20650     this.picker = this.component;
20651     this.addEvents({select: true});
20652     
20653     this.picker.on("render", function(picker){
20654         picker.getEl().swallowEvent("click");
20655         picker.container.addClass("x-menu-date-item");
20656     });
20657
20658     this.picker.on("select", this.onSelect, this);
20659 };
20660
20661 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20662     // private
20663     onSelect : function(picker, date){
20664         this.fireEvent("select", this, date, picker);
20665         Roo.menu.DateItem.superclass.handleClick.call(this);
20666     }
20667 });/*
20668  * Based on:
20669  * Ext JS Library 1.1.1
20670  * Copyright(c) 2006-2007, Ext JS, LLC.
20671  *
20672  * Originally Released Under LGPL - original licence link has changed is not relivant.
20673  *
20674  * Fork - LGPL
20675  * <script type="text/javascript">
20676  */
20677  
20678 /**
20679  * @class Roo.menu.ColorItem
20680  * @extends Roo.menu.Adapter
20681  * A menu item that wraps the {@link Roo.ColorPalette} component.
20682  * @constructor
20683  * Creates a new ColorItem
20684  * @param {Object} config Configuration options
20685  */
20686 Roo.menu.ColorItem = function(config){
20687     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20688     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20689     this.palette = this.component;
20690     this.relayEvents(this.palette, ["select"]);
20691     if(this.selectHandler){
20692         this.on('select', this.selectHandler, this.scope);
20693     }
20694 };
20695 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20696  * Based on:
20697  * Ext JS Library 1.1.1
20698  * Copyright(c) 2006-2007, Ext JS, LLC.
20699  *
20700  * Originally Released Under LGPL - original licence link has changed is not relivant.
20701  *
20702  * Fork - LGPL
20703  * <script type="text/javascript">
20704  */
20705  
20706
20707 /**
20708  * @class Roo.menu.DateMenu
20709  * @extends Roo.menu.Menu
20710  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20711  * @constructor
20712  * Creates a new DateMenu
20713  * @param {Object} config Configuration options
20714  */
20715 Roo.menu.DateMenu = function(config){
20716     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20717     this.plain = true;
20718     var di = new Roo.menu.DateItem(config);
20719     this.add(di);
20720     /**
20721      * The {@link Roo.DatePicker} instance for this DateMenu
20722      * @type DatePicker
20723      */
20724     this.picker = di.picker;
20725     /**
20726      * @event select
20727      * @param {DatePicker} picker
20728      * @param {Date} date
20729      */
20730     this.relayEvents(di, ["select"]);
20731
20732     this.on('beforeshow', function(){
20733         if(this.picker){
20734             this.picker.hideMonthPicker(true);
20735         }
20736     }, this);
20737 };
20738 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20739     cls:'x-date-menu'
20740 });/*
20741  * Based on:
20742  * Ext JS Library 1.1.1
20743  * Copyright(c) 2006-2007, Ext JS, LLC.
20744  *
20745  * Originally Released Under LGPL - original licence link has changed is not relivant.
20746  *
20747  * Fork - LGPL
20748  * <script type="text/javascript">
20749  */
20750  
20751
20752 /**
20753  * @class Roo.menu.ColorMenu
20754  * @extends Roo.menu.Menu
20755  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20756  * @constructor
20757  * Creates a new ColorMenu
20758  * @param {Object} config Configuration options
20759  */
20760 Roo.menu.ColorMenu = function(config){
20761     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20762     this.plain = true;
20763     var ci = new Roo.menu.ColorItem(config);
20764     this.add(ci);
20765     /**
20766      * The {@link Roo.ColorPalette} instance for this ColorMenu
20767      * @type ColorPalette
20768      */
20769     this.palette = ci.palette;
20770     /**
20771      * @event select
20772      * @param {ColorPalette} palette
20773      * @param {String} color
20774      */
20775     this.relayEvents(ci, ["select"]);
20776 };
20777 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20778  * Based on:
20779  * Ext JS Library 1.1.1
20780  * Copyright(c) 2006-2007, Ext JS, LLC.
20781  *
20782  * Originally Released Under LGPL - original licence link has changed is not relivant.
20783  *
20784  * Fork - LGPL
20785  * <script type="text/javascript">
20786  */
20787  
20788 /**
20789  * @class Roo.form.Field
20790  * @extends Roo.BoxComponent
20791  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20792  * @constructor
20793  * Creates a new Field
20794  * @param {Object} config Configuration options
20795  */
20796 Roo.form.Field = function(config){
20797     Roo.form.Field.superclass.constructor.call(this, config);
20798 };
20799
20800 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20801     /**
20802      * @cfg {String} fieldLabel Label to use when rendering a form.
20803      */
20804        /**
20805      * @cfg {String} qtip Mouse over tip
20806      */
20807      
20808     /**
20809      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20810      */
20811     invalidClass : "x-form-invalid",
20812     /**
20813      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20814      */
20815     invalidText : "The value in this field is invalid",
20816     /**
20817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20818      */
20819     focusClass : "x-form-focus",
20820     /**
20821      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20822       automatic validation (defaults to "keyup").
20823      */
20824     validationEvent : "keyup",
20825     /**
20826      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20827      */
20828     validateOnBlur : true,
20829     /**
20830      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20831      */
20832     validationDelay : 250,
20833     /**
20834      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20835      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20836      */
20837     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20838     /**
20839      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20840      */
20841     fieldClass : "x-form-field",
20842     /**
20843      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20844      *<pre>
20845 Value         Description
20846 -----------   ----------------------------------------------------------------------
20847 qtip          Display a quick tip when the user hovers over the field
20848 title         Display a default browser title attribute popup
20849 under         Add a block div beneath the field containing the error text
20850 side          Add an error icon to the right of the field with a popup on hover
20851 [element id]  Add the error text directly to the innerHTML of the specified element
20852 </pre>
20853      */
20854     msgTarget : 'qtip',
20855     /**
20856      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20857      */
20858     msgFx : 'normal',
20859
20860     /**
20861      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20862      */
20863     readOnly : false,
20864
20865     /**
20866      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20867      */
20868     disabled : false,
20869
20870     /**
20871      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20872      */
20873     inputType : undefined,
20874     
20875     /**
20876      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20877          */
20878         tabIndex : undefined,
20879         
20880     // private
20881     isFormField : true,
20882
20883     // private
20884     hasFocus : false,
20885     /**
20886      * @property {Roo.Element} fieldEl
20887      * Element Containing the rendered Field (with label etc.)
20888      */
20889     /**
20890      * @cfg {Mixed} value A value to initialize this field with.
20891      */
20892     value : undefined,
20893
20894     /**
20895      * @cfg {String} name The field's HTML name attribute.
20896      */
20897     /**
20898      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20899      */
20900
20901         // private ??
20902         initComponent : function(){
20903         Roo.form.Field.superclass.initComponent.call(this);
20904         this.addEvents({
20905             /**
20906              * @event focus
20907              * Fires when this field receives input focus.
20908              * @param {Roo.form.Field} this
20909              */
20910             focus : true,
20911             /**
20912              * @event blur
20913              * Fires when this field loses input focus.
20914              * @param {Roo.form.Field} this
20915              */
20916             blur : true,
20917             /**
20918              * @event specialkey
20919              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20920              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20921              * @param {Roo.form.Field} this
20922              * @param {Roo.EventObject} e The event object
20923              */
20924             specialkey : true,
20925             /**
20926              * @event change
20927              * Fires just before the field blurs if the field value has changed.
20928              * @param {Roo.form.Field} this
20929              * @param {Mixed} newValue The new value
20930              * @param {Mixed} oldValue The original value
20931              */
20932             change : true,
20933             /**
20934              * @event invalid
20935              * Fires after the field has been marked as invalid.
20936              * @param {Roo.form.Field} this
20937              * @param {String} msg The validation message
20938              */
20939             invalid : true,
20940             /**
20941              * @event valid
20942              * Fires after the field has been validated with no errors.
20943              * @param {Roo.form.Field} this
20944              */
20945             valid : true,
20946              /**
20947              * @event keyup
20948              * Fires after the key up
20949              * @param {Roo.form.Field} this
20950              * @param {Roo.EventObject}  e The event Object
20951              */
20952             keyup : true
20953         });
20954     },
20955
20956     /**
20957      * Returns the name attribute of the field if available
20958      * @return {String} name The field name
20959      */
20960     getName: function(){
20961          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20962     },
20963
20964     // private
20965     onRender : function(ct, position){
20966         Roo.form.Field.superclass.onRender.call(this, ct, position);
20967         if(!this.el){
20968             var cfg = this.getAutoCreate();
20969             if(!cfg.name){
20970                 cfg.name = this.name || this.id;
20971             }
20972             if(this.inputType){
20973                 cfg.type = this.inputType;
20974             }
20975             this.el = ct.createChild(cfg, position);
20976         }
20977         var type = this.el.dom.type;
20978         if(type){
20979             if(type == 'password'){
20980                 type = 'text';
20981             }
20982             this.el.addClass('x-form-'+type);
20983         }
20984         if(this.readOnly){
20985             this.el.dom.readOnly = true;
20986         }
20987         if(this.tabIndex !== undefined){
20988             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20989         }
20990
20991         this.el.addClass([this.fieldClass, this.cls]);
20992         this.initValue();
20993     },
20994
20995     /**
20996      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20997      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20998      * @return {Roo.form.Field} this
20999      */
21000     applyTo : function(target){
21001         this.allowDomMove = false;
21002         this.el = Roo.get(target);
21003         this.render(this.el.dom.parentNode);
21004         return this;
21005     },
21006
21007     // private
21008     initValue : function(){
21009         if(this.value !== undefined){
21010             this.setValue(this.value);
21011         }else if(this.el.dom.value.length > 0){
21012             this.setValue(this.el.dom.value);
21013         }
21014     },
21015
21016     /**
21017      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21018      */
21019     isDirty : function() {
21020         if(this.disabled) {
21021             return false;
21022         }
21023         return String(this.getValue()) !== String(this.originalValue);
21024     },
21025
21026     // private
21027     afterRender : function(){
21028         Roo.form.Field.superclass.afterRender.call(this);
21029         this.initEvents();
21030     },
21031
21032     // private
21033     fireKey : function(e){
21034         //Roo.log('field ' + e.getKey());
21035         if(e.isNavKeyPress()){
21036             this.fireEvent("specialkey", this, e);
21037         }
21038     },
21039
21040     /**
21041      * Resets the current field value to the originally loaded value and clears any validation messages
21042      */
21043     reset : function(){
21044         this.setValue(this.originalValue);
21045         this.clearInvalid();
21046     },
21047
21048     // private
21049     initEvents : function(){
21050         // safari killled keypress - so keydown is now used..
21051         this.el.on("keydown" , this.fireKey,  this);
21052         this.el.on("focus", this.onFocus,  this);
21053         this.el.on("blur", this.onBlur,  this);
21054         this.el.relayEvent('keyup', this);
21055
21056         // reference to original value for reset
21057         this.originalValue = this.getValue();
21058     },
21059
21060     // private
21061     onFocus : function(){
21062         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21063             this.el.addClass(this.focusClass);
21064         }
21065         if(!this.hasFocus){
21066             this.hasFocus = true;
21067             this.startValue = this.getValue();
21068             this.fireEvent("focus", this);
21069         }
21070     },
21071
21072     beforeBlur : Roo.emptyFn,
21073
21074     // private
21075     onBlur : function(){
21076         this.beforeBlur();
21077         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21078             this.el.removeClass(this.focusClass);
21079         }
21080         this.hasFocus = false;
21081         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21082             this.validate();
21083         }
21084         var v = this.getValue();
21085         if(String(v) !== String(this.startValue)){
21086             this.fireEvent('change', this, v, this.startValue);
21087         }
21088         this.fireEvent("blur", this);
21089     },
21090
21091     /**
21092      * Returns whether or not the field value is currently valid
21093      * @param {Boolean} preventMark True to disable marking the field invalid
21094      * @return {Boolean} True if the value is valid, else false
21095      */
21096     isValid : function(preventMark){
21097         if(this.disabled){
21098             return true;
21099         }
21100         var restore = this.preventMark;
21101         this.preventMark = preventMark === true;
21102         var v = this.validateValue(this.processValue(this.getRawValue()));
21103         this.preventMark = restore;
21104         return v;
21105     },
21106
21107     /**
21108      * Validates the field value
21109      * @return {Boolean} True if the value is valid, else false
21110      */
21111     validate : function(){
21112         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21113             this.clearInvalid();
21114             return true;
21115         }
21116         return false;
21117     },
21118
21119     processValue : function(value){
21120         return value;
21121     },
21122
21123     // private
21124     // Subclasses should provide the validation implementation by overriding this
21125     validateValue : function(value){
21126         return true;
21127     },
21128
21129     /**
21130      * Mark this field as invalid
21131      * @param {String} msg The validation message
21132      */
21133     markInvalid : function(msg){
21134         if(!this.rendered || this.preventMark){ // not rendered
21135             return;
21136         }
21137         this.el.addClass(this.invalidClass);
21138         msg = msg || this.invalidText;
21139         switch(this.msgTarget){
21140             case 'qtip':
21141                 this.el.dom.qtip = msg;
21142                 this.el.dom.qclass = 'x-form-invalid-tip';
21143                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21144                     Roo.QuickTips.enable();
21145                 }
21146                 break;
21147             case 'title':
21148                 this.el.dom.title = msg;
21149                 break;
21150             case 'under':
21151                 if(!this.errorEl){
21152                     var elp = this.el.findParent('.x-form-element', 5, true);
21153                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21154                     this.errorEl.setWidth(elp.getWidth(true)-20);
21155                 }
21156                 this.errorEl.update(msg);
21157                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21158                 break;
21159             case 'side':
21160                 if(!this.errorIcon){
21161                     var elp = this.el.findParent('.x-form-element', 5, true);
21162                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21163                 }
21164                 this.alignErrorIcon();
21165                 this.errorIcon.dom.qtip = msg;
21166                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21167                 this.errorIcon.show();
21168                 this.on('resize', this.alignErrorIcon, this);
21169                 break;
21170             default:
21171                 var t = Roo.getDom(this.msgTarget);
21172                 t.innerHTML = msg;
21173                 t.style.display = this.msgDisplay;
21174                 break;
21175         }
21176         this.fireEvent('invalid', this, msg);
21177     },
21178
21179     // private
21180     alignErrorIcon : function(){
21181         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21182     },
21183
21184     /**
21185      * Clear any invalid styles/messages for this field
21186      */
21187     clearInvalid : function(){
21188         if(!this.rendered || this.preventMark){ // not rendered
21189             return;
21190         }
21191         this.el.removeClass(this.invalidClass);
21192         switch(this.msgTarget){
21193             case 'qtip':
21194                 this.el.dom.qtip = '';
21195                 break;
21196             case 'title':
21197                 this.el.dom.title = '';
21198                 break;
21199             case 'under':
21200                 if(this.errorEl){
21201                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21202                 }
21203                 break;
21204             case 'side':
21205                 if(this.errorIcon){
21206                     this.errorIcon.dom.qtip = '';
21207                     this.errorIcon.hide();
21208                     this.un('resize', this.alignErrorIcon, this);
21209                 }
21210                 break;
21211             default:
21212                 var t = Roo.getDom(this.msgTarget);
21213                 t.innerHTML = '';
21214                 t.style.display = 'none';
21215                 break;
21216         }
21217         this.fireEvent('valid', this);
21218     },
21219
21220     /**
21221      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21222      * @return {Mixed} value The field value
21223      */
21224     getRawValue : function(){
21225         var v = this.el.getValue();
21226         if(v === this.emptyText){
21227             v = '';
21228         }
21229         return v;
21230     },
21231
21232     /**
21233      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21234      * @return {Mixed} value The field value
21235      */
21236     getValue : function(){
21237         var v = this.el.getValue();
21238         if(v === this.emptyText || v === undefined){
21239             v = '';
21240         }
21241         return v;
21242     },
21243
21244     /**
21245      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21246      * @param {Mixed} value The value to set
21247      */
21248     setRawValue : function(v){
21249         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21250     },
21251
21252     /**
21253      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21254      * @param {Mixed} value The value to set
21255      */
21256     setValue : function(v){
21257         this.value = v;
21258         if(this.rendered){
21259             this.el.dom.value = (v === null || v === undefined ? '' : v);
21260             this.validate();
21261         }
21262     },
21263
21264     adjustSize : function(w, h){
21265         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21266         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21267         return s;
21268     },
21269
21270     adjustWidth : function(tag, w){
21271         tag = tag.toLowerCase();
21272         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21273             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21274                 if(tag == 'input'){
21275                     return w + 2;
21276                 }
21277                 if(tag = 'textarea'){
21278                     return w-2;
21279                 }
21280             }else if(Roo.isOpera){
21281                 if(tag == 'input'){
21282                     return w + 2;
21283                 }
21284                 if(tag = 'textarea'){
21285                     return w-2;
21286                 }
21287             }
21288         }
21289         return w;
21290     }
21291 });
21292
21293
21294 // anything other than normal should be considered experimental
21295 Roo.form.Field.msgFx = {
21296     normal : {
21297         show: function(msgEl, f){
21298             msgEl.setDisplayed('block');
21299         },
21300
21301         hide : function(msgEl, f){
21302             msgEl.setDisplayed(false).update('');
21303         }
21304     },
21305
21306     slide : {
21307         show: function(msgEl, f){
21308             msgEl.slideIn('t', {stopFx:true});
21309         },
21310
21311         hide : function(msgEl, f){
21312             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21313         }
21314     },
21315
21316     slideRight : {
21317         show: function(msgEl, f){
21318             msgEl.fixDisplay();
21319             msgEl.alignTo(f.el, 'tl-tr');
21320             msgEl.slideIn('l', {stopFx:true});
21321         },
21322
21323         hide : function(msgEl, f){
21324             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21325         }
21326     }
21327 };/*
21328  * Based on:
21329  * Ext JS Library 1.1.1
21330  * Copyright(c) 2006-2007, Ext JS, LLC.
21331  *
21332  * Originally Released Under LGPL - original licence link has changed is not relivant.
21333  *
21334  * Fork - LGPL
21335  * <script type="text/javascript">
21336  */
21337  
21338
21339 /**
21340  * @class Roo.form.TextField
21341  * @extends Roo.form.Field
21342  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21343  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21344  * @constructor
21345  * Creates a new TextField
21346  * @param {Object} config Configuration options
21347  */
21348 Roo.form.TextField = function(config){
21349     Roo.form.TextField.superclass.constructor.call(this, config);
21350     this.addEvents({
21351         /**
21352          * @event autosize
21353          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21354          * according to the default logic, but this event provides a hook for the developer to apply additional
21355          * logic at runtime to resize the field if needed.
21356              * @param {Roo.form.Field} this This text field
21357              * @param {Number} width The new field width
21358              */
21359         autosize : true
21360     });
21361 };
21362
21363 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21364     /**
21365      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21366      */
21367     grow : false,
21368     /**
21369      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21370      */
21371     growMin : 30,
21372     /**
21373      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21374      */
21375     growMax : 800,
21376     /**
21377      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21378      */
21379     vtype : null,
21380     /**
21381      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21382      */
21383     maskRe : null,
21384     /**
21385      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21386      */
21387     disableKeyFilter : false,
21388     /**
21389      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21390      */
21391     allowBlank : true,
21392     /**
21393      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21394      */
21395     minLength : 0,
21396     /**
21397      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21398      */
21399     maxLength : Number.MAX_VALUE,
21400     /**
21401      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21402      */
21403     minLengthText : "The minimum length for this field is {0}",
21404     /**
21405      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21406      */
21407     maxLengthText : "The maximum length for this field is {0}",
21408     /**
21409      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21410      */
21411     selectOnFocus : false,
21412     /**
21413      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21414      */
21415     blankText : "This field is required",
21416     /**
21417      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21418      * If available, this function will be called only after the basic validators all return true, and will be passed the
21419      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21420      */
21421     validator : null,
21422     /**
21423      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21424      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21425      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21426      */
21427     regex : null,
21428     /**
21429      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21430      */
21431     regexText : "",
21432     /**
21433      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21434      */
21435     emptyText : null,
21436     /**
21437      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21438      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21439      */
21440     emptyClass : 'x-form-empty-field',
21441
21442     // private
21443     initEvents : function(){
21444         Roo.form.TextField.superclass.initEvents.call(this);
21445         if(this.validationEvent == 'keyup'){
21446             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21447             this.el.on('keyup', this.filterValidation, this);
21448         }
21449         else if(this.validationEvent !== false){
21450             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21451         }
21452         if(this.selectOnFocus || this.emptyText){
21453             this.on("focus", this.preFocus, this);
21454             if(this.emptyText){
21455                 this.on('blur', this.postBlur, this);
21456                 this.applyEmptyText();
21457             }
21458         }
21459         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21460             this.el.on("keypress", this.filterKeys, this);
21461         }
21462         if(this.grow){
21463             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21464             this.el.on("click", this.autoSize,  this);
21465         }
21466     },
21467
21468     processValue : function(value){
21469         if(this.stripCharsRe){
21470             var newValue = value.replace(this.stripCharsRe, '');
21471             if(newValue !== value){
21472                 this.setRawValue(newValue);
21473                 return newValue;
21474             }
21475         }
21476         return value;
21477     },
21478
21479     filterValidation : function(e){
21480         if(!e.isNavKeyPress()){
21481             this.validationTask.delay(this.validationDelay);
21482         }
21483     },
21484
21485     // private
21486     onKeyUp : function(e){
21487         if(!e.isNavKeyPress()){
21488             this.autoSize();
21489         }
21490     },
21491
21492     /**
21493      * Resets the current field value to the originally-loaded value and clears any validation messages.
21494      * Also adds emptyText and emptyClass if the original value was blank.
21495      */
21496     reset : function(){
21497         Roo.form.TextField.superclass.reset.call(this);
21498         this.applyEmptyText();
21499     },
21500
21501     applyEmptyText : function(){
21502         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21503             this.setRawValue(this.emptyText);
21504             this.el.addClass(this.emptyClass);
21505         }
21506     },
21507
21508     // private
21509     preFocus : function(){
21510         if(this.emptyText){
21511             if(this.el.dom.value == this.emptyText){
21512                 this.setRawValue('');
21513             }
21514             this.el.removeClass(this.emptyClass);
21515         }
21516         if(this.selectOnFocus){
21517             this.el.dom.select();
21518         }
21519     },
21520
21521     // private
21522     postBlur : function(){
21523         this.applyEmptyText();
21524     },
21525
21526     // private
21527     filterKeys : function(e){
21528         var k = e.getKey();
21529         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21530             return;
21531         }
21532         var c = e.getCharCode(), cc = String.fromCharCode(c);
21533         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21534             return;
21535         }
21536         if(!this.maskRe.test(cc)){
21537             e.stopEvent();
21538         }
21539     },
21540
21541     setValue : function(v){
21542         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21543             this.el.removeClass(this.emptyClass);
21544         }
21545         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21546         this.applyEmptyText();
21547         this.autoSize();
21548     },
21549
21550     /**
21551      * Validates a value according to the field's validation rules and marks the field as invalid
21552      * if the validation fails
21553      * @param {Mixed} value The value to validate
21554      * @return {Boolean} True if the value is valid, else false
21555      */
21556     validateValue : function(value){
21557         if(value.length < 1 || value === this.emptyText){ // if it's blank
21558              if(this.allowBlank){
21559                 this.clearInvalid();
21560                 return true;
21561              }else{
21562                 this.markInvalid(this.blankText);
21563                 return false;
21564              }
21565         }
21566         if(value.length < this.minLength){
21567             this.markInvalid(String.format(this.minLengthText, this.minLength));
21568             return false;
21569         }
21570         if(value.length > this.maxLength){
21571             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21572             return false;
21573         }
21574         if(this.vtype){
21575             var vt = Roo.form.VTypes;
21576             if(!vt[this.vtype](value, this)){
21577                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21578                 return false;
21579             }
21580         }
21581         if(typeof this.validator == "function"){
21582             var msg = this.validator(value);
21583             if(msg !== true){
21584                 this.markInvalid(msg);
21585                 return false;
21586             }
21587         }
21588         if(this.regex && !this.regex.test(value)){
21589             this.markInvalid(this.regexText);
21590             return false;
21591         }
21592         return true;
21593     },
21594
21595     /**
21596      * Selects text in this field
21597      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21598      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21599      */
21600     selectText : function(start, end){
21601         var v = this.getRawValue();
21602         if(v.length > 0){
21603             start = start === undefined ? 0 : start;
21604             end = end === undefined ? v.length : end;
21605             var d = this.el.dom;
21606             if(d.setSelectionRange){
21607                 d.setSelectionRange(start, end);
21608             }else if(d.createTextRange){
21609                 var range = d.createTextRange();
21610                 range.moveStart("character", start);
21611                 range.moveEnd("character", v.length-end);
21612                 range.select();
21613             }
21614         }
21615     },
21616
21617     /**
21618      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21619      * This only takes effect if grow = true, and fires the autosize event.
21620      */
21621     autoSize : function(){
21622         if(!this.grow || !this.rendered){
21623             return;
21624         }
21625         if(!this.metrics){
21626             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21627         }
21628         var el = this.el;
21629         var v = el.dom.value;
21630         var d = document.createElement('div');
21631         d.appendChild(document.createTextNode(v));
21632         v = d.innerHTML;
21633         d = null;
21634         v += "&#160;";
21635         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21636         this.el.setWidth(w);
21637         this.fireEvent("autosize", this, w);
21638     }
21639 });/*
21640  * Based on:
21641  * Ext JS Library 1.1.1
21642  * Copyright(c) 2006-2007, Ext JS, LLC.
21643  *
21644  * Originally Released Under LGPL - original licence link has changed is not relivant.
21645  *
21646  * Fork - LGPL
21647  * <script type="text/javascript">
21648  */
21649  
21650 /**
21651  * @class Roo.form.Hidden
21652  * @extends Roo.form.TextField
21653  * Simple Hidden element used on forms 
21654  * 
21655  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21656  * 
21657  * @constructor
21658  * Creates a new Hidden form element.
21659  * @param {Object} config Configuration options
21660  */
21661
21662
21663
21664 // easy hidden field...
21665 Roo.form.Hidden = function(config){
21666     Roo.form.Hidden.superclass.constructor.call(this, config);
21667 };
21668   
21669 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21670     fieldLabel:      '',
21671     inputType:      'hidden',
21672     width:          50,
21673     allowBlank:     true,
21674     labelSeparator: '',
21675     hidden:         true,
21676     itemCls :       'x-form-item-display-none'
21677
21678
21679 });
21680
21681
21682 /*
21683  * Based on:
21684  * Ext JS Library 1.1.1
21685  * Copyright(c) 2006-2007, Ext JS, LLC.
21686  *
21687  * Originally Released Under LGPL - original licence link has changed is not relivant.
21688  *
21689  * Fork - LGPL
21690  * <script type="text/javascript">
21691  */
21692  
21693 /**
21694  * @class Roo.form.TriggerField
21695  * @extends Roo.form.TextField
21696  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21697  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21698  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21699  * for which you can provide a custom implementation.  For example:
21700  * <pre><code>
21701 var trigger = new Roo.form.TriggerField();
21702 trigger.onTriggerClick = myTriggerFn;
21703 trigger.applyTo('my-field');
21704 </code></pre>
21705  *
21706  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21707  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21708  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21709  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21710  * @constructor
21711  * Create a new TriggerField.
21712  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21713  * to the base TextField)
21714  */
21715 Roo.form.TriggerField = function(config){
21716     this.mimicing = false;
21717     Roo.form.TriggerField.superclass.constructor.call(this, config);
21718 };
21719
21720 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21721     /**
21722      * @cfg {String} triggerClass A CSS class to apply to the trigger
21723      */
21724     /**
21725      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21726      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21727      */
21728     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21729     /**
21730      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21731      */
21732     hideTrigger:false,
21733
21734     /** @cfg {Boolean} grow @hide */
21735     /** @cfg {Number} growMin @hide */
21736     /** @cfg {Number} growMax @hide */
21737
21738     /**
21739      * @hide 
21740      * @method
21741      */
21742     autoSize: Roo.emptyFn,
21743     // private
21744     monitorTab : true,
21745     // private
21746     deferHeight : true,
21747
21748     
21749     actionMode : 'wrap',
21750     // private
21751     onResize : function(w, h){
21752         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21753         if(typeof w == 'number'){
21754             var x = w - this.trigger.getWidth();
21755             this.el.setWidth(this.adjustWidth('input', x));
21756             this.trigger.setStyle('left', x+'px');
21757         }
21758     },
21759
21760     // private
21761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21762
21763     // private
21764     getResizeEl : function(){
21765         return this.wrap;
21766     },
21767
21768     // private
21769     getPositionEl : function(){
21770         return this.wrap;
21771     },
21772
21773     // private
21774     alignErrorIcon : function(){
21775         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21776     },
21777
21778     // private
21779     onRender : function(ct, position){
21780         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21781         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21782         this.trigger = this.wrap.createChild(this.triggerConfig ||
21783                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21784         if(this.hideTrigger){
21785             this.trigger.setDisplayed(false);
21786         }
21787         this.initTrigger();
21788         if(!this.width){
21789             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21790         }
21791     },
21792
21793     // private
21794     initTrigger : function(){
21795         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21796         this.trigger.addClassOnOver('x-form-trigger-over');
21797         this.trigger.addClassOnClick('x-form-trigger-click');
21798     },
21799
21800     // private
21801     onDestroy : function(){
21802         if(this.trigger){
21803             this.trigger.removeAllListeners();
21804             this.trigger.remove();
21805         }
21806         if(this.wrap){
21807             this.wrap.remove();
21808         }
21809         Roo.form.TriggerField.superclass.onDestroy.call(this);
21810     },
21811
21812     // private
21813     onFocus : function(){
21814         Roo.form.TriggerField.superclass.onFocus.call(this);
21815         if(!this.mimicing){
21816             this.wrap.addClass('x-trigger-wrap-focus');
21817             this.mimicing = true;
21818             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21819             if(this.monitorTab){
21820                 this.el.on("keydown", this.checkTab, this);
21821             }
21822         }
21823     },
21824
21825     // private
21826     checkTab : function(e){
21827         if(e.getKey() == e.TAB){
21828             this.triggerBlur();
21829         }
21830     },
21831
21832     // private
21833     onBlur : function(){
21834         // do nothing
21835     },
21836
21837     // private
21838     mimicBlur : function(e, t){
21839         if(!this.wrap.contains(t) && this.validateBlur()){
21840             this.triggerBlur();
21841         }
21842     },
21843
21844     // private
21845     triggerBlur : function(){
21846         this.mimicing = false;
21847         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21848         if(this.monitorTab){
21849             this.el.un("keydown", this.checkTab, this);
21850         }
21851         this.wrap.removeClass('x-trigger-wrap-focus');
21852         Roo.form.TriggerField.superclass.onBlur.call(this);
21853     },
21854
21855     // private
21856     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21857     validateBlur : function(e, t){
21858         return true;
21859     },
21860
21861     // private
21862     onDisable : function(){
21863         Roo.form.TriggerField.superclass.onDisable.call(this);
21864         if(this.wrap){
21865             this.wrap.addClass('x-item-disabled');
21866         }
21867     },
21868
21869     // private
21870     onEnable : function(){
21871         Roo.form.TriggerField.superclass.onEnable.call(this);
21872         if(this.wrap){
21873             this.wrap.removeClass('x-item-disabled');
21874         }
21875     },
21876
21877     // private
21878     onShow : function(){
21879         var ae = this.getActionEl();
21880         
21881         if(ae){
21882             ae.dom.style.display = '';
21883             ae.dom.style.visibility = 'visible';
21884         }
21885     },
21886
21887     // private
21888     
21889     onHide : function(){
21890         var ae = this.getActionEl();
21891         ae.dom.style.display = 'none';
21892     },
21893
21894     /**
21895      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21896      * by an implementing function.
21897      * @method
21898      * @param {EventObject} e
21899      */
21900     onTriggerClick : Roo.emptyFn
21901 });
21902
21903 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21904 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21905 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21906 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21907     initComponent : function(){
21908         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21909
21910         this.triggerConfig = {
21911             tag:'span', cls:'x-form-twin-triggers', cn:[
21912             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21913             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21914         ]};
21915     },
21916
21917     getTrigger : function(index){
21918         return this.triggers[index];
21919     },
21920
21921     initTrigger : function(){
21922         var ts = this.trigger.select('.x-form-trigger', true);
21923         this.wrap.setStyle('overflow', 'hidden');
21924         var triggerField = this;
21925         ts.each(function(t, all, index){
21926             t.hide = function(){
21927                 var w = triggerField.wrap.getWidth();
21928                 this.dom.style.display = 'none';
21929                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21930             };
21931             t.show = function(){
21932                 var w = triggerField.wrap.getWidth();
21933                 this.dom.style.display = '';
21934                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21935             };
21936             var triggerIndex = 'Trigger'+(index+1);
21937
21938             if(this['hide'+triggerIndex]){
21939                 t.dom.style.display = 'none';
21940             }
21941             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21942             t.addClassOnOver('x-form-trigger-over');
21943             t.addClassOnClick('x-form-trigger-click');
21944         }, this);
21945         this.triggers = ts.elements;
21946     },
21947
21948     onTrigger1Click : Roo.emptyFn,
21949     onTrigger2Click : Roo.emptyFn
21950 });/*
21951  * Based on:
21952  * Ext JS Library 1.1.1
21953  * Copyright(c) 2006-2007, Ext JS, LLC.
21954  *
21955  * Originally Released Under LGPL - original licence link has changed is not relivant.
21956  *
21957  * Fork - LGPL
21958  * <script type="text/javascript">
21959  */
21960  
21961 /**
21962  * @class Roo.form.TextArea
21963  * @extends Roo.form.TextField
21964  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21965  * support for auto-sizing.
21966  * @constructor
21967  * Creates a new TextArea
21968  * @param {Object} config Configuration options
21969  */
21970 Roo.form.TextArea = function(config){
21971     Roo.form.TextArea.superclass.constructor.call(this, config);
21972     // these are provided exchanges for backwards compat
21973     // minHeight/maxHeight were replaced by growMin/growMax to be
21974     // compatible with TextField growing config values
21975     if(this.minHeight !== undefined){
21976         this.growMin = this.minHeight;
21977     }
21978     if(this.maxHeight !== undefined){
21979         this.growMax = this.maxHeight;
21980     }
21981 };
21982
21983 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21984     /**
21985      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21986      */
21987     growMin : 60,
21988     /**
21989      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21990      */
21991     growMax: 1000,
21992     /**
21993      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21994      * in the field (equivalent to setting overflow: hidden, defaults to false)
21995      */
21996     preventScrollbars: false,
21997     /**
21998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21999      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22000      */
22001
22002     // private
22003     onRender : function(ct, position){
22004         if(!this.el){
22005             this.defaultAutoCreate = {
22006                 tag: "textarea",
22007                 style:"width:300px;height:60px;",
22008                 autocomplete: "off"
22009             };
22010         }
22011         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22012         if(this.grow){
22013             this.textSizeEl = Roo.DomHelper.append(document.body, {
22014                 tag: "pre", cls: "x-form-grow-sizer"
22015             });
22016             if(this.preventScrollbars){
22017                 this.el.setStyle("overflow", "hidden");
22018             }
22019             this.el.setHeight(this.growMin);
22020         }
22021     },
22022
22023     onDestroy : function(){
22024         if(this.textSizeEl){
22025             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22026         }
22027         Roo.form.TextArea.superclass.onDestroy.call(this);
22028     },
22029
22030     // private
22031     onKeyUp : function(e){
22032         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22033             this.autoSize();
22034         }
22035     },
22036
22037     /**
22038      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22039      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22040      */
22041     autoSize : function(){
22042         if(!this.grow || !this.textSizeEl){
22043             return;
22044         }
22045         var el = this.el;
22046         var v = el.dom.value;
22047         var ts = this.textSizeEl;
22048
22049         ts.innerHTML = '';
22050         ts.appendChild(document.createTextNode(v));
22051         v = ts.innerHTML;
22052
22053         Roo.fly(ts).setWidth(this.el.getWidth());
22054         if(v.length < 1){
22055             v = "&#160;&#160;";
22056         }else{
22057             if(Roo.isIE){
22058                 v = v.replace(/\n/g, '<p>&#160;</p>');
22059             }
22060             v += "&#160;\n&#160;";
22061         }
22062         ts.innerHTML = v;
22063         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22064         if(h != this.lastHeight){
22065             this.lastHeight = h;
22066             this.el.setHeight(h);
22067             this.fireEvent("autosize", this, h);
22068         }
22069     }
22070 });/*
22071  * Based on:
22072  * Ext JS Library 1.1.1
22073  * Copyright(c) 2006-2007, Ext JS, LLC.
22074  *
22075  * Originally Released Under LGPL - original licence link has changed is not relivant.
22076  *
22077  * Fork - LGPL
22078  * <script type="text/javascript">
22079  */
22080  
22081
22082 /**
22083  * @class Roo.form.NumberField
22084  * @extends Roo.form.TextField
22085  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22086  * @constructor
22087  * Creates a new NumberField
22088  * @param {Object} config Configuration options
22089  */
22090 Roo.form.NumberField = function(config){
22091     Roo.form.NumberField.superclass.constructor.call(this, config);
22092 };
22093
22094 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22095     /**
22096      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22097      */
22098     fieldClass: "x-form-field x-form-num-field",
22099     /**
22100      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22101      */
22102     allowDecimals : true,
22103     /**
22104      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22105      */
22106     decimalSeparator : ".",
22107     /**
22108      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22109      */
22110     decimalPrecision : 2,
22111     /**
22112      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22113      */
22114     allowNegative : true,
22115     /**
22116      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22117      */
22118     minValue : Number.NEGATIVE_INFINITY,
22119     /**
22120      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22121      */
22122     maxValue : Number.MAX_VALUE,
22123     /**
22124      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22125      */
22126     minText : "The minimum value for this field is {0}",
22127     /**
22128      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22129      */
22130     maxText : "The maximum value for this field is {0}",
22131     /**
22132      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22133      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22134      */
22135     nanText : "{0} is not a valid number",
22136
22137     // private
22138     initEvents : function(){
22139         Roo.form.NumberField.superclass.initEvents.call(this);
22140         var allowed = "0123456789";
22141         if(this.allowDecimals){
22142             allowed += this.decimalSeparator;
22143         }
22144         if(this.allowNegative){
22145             allowed += "-";
22146         }
22147         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22148         var keyPress = function(e){
22149             var k = e.getKey();
22150             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22151                 return;
22152             }
22153             var c = e.getCharCode();
22154             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22155                 e.stopEvent();
22156             }
22157         };
22158         this.el.on("keypress", keyPress, this);
22159     },
22160
22161     // private
22162     validateValue : function(value){
22163         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22164             return false;
22165         }
22166         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22167              return true;
22168         }
22169         var num = this.parseValue(value);
22170         if(isNaN(num)){
22171             this.markInvalid(String.format(this.nanText, value));
22172             return false;
22173         }
22174         if(num < this.minValue){
22175             this.markInvalid(String.format(this.minText, this.minValue));
22176             return false;
22177         }
22178         if(num > this.maxValue){
22179             this.markInvalid(String.format(this.maxText, this.maxValue));
22180             return false;
22181         }
22182         return true;
22183     },
22184
22185     getValue : function(){
22186         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22187     },
22188
22189     // private
22190     parseValue : function(value){
22191         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22192         return isNaN(value) ? '' : value;
22193     },
22194
22195     // private
22196     fixPrecision : function(value){
22197         var nan = isNaN(value);
22198         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22199             return nan ? '' : value;
22200         }
22201         return parseFloat(value).toFixed(this.decimalPrecision);
22202     },
22203
22204     setValue : function(v){
22205         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22206     },
22207
22208     // private
22209     decimalPrecisionFcn : function(v){
22210         return Math.floor(v);
22211     },
22212
22213     beforeBlur : function(){
22214         var v = this.parseValue(this.getRawValue());
22215         if(v){
22216             this.setValue(this.fixPrecision(v));
22217         }
22218     }
22219 });/*
22220  * Based on:
22221  * Ext JS Library 1.1.1
22222  * Copyright(c) 2006-2007, Ext JS, LLC.
22223  *
22224  * Originally Released Under LGPL - original licence link has changed is not relivant.
22225  *
22226  * Fork - LGPL
22227  * <script type="text/javascript">
22228  */
22229  
22230 /**
22231  * @class Roo.form.DateField
22232  * @extends Roo.form.TriggerField
22233  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22234 * @constructor
22235 * Create a new DateField
22236 * @param {Object} config
22237  */
22238 Roo.form.DateField = function(config){
22239     Roo.form.DateField.superclass.constructor.call(this, config);
22240     
22241       this.addEvents({
22242          
22243         /**
22244          * @event select
22245          * Fires when a date is selected
22246              * @param {Roo.form.DateField} combo This combo box
22247              * @param {Date} date The date selected
22248              */
22249         'select' : true
22250          
22251     });
22252     
22253     
22254     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22255     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22256     this.ddMatch = null;
22257     if(this.disabledDates){
22258         var dd = this.disabledDates;
22259         var re = "(?:";
22260         for(var i = 0; i < dd.length; i++){
22261             re += dd[i];
22262             if(i != dd.length-1) re += "|";
22263         }
22264         this.ddMatch = new RegExp(re + ")");
22265     }
22266 };
22267
22268 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22269     /**
22270      * @cfg {String} format
22271      * The default date format string which can be overriden for localization support.  The format must be
22272      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22273      */
22274     format : "m/d/y",
22275     /**
22276      * @cfg {String} altFormats
22277      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22278      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22279      */
22280     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22281     /**
22282      * @cfg {Array} disabledDays
22283      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22284      */
22285     disabledDays : null,
22286     /**
22287      * @cfg {String} disabledDaysText
22288      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22289      */
22290     disabledDaysText : "Disabled",
22291     /**
22292      * @cfg {Array} disabledDates
22293      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22294      * expression so they are very powerful. Some examples:
22295      * <ul>
22296      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22297      * <li>["03/08", "09/16"] would disable those days for every year</li>
22298      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22299      * <li>["03/../2006"] would disable every day in March 2006</li>
22300      * <li>["^03"] would disable every day in every March</li>
22301      * </ul>
22302      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22303      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22304      */
22305     disabledDates : null,
22306     /**
22307      * @cfg {String} disabledDatesText
22308      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22309      */
22310     disabledDatesText : "Disabled",
22311     /**
22312      * @cfg {Date/String} minValue
22313      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22314      * valid format (defaults to null).
22315      */
22316     minValue : null,
22317     /**
22318      * @cfg {Date/String} maxValue
22319      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22320      * valid format (defaults to null).
22321      */
22322     maxValue : null,
22323     /**
22324      * @cfg {String} minText
22325      * The error text to display when the date in the cell is before minValue (defaults to
22326      * 'The date in this field must be after {minValue}').
22327      */
22328     minText : "The date in this field must be equal to or after {0}",
22329     /**
22330      * @cfg {String} maxText
22331      * The error text to display when the date in the cell is after maxValue (defaults to
22332      * 'The date in this field must be before {maxValue}').
22333      */
22334     maxText : "The date in this field must be equal to or before {0}",
22335     /**
22336      * @cfg {String} invalidText
22337      * The error text to display when the date in the field is invalid (defaults to
22338      * '{value} is not a valid date - it must be in the format {format}').
22339      */
22340     invalidText : "{0} is not a valid date - it must be in the format {1}",
22341     /**
22342      * @cfg {String} triggerClass
22343      * An additional CSS class used to style the trigger button.  The trigger will always get the
22344      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22345      * which displays a calendar icon).
22346      */
22347     triggerClass : 'x-form-date-trigger',
22348     
22349
22350     /**
22351      * @cfg {bool} useIso
22352      * if enabled, then the date field will use a hidden field to store the 
22353      * real value as iso formated date. default (false)
22354      */ 
22355     useIso : false,
22356     /**
22357      * @cfg {String/Object} autoCreate
22358      * A DomHelper element spec, or true for a default element spec (defaults to
22359      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22360      */ 
22361     // private
22362     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22363     
22364     // private
22365     hiddenField: false,
22366     
22367     onRender : function(ct, position)
22368     {
22369         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22370         if (this.useIso) {
22371             this.el.dom.removeAttribute('name'); 
22372             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22373                     'before', true);
22374             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22375             // prevent input submission
22376             this.hiddenName = this.name;
22377         }
22378             
22379             
22380     },
22381     
22382     // private
22383     validateValue : function(value)
22384     {
22385         value = this.formatDate(value);
22386         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22387             return false;
22388         }
22389         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22390              return true;
22391         }
22392         var svalue = value;
22393         value = this.parseDate(value);
22394         if(!value){
22395             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22396             return false;
22397         }
22398         var time = value.getTime();
22399         if(this.minValue && time < this.minValue.getTime()){
22400             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22401             return false;
22402         }
22403         if(this.maxValue && time > this.maxValue.getTime()){
22404             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22405             return false;
22406         }
22407         if(this.disabledDays){
22408             var day = value.getDay();
22409             for(var i = 0; i < this.disabledDays.length; i++) {
22410                 if(day === this.disabledDays[i]){
22411                     this.markInvalid(this.disabledDaysText);
22412                     return false;
22413                 }
22414             }
22415         }
22416         var fvalue = this.formatDate(value);
22417         if(this.ddMatch && this.ddMatch.test(fvalue)){
22418             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22419             return false;
22420         }
22421         return true;
22422     },
22423
22424     // private
22425     // Provides logic to override the default TriggerField.validateBlur which just returns true
22426     validateBlur : function(){
22427         return !this.menu || !this.menu.isVisible();
22428     },
22429
22430     /**
22431      * Returns the current date value of the date field.
22432      * @return {Date} The date value
22433      */
22434     getValue : function(){
22435         
22436         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22437     },
22438
22439     /**
22440      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22441      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22442      * (the default format used is "m/d/y").
22443      * <br />Usage:
22444      * <pre><code>
22445 //All of these calls set the same date value (May 4, 2006)
22446
22447 //Pass a date object:
22448 var dt = new Date('5/4/06');
22449 dateField.setValue(dt);
22450
22451 //Pass a date string (default format):
22452 dateField.setValue('5/4/06');
22453
22454 //Pass a date string (custom format):
22455 dateField.format = 'Y-m-d';
22456 dateField.setValue('2006-5-4');
22457 </code></pre>
22458      * @param {String/Date} date The date or valid date string
22459      */
22460     setValue : function(date){
22461         if (this.hiddenField) {
22462             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22463         }
22464         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22465     },
22466
22467     // private
22468     parseDate : function(value){
22469         if(!value || value instanceof Date){
22470             return value;
22471         }
22472         var v = Date.parseDate(value, this.format);
22473         if(!v && this.altFormats){
22474             if(!this.altFormatsArray){
22475                 this.altFormatsArray = this.altFormats.split("|");
22476             }
22477             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22478                 v = Date.parseDate(value, this.altFormatsArray[i]);
22479             }
22480         }
22481         return v;
22482     },
22483
22484     // private
22485     formatDate : function(date, fmt){
22486         return (!date || !(date instanceof Date)) ?
22487                date : date.dateFormat(fmt || this.format);
22488     },
22489
22490     // private
22491     menuListeners : {
22492         select: function(m, d){
22493             this.setValue(d);
22494             this.fireEvent('select', this, d);
22495         },
22496         show : function(){ // retain focus styling
22497             this.onFocus();
22498         },
22499         hide : function(){
22500             this.focus.defer(10, this);
22501             var ml = this.menuListeners;
22502             this.menu.un("select", ml.select,  this);
22503             this.menu.un("show", ml.show,  this);
22504             this.menu.un("hide", ml.hide,  this);
22505         }
22506     },
22507
22508     // private
22509     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22510     onTriggerClick : function(){
22511         if(this.disabled){
22512             return;
22513         }
22514         if(this.menu == null){
22515             this.menu = new Roo.menu.DateMenu();
22516         }
22517         Roo.apply(this.menu.picker,  {
22518             showClear: this.allowBlank,
22519             minDate : this.minValue,
22520             maxDate : this.maxValue,
22521             disabledDatesRE : this.ddMatch,
22522             disabledDatesText : this.disabledDatesText,
22523             disabledDays : this.disabledDays,
22524             disabledDaysText : this.disabledDaysText,
22525             format : this.format,
22526             minText : String.format(this.minText, this.formatDate(this.minValue)),
22527             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22528         });
22529         this.menu.on(Roo.apply({}, this.menuListeners, {
22530             scope:this
22531         }));
22532         this.menu.picker.setValue(this.getValue() || new Date());
22533         this.menu.show(this.el, "tl-bl?");
22534     },
22535
22536     beforeBlur : function(){
22537         var v = this.parseDate(this.getRawValue());
22538         if(v){
22539             this.setValue(v);
22540         }
22541     }
22542
22543     /** @cfg {Boolean} grow @hide */
22544     /** @cfg {Number} growMin @hide */
22545     /** @cfg {Number} growMax @hide */
22546     /**
22547      * @hide
22548      * @method autoSize
22549      */
22550 });/*
22551  * Based on:
22552  * Ext JS Library 1.1.1
22553  * Copyright(c) 2006-2007, Ext JS, LLC.
22554  *
22555  * Originally Released Under LGPL - original licence link has changed is not relivant.
22556  *
22557  * Fork - LGPL
22558  * <script type="text/javascript">
22559  */
22560  
22561
22562 /**
22563  * @class Roo.form.ComboBox
22564  * @extends Roo.form.TriggerField
22565  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22566  * @constructor
22567  * Create a new ComboBox.
22568  * @param {Object} config Configuration options
22569  */
22570 Roo.form.ComboBox = function(config){
22571     Roo.form.ComboBox.superclass.constructor.call(this, config);
22572     this.addEvents({
22573         /**
22574          * @event expand
22575          * Fires when the dropdown list is expanded
22576              * @param {Roo.form.ComboBox} combo This combo box
22577              */
22578         'expand' : true,
22579         /**
22580          * @event collapse
22581          * Fires when the dropdown list is collapsed
22582              * @param {Roo.form.ComboBox} combo This combo box
22583              */
22584         'collapse' : true,
22585         /**
22586          * @event beforeselect
22587          * Fires before a list item is selected. Return false to cancel the selection.
22588              * @param {Roo.form.ComboBox} combo This combo box
22589              * @param {Roo.data.Record} record The data record returned from the underlying store
22590              * @param {Number} index The index of the selected item in the dropdown list
22591              */
22592         'beforeselect' : true,
22593         /**
22594          * @event select
22595          * Fires when a list item is selected
22596              * @param {Roo.form.ComboBox} combo This combo box
22597              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22598              * @param {Number} index The index of the selected item in the dropdown list
22599              */
22600         'select' : true,
22601         /**
22602          * @event beforequery
22603          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22604          * The event object passed has these properties:
22605              * @param {Roo.form.ComboBox} combo This combo box
22606              * @param {String} query The query
22607              * @param {Boolean} forceAll true to force "all" query
22608              * @param {Boolean} cancel true to cancel the query
22609              * @param {Object} e The query event object
22610              */
22611         'beforequery': true,
22612          /**
22613          * @event add
22614          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22615              * @param {Roo.form.ComboBox} combo This combo box
22616              */
22617         'add' : true,
22618         /**
22619          * @event edit
22620          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22621              * @param {Roo.form.ComboBox} combo This combo box
22622              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22623              */
22624         'edit' : true
22625         
22626         
22627     });
22628     if(this.transform){
22629         this.allowDomMove = false;
22630         var s = Roo.getDom(this.transform);
22631         if(!this.hiddenName){
22632             this.hiddenName = s.name;
22633         }
22634         if(!this.store){
22635             this.mode = 'local';
22636             var d = [], opts = s.options;
22637             for(var i = 0, len = opts.length;i < len; i++){
22638                 var o = opts[i];
22639                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22640                 if(o.selected) {
22641                     this.value = value;
22642                 }
22643                 d.push([value, o.text]);
22644             }
22645             this.store = new Roo.data.SimpleStore({
22646                 'id': 0,
22647                 fields: ['value', 'text'],
22648                 data : d
22649             });
22650             this.valueField = 'value';
22651             this.displayField = 'text';
22652         }
22653         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22654         if(!this.lazyRender){
22655             this.target = true;
22656             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22657             s.parentNode.removeChild(s); // remove it
22658             this.render(this.el.parentNode);
22659         }else{
22660             s.parentNode.removeChild(s); // remove it
22661         }
22662
22663     }
22664     if (this.store) {
22665         this.store = Roo.factory(this.store, Roo.data);
22666     }
22667     
22668     this.selectedIndex = -1;
22669     if(this.mode == 'local'){
22670         if(config.queryDelay === undefined){
22671             this.queryDelay = 10;
22672         }
22673         if(config.minChars === undefined){
22674             this.minChars = 0;
22675         }
22676     }
22677 };
22678
22679 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22680     /**
22681      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22682      */
22683     /**
22684      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22685      * rendering into an Roo.Editor, defaults to false)
22686      */
22687     /**
22688      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22689      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22690      */
22691     /**
22692      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22693      */
22694     /**
22695      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22696      * the dropdown list (defaults to undefined, with no header element)
22697      */
22698
22699      /**
22700      * @cfg {String/Roo.Template} tpl The template to use to render the output
22701      */
22702      
22703     // private
22704     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22705     /**
22706      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22707      */
22708     listWidth: undefined,
22709     /**
22710      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22711      * mode = 'remote' or 'text' if mode = 'local')
22712      */
22713     displayField: undefined,
22714     /**
22715      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22716      * mode = 'remote' or 'value' if mode = 'local'). 
22717      * Note: use of a valueField requires the user make a selection
22718      * in order for a value to be mapped.
22719      */
22720     valueField: undefined,
22721     /**
22722      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22723      * field's data value (defaults to the underlying DOM element's name)
22724      */
22725     hiddenName: undefined,
22726     /**
22727      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22728      */
22729     listClass: '',
22730     /**
22731      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22732      */
22733     selectedClass: 'x-combo-selected',
22734     /**
22735      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22736      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22737      * which displays a downward arrow icon).
22738      */
22739     triggerClass : 'x-form-arrow-trigger',
22740     /**
22741      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22742      */
22743     shadow:'sides',
22744     /**
22745      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22746      * anchor positions (defaults to 'tl-bl')
22747      */
22748     listAlign: 'tl-bl?',
22749     /**
22750      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22751      */
22752     maxHeight: 300,
22753     /**
22754      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22755      * query specified by the allQuery config option (defaults to 'query')
22756      */
22757     triggerAction: 'query',
22758     /**
22759      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22760      * (defaults to 4, does not apply if editable = false)
22761      */
22762     minChars : 4,
22763     /**
22764      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22765      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22766      */
22767     typeAhead: false,
22768     /**
22769      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22770      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22771      */
22772     queryDelay: 500,
22773     /**
22774      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22775      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22776      */
22777     pageSize: 0,
22778     /**
22779      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22780      * when editable = true (defaults to false)
22781      */
22782     selectOnFocus:false,
22783     /**
22784      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22785      */
22786     queryParam: 'query',
22787     /**
22788      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22789      * when mode = 'remote' (defaults to 'Loading...')
22790      */
22791     loadingText: 'Loading...',
22792     /**
22793      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22794      */
22795     resizable: false,
22796     /**
22797      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22798      */
22799     handleHeight : 8,
22800     /**
22801      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22802      * traditional select (defaults to true)
22803      */
22804     editable: true,
22805     /**
22806      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22807      */
22808     allQuery: '',
22809     /**
22810      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22811      */
22812     mode: 'remote',
22813     /**
22814      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22815      * listWidth has a higher value)
22816      */
22817     minListWidth : 70,
22818     /**
22819      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22820      * allow the user to set arbitrary text into the field (defaults to false)
22821      */
22822     forceSelection:false,
22823     /**
22824      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22825      * if typeAhead = true (defaults to 250)
22826      */
22827     typeAheadDelay : 250,
22828     /**
22829      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22830      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22831      */
22832     valueNotFoundText : undefined,
22833     /**
22834      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22835      */
22836     blockFocus : false,
22837     
22838     /**
22839      * @cfg {Boolean} disableClear Disable showing of clear button.
22840      */
22841     disableClear : false,
22842     /**
22843      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22844      */
22845     alwaysQuery : false,
22846     
22847     //private
22848     addicon : false,
22849     editicon: false,
22850     
22851     
22852     // private
22853     onRender : function(ct, position){
22854         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22855         if(this.hiddenName){
22856             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22857                     'before', true);
22858             this.hiddenField.value =
22859                 this.hiddenValue !== undefined ? this.hiddenValue :
22860                 this.value !== undefined ? this.value : '';
22861
22862             // prevent input submission
22863             this.el.dom.removeAttribute('name');
22864         }
22865         if(Roo.isGecko){
22866             this.el.dom.setAttribute('autocomplete', 'off');
22867         }
22868
22869         var cls = 'x-combo-list';
22870
22871         this.list = new Roo.Layer({
22872             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22873         });
22874
22875         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22876         this.list.setWidth(lw);
22877         this.list.swallowEvent('mousewheel');
22878         this.assetHeight = 0;
22879
22880         if(this.title){
22881             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22882             this.assetHeight += this.header.getHeight();
22883         }
22884
22885         this.innerList = this.list.createChild({cls:cls+'-inner'});
22886         this.innerList.on('mouseover', this.onViewOver, this);
22887         this.innerList.on('mousemove', this.onViewMove, this);
22888         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22889         
22890         if(this.allowBlank && !this.pageSize && !this.disableClear){
22891             this.footer = this.list.createChild({cls:cls+'-ft'});
22892             this.pageTb = new Roo.Toolbar(this.footer);
22893            
22894         }
22895         if(this.pageSize){
22896             this.footer = this.list.createChild({cls:cls+'-ft'});
22897             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22898                     {pageSize: this.pageSize});
22899             
22900         }
22901         
22902         if (this.pageTb && this.allowBlank && !this.disableClear) {
22903             var _this = this;
22904             this.pageTb.add(new Roo.Toolbar.Fill(), {
22905                 cls: 'x-btn-icon x-btn-clear',
22906                 text: '&#160;',
22907                 handler: function()
22908                 {
22909                     _this.collapse();
22910                     _this.clearValue();
22911                     _this.onSelect(false, -1);
22912                 }
22913             });
22914         }
22915         if (this.footer) {
22916             this.assetHeight += this.footer.getHeight();
22917         }
22918         
22919
22920         if(!this.tpl){
22921             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22922         }
22923
22924         this.view = new Roo.View(this.innerList, this.tpl, {
22925             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22926         });
22927
22928         this.view.on('click', this.onViewClick, this);
22929
22930         this.store.on('beforeload', this.onBeforeLoad, this);
22931         this.store.on('load', this.onLoad, this);
22932         this.store.on('loadexception', this.collapse, this);
22933
22934         if(this.resizable){
22935             this.resizer = new Roo.Resizable(this.list,  {
22936                pinned:true, handles:'se'
22937             });
22938             this.resizer.on('resize', function(r, w, h){
22939                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22940                 this.listWidth = w;
22941                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22942                 this.restrictHeight();
22943             }, this);
22944             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22945         }
22946         if(!this.editable){
22947             this.editable = true;
22948             this.setEditable(false);
22949         }  
22950         
22951         
22952         if (typeof(this.events.add.listeners) != 'undefined') {
22953             
22954             this.addicon = this.wrap.createChild(
22955                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22956        
22957             this.addicon.on('click', function(e) {
22958                 this.fireEvent('add', this);
22959             }, this);
22960         }
22961         if (typeof(this.events.edit.listeners) != 'undefined') {
22962             
22963             this.editicon = this.wrap.createChild(
22964                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22965             if (this.addicon) {
22966                 this.editicon.setStyle('margin-left', '40px');
22967             }
22968             this.editicon.on('click', function(e) {
22969                 
22970                 // we fire even  if inothing is selected..
22971                 this.fireEvent('edit', this, this.lastData );
22972                 
22973             }, this);
22974         }
22975         
22976         
22977         
22978     },
22979
22980     // private
22981     initEvents : function(){
22982         Roo.form.ComboBox.superclass.initEvents.call(this);
22983
22984         this.keyNav = new Roo.KeyNav(this.el, {
22985             "up" : function(e){
22986                 this.inKeyMode = true;
22987                 this.selectPrev();
22988             },
22989
22990             "down" : function(e){
22991                 if(!this.isExpanded()){
22992                     this.onTriggerClick();
22993                 }else{
22994                     this.inKeyMode = true;
22995                     this.selectNext();
22996                 }
22997             },
22998
22999             "enter" : function(e){
23000                 this.onViewClick();
23001                 //return true;
23002             },
23003
23004             "esc" : function(e){
23005                 this.collapse();
23006             },
23007
23008             "tab" : function(e){
23009                 this.onViewClick(false);
23010                 return true;
23011             },
23012
23013             scope : this,
23014
23015             doRelay : function(foo, bar, hname){
23016                 if(hname == 'down' || this.scope.isExpanded()){
23017                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23018                 }
23019                 return true;
23020             },
23021
23022             forceKeyDown: true
23023         });
23024         this.queryDelay = Math.max(this.queryDelay || 10,
23025                 this.mode == 'local' ? 10 : 250);
23026         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23027         if(this.typeAhead){
23028             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23029         }
23030         if(this.editable !== false){
23031             this.el.on("keyup", this.onKeyUp, this);
23032         }
23033         if(this.forceSelection){
23034             this.on('blur', this.doForce, this);
23035         }
23036     },
23037
23038     onDestroy : function(){
23039         if(this.view){
23040             this.view.setStore(null);
23041             this.view.el.removeAllListeners();
23042             this.view.el.remove();
23043             this.view.purgeListeners();
23044         }
23045         if(this.list){
23046             this.list.destroy();
23047         }
23048         if(this.store){
23049             this.store.un('beforeload', this.onBeforeLoad, this);
23050             this.store.un('load', this.onLoad, this);
23051             this.store.un('loadexception', this.collapse, this);
23052         }
23053         Roo.form.ComboBox.superclass.onDestroy.call(this);
23054     },
23055
23056     // private
23057     fireKey : function(e){
23058         if(e.isNavKeyPress() && !this.list.isVisible()){
23059             this.fireEvent("specialkey", this, e);
23060         }
23061     },
23062
23063     // private
23064     onResize: function(w, h){
23065         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23066         
23067         if(typeof w != 'number'){
23068             // we do not handle it!?!?
23069             return;
23070         }
23071         var tw = this.trigger.getWidth();
23072         tw += this.addicon ? this.addicon.getWidth() : 0;
23073         tw += this.editicon ? this.editicon.getWidth() : 0;
23074         var x = w - tw;
23075         this.el.setWidth( this.adjustWidth('input', x));
23076             
23077         this.trigger.setStyle('left', x+'px');
23078         
23079         if(this.list && this.listWidth === undefined){
23080             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23081             this.list.setWidth(lw);
23082             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23083         }
23084         
23085     
23086         
23087     },
23088
23089     /**
23090      * Allow or prevent the user from directly editing the field text.  If false is passed,
23091      * the user will only be able to select from the items defined in the dropdown list.  This method
23092      * is the runtime equivalent of setting the 'editable' config option at config time.
23093      * @param {Boolean} value True to allow the user to directly edit the field text
23094      */
23095     setEditable : function(value){
23096         if(value == this.editable){
23097             return;
23098         }
23099         this.editable = value;
23100         if(!value){
23101             this.el.dom.setAttribute('readOnly', true);
23102             this.el.on('mousedown', this.onTriggerClick,  this);
23103             this.el.addClass('x-combo-noedit');
23104         }else{
23105             this.el.dom.setAttribute('readOnly', false);
23106             this.el.un('mousedown', this.onTriggerClick,  this);
23107             this.el.removeClass('x-combo-noedit');
23108         }
23109     },
23110
23111     // private
23112     onBeforeLoad : function(){
23113         if(!this.hasFocus){
23114             return;
23115         }
23116         this.innerList.update(this.loadingText ?
23117                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23118         this.restrictHeight();
23119         this.selectedIndex = -1;
23120     },
23121
23122     // private
23123     onLoad : function(){
23124         if(!this.hasFocus){
23125             return;
23126         }
23127         if(this.store.getCount() > 0){
23128             this.expand();
23129             this.restrictHeight();
23130             if(this.lastQuery == this.allQuery){
23131                 if(this.editable){
23132                     this.el.dom.select();
23133                 }
23134                 if(!this.selectByValue(this.value, true)){
23135                     this.select(0, true);
23136                 }
23137             }else{
23138                 this.selectNext();
23139                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23140                     this.taTask.delay(this.typeAheadDelay);
23141                 }
23142             }
23143         }else{
23144             this.onEmptyResults();
23145         }
23146         //this.el.focus();
23147     },
23148
23149     // private
23150     onTypeAhead : function(){
23151         if(this.store.getCount() > 0){
23152             var r = this.store.getAt(0);
23153             var newValue = r.data[this.displayField];
23154             var len = newValue.length;
23155             var selStart = this.getRawValue().length;
23156             if(selStart != len){
23157                 this.setRawValue(newValue);
23158                 this.selectText(selStart, newValue.length);
23159             }
23160         }
23161     },
23162
23163     // private
23164     onSelect : function(record, index){
23165         if(this.fireEvent('beforeselect', this, record, index) !== false){
23166             this.setFromData(index > -1 ? record.data : false);
23167             this.collapse();
23168             this.fireEvent('select', this, record, index);
23169         }
23170     },
23171
23172     /**
23173      * Returns the currently selected field value or empty string if no value is set.
23174      * @return {String} value The selected value
23175      */
23176     getValue : function(){
23177         if(this.valueField){
23178             return typeof this.value != 'undefined' ? this.value : '';
23179         }else{
23180             return Roo.form.ComboBox.superclass.getValue.call(this);
23181         }
23182     },
23183
23184     /**
23185      * Clears any text/value currently set in the field
23186      */
23187     clearValue : function(){
23188         if(this.hiddenField){
23189             this.hiddenField.value = '';
23190         }
23191         this.value = '';
23192         this.setRawValue('');
23193         this.lastSelectionText = '';
23194         this.applyEmptyText();
23195     },
23196
23197     /**
23198      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23199      * will be displayed in the field.  If the value does not match the data value of an existing item,
23200      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23201      * Otherwise the field will be blank (although the value will still be set).
23202      * @param {String} value The value to match
23203      */
23204     setValue : function(v){
23205         var text = v;
23206         if(this.valueField){
23207             var r = this.findRecord(this.valueField, v);
23208             if(r){
23209                 text = r.data[this.displayField];
23210             }else if(this.valueNotFoundText !== undefined){
23211                 text = this.valueNotFoundText;
23212             }
23213         }
23214         this.lastSelectionText = text;
23215         if(this.hiddenField){
23216             this.hiddenField.value = v;
23217         }
23218         Roo.form.ComboBox.superclass.setValue.call(this, text);
23219         this.value = v;
23220     },
23221     /**
23222      * @property {Object} the last set data for the element
23223      */
23224     
23225     lastData : false,
23226     /**
23227      * Sets the value of the field based on a object which is related to the record format for the store.
23228      * @param {Object} value the value to set as. or false on reset?
23229      */
23230     setFromData : function(o){
23231         var dv = ''; // display value
23232         var vv = ''; // value value..
23233         this.lastData = o;
23234         if (this.displayField) {
23235             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23236         } else {
23237             // this is an error condition!!!
23238             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23239         }
23240         
23241         if(this.valueField){
23242             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23243         }
23244         if(this.hiddenField){
23245             this.hiddenField.value = vv;
23246             
23247             this.lastSelectionText = dv;
23248             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23249             this.value = vv;
23250             return;
23251         }
23252         // no hidden field.. - we store the value in 'value', but still display
23253         // display field!!!!
23254         this.lastSelectionText = dv;
23255         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23256         this.value = vv;
23257         
23258         
23259     },
23260     // private
23261     reset : function(){
23262         // overridden so that last data is reset..
23263         this.setValue(this.originalValue);
23264         this.clearInvalid();
23265         this.lastData = false;
23266     },
23267     // private
23268     findRecord : function(prop, value){
23269         var record;
23270         if(this.store.getCount() > 0){
23271             this.store.each(function(r){
23272                 if(r.data[prop] == value){
23273                     record = r;
23274                     return false;
23275                 }
23276             });
23277         }
23278         return record;
23279     },
23280
23281     // private
23282     onViewMove : function(e, t){
23283         this.inKeyMode = false;
23284     },
23285
23286     // private
23287     onViewOver : function(e, t){
23288         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23289             return;
23290         }
23291         var item = this.view.findItemFromChild(t);
23292         if(item){
23293             var index = this.view.indexOf(item);
23294             this.select(index, false);
23295         }
23296     },
23297
23298     // private
23299     onViewClick : function(doFocus){
23300         var index = this.view.getSelectedIndexes()[0];
23301         var r = this.store.getAt(index);
23302         if(r){
23303             this.onSelect(r, index);
23304         }
23305         if(doFocus !== false && !this.blockFocus){
23306             this.el.focus();
23307         }
23308     },
23309
23310     // private
23311     restrictHeight : function(){
23312         this.innerList.dom.style.height = '';
23313         var inner = this.innerList.dom;
23314         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23315         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23316         this.list.beginUpdate();
23317         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23318         this.list.alignTo(this.el, this.listAlign);
23319         this.list.endUpdate();
23320     },
23321
23322     // private
23323     onEmptyResults : function(){
23324         this.collapse();
23325     },
23326
23327     /**
23328      * Returns true if the dropdown list is expanded, else false.
23329      */
23330     isExpanded : function(){
23331         return this.list.isVisible();
23332     },
23333
23334     /**
23335      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23336      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23337      * @param {String} value The data value of the item to select
23338      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23339      * selected item if it is not currently in view (defaults to true)
23340      * @return {Boolean} True if the value matched an item in the list, else false
23341      */
23342     selectByValue : function(v, scrollIntoView){
23343         if(v !== undefined && v !== null){
23344             var r = this.findRecord(this.valueField || this.displayField, v);
23345             if(r){
23346                 this.select(this.store.indexOf(r), scrollIntoView);
23347                 return true;
23348             }
23349         }
23350         return false;
23351     },
23352
23353     /**
23354      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23355      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23356      * @param {Number} index The zero-based index of the list item to select
23357      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23358      * selected item if it is not currently in view (defaults to true)
23359      */
23360     select : function(index, scrollIntoView){
23361         this.selectedIndex = index;
23362         this.view.select(index);
23363         if(scrollIntoView !== false){
23364             var el = this.view.getNode(index);
23365             if(el){
23366                 this.innerList.scrollChildIntoView(el, false);
23367             }
23368         }
23369     },
23370
23371     // private
23372     selectNext : function(){
23373         var ct = this.store.getCount();
23374         if(ct > 0){
23375             if(this.selectedIndex == -1){
23376                 this.select(0);
23377             }else if(this.selectedIndex < ct-1){
23378                 this.select(this.selectedIndex+1);
23379             }
23380         }
23381     },
23382
23383     // private
23384     selectPrev : function(){
23385         var ct = this.store.getCount();
23386         if(ct > 0){
23387             if(this.selectedIndex == -1){
23388                 this.select(0);
23389             }else if(this.selectedIndex != 0){
23390                 this.select(this.selectedIndex-1);
23391             }
23392         }
23393     },
23394
23395     // private
23396     onKeyUp : function(e){
23397         if(this.editable !== false && !e.isSpecialKey()){
23398             this.lastKey = e.getKey();
23399             this.dqTask.delay(this.queryDelay);
23400         }
23401     },
23402
23403     // private
23404     validateBlur : function(){
23405         return !this.list || !this.list.isVisible();   
23406     },
23407
23408     // private
23409     initQuery : function(){
23410         this.doQuery(this.getRawValue());
23411     },
23412
23413     // private
23414     doForce : function(){
23415         if(this.el.dom.value.length > 0){
23416             this.el.dom.value =
23417                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23418             this.applyEmptyText();
23419         }
23420     },
23421
23422     /**
23423      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23424      * query allowing the query action to be canceled if needed.
23425      * @param {String} query The SQL query to execute
23426      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23427      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23428      * saved in the current store (defaults to false)
23429      */
23430     doQuery : function(q, forceAll){
23431         if(q === undefined || q === null){
23432             q = '';
23433         }
23434         var qe = {
23435             query: q,
23436             forceAll: forceAll,
23437             combo: this,
23438             cancel:false
23439         };
23440         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23441             return false;
23442         }
23443         q = qe.query;
23444         forceAll = qe.forceAll;
23445         if(forceAll === true || (q.length >= this.minChars)){
23446             if(this.lastQuery != q || this.alwaysQuery){
23447                 this.lastQuery = q;
23448                 if(this.mode == 'local'){
23449                     this.selectedIndex = -1;
23450                     if(forceAll){
23451                         this.store.clearFilter();
23452                     }else{
23453                         this.store.filter(this.displayField, q);
23454                     }
23455                     this.onLoad();
23456                 }else{
23457                     this.store.baseParams[this.queryParam] = q;
23458                     this.store.load({
23459                         params: this.getParams(q)
23460                     });
23461                     this.expand();
23462                 }
23463             }else{
23464                 this.selectedIndex = -1;
23465                 this.onLoad();   
23466             }
23467         }
23468     },
23469
23470     // private
23471     getParams : function(q){
23472         var p = {};
23473         //p[this.queryParam] = q;
23474         if(this.pageSize){
23475             p.start = 0;
23476             p.limit = this.pageSize;
23477         }
23478         return p;
23479     },
23480
23481     /**
23482      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23483      */
23484     collapse : function(){
23485         if(!this.isExpanded()){
23486             return;
23487         }
23488         this.list.hide();
23489         Roo.get(document).un('mousedown', this.collapseIf, this);
23490         Roo.get(document).un('mousewheel', this.collapseIf, this);
23491         if (!this.editable) {
23492             Roo.get(document).un('keydown', this.listKeyPress, this);
23493         }
23494         this.fireEvent('collapse', this);
23495     },
23496
23497     // private
23498     collapseIf : function(e){
23499         if(!e.within(this.wrap) && !e.within(this.list)){
23500             this.collapse();
23501         }
23502     },
23503
23504     /**
23505      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23506      */
23507     expand : function(){
23508         if(this.isExpanded() || !this.hasFocus){
23509             return;
23510         }
23511         this.list.alignTo(this.el, this.listAlign);
23512         this.list.show();
23513         Roo.get(document).on('mousedown', this.collapseIf, this);
23514         Roo.get(document).on('mousewheel', this.collapseIf, this);
23515         if (!this.editable) {
23516             Roo.get(document).on('keydown', this.listKeyPress, this);
23517         }
23518         
23519         this.fireEvent('expand', this);
23520     },
23521
23522     // private
23523     // Implements the default empty TriggerField.onTriggerClick function
23524     onTriggerClick : function(){
23525         if(this.disabled){
23526             return;
23527         }
23528         if(this.isExpanded()){
23529             this.collapse();
23530             if (!this.blockFocus) {
23531                 this.el.focus();
23532             }
23533             
23534         }else {
23535             this.hasFocus = true;
23536             if(this.triggerAction == 'all') {
23537                 this.doQuery(this.allQuery, true);
23538             } else {
23539                 this.doQuery(this.getRawValue());
23540             }
23541             if (!this.blockFocus) {
23542                 this.el.focus();
23543             }
23544         }
23545     },
23546     listKeyPress : function(e)
23547     {
23548         //Roo.log('listkeypress');
23549         // scroll to first matching element based on key pres..
23550         if (e.isSpecialKey()) {
23551             return false;
23552         }
23553         var k = String.fromCharCode(e.getKey()).toUpperCase();
23554         //Roo.log(k);
23555         var match  = false;
23556         var csel = this.view.getSelectedNodes();
23557         var cselitem = false;
23558         if (csel.length) {
23559             var ix = this.view.indexOf(csel[0]);
23560             cselitem  = this.store.getAt(ix);
23561             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23562                 cselitem = false;
23563             }
23564             
23565         }
23566         
23567         this.store.each(function(v) { 
23568             if (cselitem) {
23569                 // start at existing selection.
23570                 if (cselitem.id == v.id) {
23571                     cselitem = false;
23572                 }
23573                 return;
23574             }
23575                 
23576             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23577                 match = this.store.indexOf(v);
23578                 return false;
23579             }
23580         }, this);
23581         
23582         if (match === false) {
23583             return true; // no more action?
23584         }
23585         // scroll to?
23586         this.view.select(match);
23587         var sn = Roo.get(this.view.getSelectedNodes()[0])
23588         sn.scrollIntoView(sn.dom.parentNode, false);
23589     }
23590
23591     /** 
23592     * @cfg {Boolean} grow 
23593     * @hide 
23594     */
23595     /** 
23596     * @cfg {Number} growMin 
23597     * @hide 
23598     */
23599     /** 
23600     * @cfg {Number} growMax 
23601     * @hide 
23602     */
23603     /**
23604      * @hide
23605      * @method autoSize
23606      */
23607 });