roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @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)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <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
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from 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 removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from 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 cascade is stopped on that branch.
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     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * 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
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @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.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<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>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @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.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157         /**
9158          * @event beforeclick
9159          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
9166         /**
9167          * @event click
9168          * Fires when a template node is 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             "click" : true,
9175         /**
9176          * @event dblclick
9177          * Fires when a template node is double clicked.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "dblclick" : true,
9184         /**
9185          * @event contextmenu
9186          * Fires when a template node is right clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "contextmenu" : true,
9193         /**
9194          * @event selectionchange
9195          * Fires when the selected nodes change.
9196          * @param {Roo.View} this
9197          * @param {Array} selections Array of the selected nodes
9198          */
9199             "selectionchange" : true,
9200     
9201         /**
9202          * @event beforeselect
9203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204          * @param {Roo.View} this
9205          * @param {HTMLElement} node The node to be selected
9206          * @param {Array} selections Array of currently selected nodes
9207          */
9208             "beforeselect" : true,
9209         /**
9210          * @event preparedata
9211          * Fires on every row to render, to allow you to change the data.
9212          * @param {Roo.View} this
9213          * @param {Object} data to be rendered (change this)
9214          */
9215           "preparedata" : true
9216         });
9217
9218     this.el.on({
9219         "click": this.onClick,
9220         "dblclick": this.onDblClick,
9221         "contextmenu": this.onContextMenu,
9222         scope:this
9223     });
9224
9225     this.selections = [];
9226     this.nodes = [];
9227     this.cmp = new Roo.CompositeElementLite([]);
9228     if(this.store){
9229         this.store = Roo.factory(this.store, Roo.data);
9230         this.setStore(this.store, true);
9231     }
9232     Roo.View.superclass.constructor.call(this);
9233 };
9234
9235 Roo.extend(Roo.View, Roo.util.Observable, {
9236     
9237      /**
9238      * @cfg {Roo.data.Store} store Data store to load data from.
9239      */
9240     store : false,
9241     
9242     /**
9243      * @cfg {String|Roo.Element} el The container element.
9244      */
9245     el : '',
9246     
9247     /**
9248      * @cfg {String|Roo.Template} tpl The template used by this View 
9249      */
9250     tpl : false,
9251     
9252     /**
9253      * @cfg {String} selectedClass The css class to add to selected nodes
9254      */
9255     selectedClass : "x-view-selected",
9256      /**
9257      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9258      */
9259     emptyText : "",
9260     /**
9261      * @cfg {Boolean} multiSelect Allow multiple selection
9262      */
9263     
9264     multiSelect : false,
9265     /**
9266      * @cfg {Boolean} singleSelect Allow single selection
9267      */
9268     singleSelect:  false,
9269     
9270     /**
9271      * Returns the element this view is bound to.
9272      * @return {Roo.Element}
9273      */
9274     getEl : function(){
9275         return this.el;
9276     },
9277
9278     /**
9279      * Refreshes the view.
9280      */
9281     refresh : function(){
9282         var t = this.tpl;
9283         this.clearSelections();
9284         this.el.update("");
9285         var html = [];
9286         var records = this.store.getRange();
9287         if(records.length < 1){
9288             this.el.update(this.emptyText);
9289             return;
9290         }
9291         for(var i = 0, len = records.length; i < len; i++){
9292             var data = this.prepareData(records[i].data, i, records[i]);
9293             this.fireEvent("preparedata", this, data, i, records[i]);
9294             html[html.length] = t.apply(data);
9295         }
9296         this.el.update(html.join(""));
9297         this.nodes = this.el.dom.childNodes;
9298         this.updateIndexes(0);
9299     },
9300
9301     /**
9302      * Function to override to reformat the data that is sent to
9303      * the template for each node.
9304      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9305      * a JSON object for an UpdateManager bound view).
9306      */
9307     prepareData : function(data){
9308         return data;
9309     },
9310
9311     onUpdate : function(ds, record){
9312         this.clearSelections();
9313         var index = this.store.indexOf(record);
9314         var n = this.nodes[index];
9315         this.tpl.insertBefore(n, this.prepareData(record.data));
9316         n.parentNode.removeChild(n);
9317         this.updateIndexes(index, index);
9318     },
9319
9320     onAdd : function(ds, records, index){
9321         this.clearSelections();
9322         if(this.nodes.length == 0){
9323             this.refresh();
9324             return;
9325         }
9326         var n = this.nodes[index];
9327         for(var i = 0, len = records.length; i < len; i++){
9328             var d = this.prepareData(records[i].data);
9329             if(n){
9330                 this.tpl.insertBefore(n, d);
9331             }else{
9332                 this.tpl.append(this.el, d);
9333             }
9334         }
9335         this.updateIndexes(index);
9336     },
9337
9338     onRemove : function(ds, record, index){
9339         this.clearSelections();
9340         this.el.dom.removeChild(this.nodes[index]);
9341         this.updateIndexes(index);
9342     },
9343
9344     /**
9345      * Refresh an individual node.
9346      * @param {Number} index
9347      */
9348     refreshNode : function(index){
9349         this.onUpdate(this.store, this.store.getAt(index));
9350     },
9351
9352     updateIndexes : function(startIndex, endIndex){
9353         var ns = this.nodes;
9354         startIndex = startIndex || 0;
9355         endIndex = endIndex || ns.length - 1;
9356         for(var i = startIndex; i <= endIndex; i++){
9357             ns[i].nodeIndex = i;
9358         }
9359     },
9360
9361     /**
9362      * Changes the data store this view uses and refresh the view.
9363      * @param {Store} store
9364      */
9365     setStore : function(store, initial){
9366         if(!initial && this.store){
9367             this.store.un("datachanged", this.refresh);
9368             this.store.un("add", this.onAdd);
9369             this.store.un("remove", this.onRemove);
9370             this.store.un("update", this.onUpdate);
9371             this.store.un("clear", this.refresh);
9372         }
9373         if(store){
9374           
9375             store.on("datachanged", this.refresh, this);
9376             store.on("add", this.onAdd, this);
9377             store.on("remove", this.onRemove, this);
9378             store.on("update", this.onUpdate, this);
9379             store.on("clear", this.refresh, this);
9380         }
9381         
9382         if(store){
9383             this.refresh();
9384         }
9385     },
9386
9387     /**
9388      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9389      * @param {HTMLElement} node
9390      * @return {HTMLElement} The template node
9391      */
9392     findItemFromChild : function(node){
9393         var el = this.el.dom;
9394         if(!node || node.parentNode == el){
9395                     return node;
9396             }
9397             var p = node.parentNode;
9398             while(p && p != el){
9399             if(p.parentNode == el){
9400                 return p;
9401             }
9402             p = p.parentNode;
9403         }
9404             return null;
9405     },
9406
9407     /** @ignore */
9408     onClick : function(e){
9409         var item = this.findItemFromChild(e.getTarget());
9410         if(item){
9411             var index = this.indexOf(item);
9412             if(this.onItemClick(item, index, e) !== false){
9413                 this.fireEvent("click", this, index, item, e);
9414             }
9415         }else{
9416             this.clearSelections();
9417         }
9418     },
9419
9420     /** @ignore */
9421     onContextMenu : function(e){
9422         var item = this.findItemFromChild(e.getTarget());
9423         if(item){
9424             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9425         }
9426     },
9427
9428     /** @ignore */
9429     onDblClick : function(e){
9430         var item = this.findItemFromChild(e.getTarget());
9431         if(item){
9432             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9433         }
9434     },
9435
9436     onItemClick : function(item, index, e){
9437         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9438             return false;
9439         }
9440         if(this.multiSelect || this.singleSelect){
9441             if(this.multiSelect && e.shiftKey && this.lastSelection){
9442                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9443             }else{
9444                 this.select(item, this.multiSelect && e.ctrlKey);
9445                 this.lastSelection = item;
9446             }
9447             e.preventDefault();
9448         }
9449         return true;
9450     },
9451
9452     /**
9453      * Get the number of selected nodes.
9454      * @return {Number}
9455      */
9456     getSelectionCount : function(){
9457         return this.selections.length;
9458     },
9459
9460     /**
9461      * Get the currently selected nodes.
9462      * @return {Array} An array of HTMLElements
9463      */
9464     getSelectedNodes : function(){
9465         return this.selections;
9466     },
9467
9468     /**
9469      * Get the indexes of the selected nodes.
9470      * @return {Array}
9471      */
9472     getSelectedIndexes : function(){
9473         var indexes = [], s = this.selections;
9474         for(var i = 0, len = s.length; i < len; i++){
9475             indexes.push(s[i].nodeIndex);
9476         }
9477         return indexes;
9478     },
9479
9480     /**
9481      * Clear all selections
9482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9483      */
9484     clearSelections : function(suppressEvent){
9485         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9486             this.cmp.elements = this.selections;
9487             this.cmp.removeClass(this.selectedClass);
9488             this.selections = [];
9489             if(!suppressEvent){
9490                 this.fireEvent("selectionchange", this, this.selections);
9491             }
9492         }
9493     },
9494
9495     /**
9496      * Returns true if the passed node is selected
9497      * @param {HTMLElement/Number} node The node or node index
9498      * @return {Boolean}
9499      */
9500     isSelected : function(node){
9501         var s = this.selections;
9502         if(s.length < 1){
9503             return false;
9504         }
9505         node = this.getNode(node);
9506         return s.indexOf(node) !== -1;
9507     },
9508
9509     /**
9510      * Selects nodes.
9511      * @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
9512      * @param {Boolean} keepExisting (optional) true to keep existing selections
9513      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9514      */
9515     select : function(nodeInfo, keepExisting, suppressEvent){
9516         if(nodeInfo instanceof Array){
9517             if(!keepExisting){
9518                 this.clearSelections(true);
9519             }
9520             for(var i = 0, len = nodeInfo.length; i < len; i++){
9521                 this.select(nodeInfo[i], true, true);
9522             }
9523         } else{
9524             var node = this.getNode(nodeInfo);
9525             if(node && !this.isSelected(node)){
9526                 if(!keepExisting){
9527                     this.clearSelections(true);
9528                 }
9529                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9530                     Roo.fly(node).addClass(this.selectedClass);
9531                     this.selections.push(node);
9532                     if(!suppressEvent){
9533                         this.fireEvent("selectionchange", this, this.selections);
9534                     }
9535                 }
9536             }
9537         }
9538     },
9539
9540     /**
9541      * Gets a template node.
9542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9543      * @return {HTMLElement} The node or null if it wasn't found
9544      */
9545     getNode : function(nodeInfo){
9546         if(typeof nodeInfo == "string"){
9547             return document.getElementById(nodeInfo);
9548         }else if(typeof nodeInfo == "number"){
9549             return this.nodes[nodeInfo];
9550         }
9551         return nodeInfo;
9552     },
9553
9554     /**
9555      * Gets a range template nodes.
9556      * @param {Number} startIndex
9557      * @param {Number} endIndex
9558      * @return {Array} An array of nodes
9559      */
9560     getNodes : function(start, end){
9561         var ns = this.nodes;
9562         start = start || 0;
9563         end = typeof end == "undefined" ? ns.length - 1 : end;
9564         var nodes = [];
9565         if(start <= end){
9566             for(var i = start; i <= end; i++){
9567                 nodes.push(ns[i]);
9568             }
9569         } else{
9570             for(var i = start; i >= end; i--){
9571                 nodes.push(ns[i]);
9572             }
9573         }
9574         return nodes;
9575     },
9576
9577     /**
9578      * Finds the index of the passed node
9579      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9580      * @return {Number} The index of the node or -1
9581      */
9582     indexOf : function(node){
9583         node = this.getNode(node);
9584         if(typeof node.nodeIndex == "number"){
9585             return node.nodeIndex;
9586         }
9587         var ns = this.nodes;
9588         for(var i = 0, len = ns.length; i < len; i++){
9589             if(ns[i] == node){
9590                 return i;
9591             }
9592         }
9593         return -1;
9594     }
9595 });
9596 /*
9597  * Based on:
9598  * Ext JS Library 1.1.1
9599  * Copyright(c) 2006-2007, Ext JS, LLC.
9600  *
9601  * Originally Released Under LGPL - original licence link has changed is not relivant.
9602  *
9603  * Fork - LGPL
9604  * <script type="text/javascript">
9605  */
9606
9607 /**
9608  * @class Roo.JsonView
9609  * @extends Roo.View
9610  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9611 <pre><code>
9612 var view = new Roo.JsonView({
9613     container: "my-element",
9614     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9615     multiSelect: true, 
9616     jsonRoot: "data" 
9617 });
9618
9619 // listen for node click?
9620 view.on("click", function(vw, index, node, e){
9621     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9622 });
9623
9624 // direct load of JSON data
9625 view.load("foobar.php");
9626
9627 // Example from my blog list
9628 var tpl = new Roo.Template(
9629     '&lt;div class="entry"&gt;' +
9630     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9631     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9632     "&lt;/div&gt;&lt;hr /&gt;"
9633 );
9634
9635 var moreView = new Roo.JsonView({
9636     container :  "entry-list", 
9637     template : tpl,
9638     jsonRoot: "posts"
9639 });
9640 moreView.on("beforerender", this.sortEntries, this);
9641 moreView.load({
9642     url: "/blog/get-posts.php",
9643     params: "allposts=true",
9644     text: "Loading Blog Entries..."
9645 });
9646 </code></pre>
9647
9648 * Note: old code is supported with arguments : (container, template, config)
9649
9650
9651  * @constructor
9652  * Create a new JsonView
9653  * 
9654  * @param {Object} config The config object
9655  * 
9656  */
9657 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9658     
9659     
9660     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9661
9662     var um = this.el.getUpdateManager();
9663     um.setRenderer(this);
9664     um.on("update", this.onLoad, this);
9665     um.on("failure", this.onLoadException, this);
9666
9667     /**
9668      * @event beforerender
9669      * Fires before rendering of the downloaded JSON data.
9670      * @param {Roo.JsonView} this
9671      * @param {Object} data The JSON data loaded
9672      */
9673     /**
9674      * @event load
9675      * Fires when data is loaded.
9676      * @param {Roo.JsonView} this
9677      * @param {Object} data The JSON data loaded
9678      * @param {Object} response The raw Connect response object
9679      */
9680     /**
9681      * @event loadexception
9682      * Fires when loading fails.
9683      * @param {Roo.JsonView} this
9684      * @param {Object} response The raw Connect response object
9685      */
9686     this.addEvents({
9687         'beforerender' : true,
9688         'load' : true,
9689         'loadexception' : true
9690     });
9691 };
9692 Roo.extend(Roo.JsonView, Roo.View, {
9693     /**
9694      * @type {String} The root property in the loaded JSON object that contains the data
9695      */
9696     jsonRoot : "",
9697
9698     /**
9699      * Refreshes the view.
9700      */
9701     refresh : function(){
9702         this.clearSelections();
9703         this.el.update("");
9704         var html = [];
9705         var o = this.jsonData;
9706         if(o && o.length > 0){
9707             for(var i = 0, len = o.length; i < len; i++){
9708                 var data = this.prepareData(o[i], i, o);
9709                 html[html.length] = this.tpl.apply(data);
9710             }
9711         }else{
9712             html.push(this.emptyText);
9713         }
9714         this.el.update(html.join(""));
9715         this.nodes = this.el.dom.childNodes;
9716         this.updateIndexes(0);
9717     },
9718
9719     /**
9720      * 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.
9721      * @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:
9722      <pre><code>
9723      view.load({
9724          url: "your-url.php",
9725          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9726          callback: yourFunction,
9727          scope: yourObject, //(optional scope)
9728          discardUrl: false,
9729          nocache: false,
9730          text: "Loading...",
9731          timeout: 30,
9732          scripts: false
9733      });
9734      </code></pre>
9735      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9736      * 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.
9737      * @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}
9738      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9739      * @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.
9740      */
9741     load : function(){
9742         var um = this.el.getUpdateManager();
9743         um.update.apply(um, arguments);
9744     },
9745
9746     render : function(el, response){
9747         this.clearSelections();
9748         this.el.update("");
9749         var o;
9750         try{
9751             o = Roo.util.JSON.decode(response.responseText);
9752             if(this.jsonRoot){
9753                 
9754                 o = o[this.jsonRoot];
9755             }
9756         } catch(e){
9757         }
9758         /**
9759          * The current JSON data or null
9760          */
9761         this.jsonData = o;
9762         this.beforeRender();
9763         this.refresh();
9764     },
9765
9766 /**
9767  * Get the number of records in the current JSON dataset
9768  * @return {Number}
9769  */
9770     getCount : function(){
9771         return this.jsonData ? this.jsonData.length : 0;
9772     },
9773
9774 /**
9775  * Returns the JSON object for the specified node(s)
9776  * @param {HTMLElement/Array} node The node or an array of nodes
9777  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9778  * you get the JSON object for the node
9779  */
9780     getNodeData : function(node){
9781         if(node instanceof Array){
9782             var data = [];
9783             for(var i = 0, len = node.length; i < len; i++){
9784                 data.push(this.getNodeData(node[i]));
9785             }
9786             return data;
9787         }
9788         return this.jsonData[this.indexOf(node)] || null;
9789     },
9790
9791     beforeRender : function(){
9792         this.snapshot = this.jsonData;
9793         if(this.sortInfo){
9794             this.sort.apply(this, this.sortInfo);
9795         }
9796         this.fireEvent("beforerender", this, this.jsonData);
9797     },
9798
9799     onLoad : function(el, o){
9800         this.fireEvent("load", this, this.jsonData, o);
9801     },
9802
9803     onLoadException : function(el, o){
9804         this.fireEvent("loadexception", this, o);
9805     },
9806
9807 /**
9808  * Filter the data by a specific property.
9809  * @param {String} property A property on your JSON objects
9810  * @param {String/RegExp} value Either string that the property values
9811  * should start with, or a RegExp to test against the property
9812  */
9813     filter : function(property, value){
9814         if(this.jsonData){
9815             var data = [];
9816             var ss = this.snapshot;
9817             if(typeof value == "string"){
9818                 var vlen = value.length;
9819                 if(vlen == 0){
9820                     this.clearFilter();
9821                     return;
9822                 }
9823                 value = value.toLowerCase();
9824                 for(var i = 0, len = ss.length; i < len; i++){
9825                     var o = ss[i];
9826                     if(o[property].substr(0, vlen).toLowerCase() == value){
9827                         data.push(o);
9828                     }
9829                 }
9830             } else if(value.exec){ // regex?
9831                 for(var i = 0, len = ss.length; i < len; i++){
9832                     var o = ss[i];
9833                     if(value.test(o[property])){
9834                         data.push(o);
9835                     }
9836                 }
9837             } else{
9838                 return;
9839             }
9840             this.jsonData = data;
9841             this.refresh();
9842         }
9843     },
9844
9845 /**
9846  * Filter by a function. The passed function will be called with each
9847  * object in the current dataset. If the function returns true the value is kept,
9848  * otherwise it is filtered.
9849  * @param {Function} fn
9850  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9851  */
9852     filterBy : function(fn, scope){
9853         if(this.jsonData){
9854             var data = [];
9855             var ss = this.snapshot;
9856             for(var i = 0, len = ss.length; i < len; i++){
9857                 var o = ss[i];
9858                 if(fn.call(scope || this, o)){
9859                     data.push(o);
9860                 }
9861             }
9862             this.jsonData = data;
9863             this.refresh();
9864         }
9865     },
9866
9867 /**
9868  * Clears the current filter.
9869  */
9870     clearFilter : function(){
9871         if(this.snapshot && this.jsonData != this.snapshot){
9872             this.jsonData = this.snapshot;
9873             this.refresh();
9874         }
9875     },
9876
9877
9878 /**
9879  * Sorts the data for this view and refreshes it.
9880  * @param {String} property A property on your JSON objects to sort on
9881  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9882  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9883  */
9884     sort : function(property, dir, sortType){
9885         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9886         if(this.jsonData){
9887             var p = property;
9888             var dsc = dir && dir.toLowerCase() == "desc";
9889             var f = function(o1, o2){
9890                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9891                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9892                 ;
9893                 if(v1 < v2){
9894                     return dsc ? +1 : -1;
9895                 } else if(v1 > v2){
9896                     return dsc ? -1 : +1;
9897                 } else{
9898                     return 0;
9899                 }
9900             };
9901             this.jsonData.sort(f);
9902             this.refresh();
9903             if(this.jsonData != this.snapshot){
9904                 this.snapshot.sort(f);
9905             }
9906         }
9907     }
9908 });/*
9909  * Based on:
9910  * Ext JS Library 1.1.1
9911  * Copyright(c) 2006-2007, Ext JS, LLC.
9912  *
9913  * Originally Released Under LGPL - original licence link has changed is not relivant.
9914  *
9915  * Fork - LGPL
9916  * <script type="text/javascript">
9917  */
9918  
9919
9920 /**
9921  * @class Roo.ColorPalette
9922  * @extends Roo.Component
9923  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9924  * Here's an example of typical usage:
9925  * <pre><code>
9926 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9927 cp.render('my-div');
9928
9929 cp.on('select', function(palette, selColor){
9930     // do something with selColor
9931 });
9932 </code></pre>
9933  * @constructor
9934  * Create a new ColorPalette
9935  * @param {Object} config The config object
9936  */
9937 Roo.ColorPalette = function(config){
9938     Roo.ColorPalette.superclass.constructor.call(this, config);
9939     this.addEvents({
9940         /**
9941              * @event select
9942              * Fires when a color is selected
9943              * @param {ColorPalette} this
9944              * @param {String} color The 6-digit color hex code (without the # symbol)
9945              */
9946         select: true
9947     });
9948
9949     if(this.handler){
9950         this.on("select", this.handler, this.scope, true);
9951     }
9952 };
9953 Roo.extend(Roo.ColorPalette, Roo.Component, {
9954     /**
9955      * @cfg {String} itemCls
9956      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9957      */
9958     itemCls : "x-color-palette",
9959     /**
9960      * @cfg {String} value
9961      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9962      * the hex codes are case-sensitive.
9963      */
9964     value : null,
9965     clickEvent:'click',
9966     // private
9967     ctype: "Roo.ColorPalette",
9968
9969     /**
9970      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9971      */
9972     allowReselect : false,
9973
9974     /**
9975      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9976      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9977      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9978      * of colors with the width setting until the box is symmetrical.</p>
9979      * <p>You can override individual colors if needed:</p>
9980      * <pre><code>
9981 var cp = new Roo.ColorPalette();
9982 cp.colors[0] = "FF0000";  // change the first box to red
9983 </code></pre>
9984
9985 Or you can provide a custom array of your own for complete control:
9986 <pre><code>
9987 var cp = new Roo.ColorPalette();
9988 cp.colors = ["000000", "993300", "333300"];
9989 </code></pre>
9990      * @type Array
9991      */
9992     colors : [
9993         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9994         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9995         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9996         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9997         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9998     ],
9999
10000     // private
10001     onRender : function(container, position){
10002         var t = new Roo.MasterTemplate(
10003             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10004         );
10005         var c = this.colors;
10006         for(var i = 0, len = c.length; i < len; i++){
10007             t.add([c[i]]);
10008         }
10009         var el = document.createElement("div");
10010         el.className = this.itemCls;
10011         t.overwrite(el);
10012         container.dom.insertBefore(el, position);
10013         this.el = Roo.get(el);
10014         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10015         if(this.clickEvent != 'click'){
10016             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10017         }
10018     },
10019
10020     // private
10021     afterRender : function(){
10022         Roo.ColorPalette.superclass.afterRender.call(this);
10023         if(this.value){
10024             var s = this.value;
10025             this.value = null;
10026             this.select(s);
10027         }
10028     },
10029
10030     // private
10031     handleClick : function(e, t){
10032         e.preventDefault();
10033         if(!this.disabled){
10034             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10035             this.select(c.toUpperCase());
10036         }
10037     },
10038
10039     /**
10040      * Selects the specified color in the palette (fires the select event)
10041      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10042      */
10043     select : function(color){
10044         color = color.replace("#", "");
10045         if(color != this.value || this.allowReselect){
10046             var el = this.el;
10047             if(this.value){
10048                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10049             }
10050             el.child("a.color-"+color).addClass("x-color-palette-sel");
10051             this.value = color;
10052             this.fireEvent("select", this, color);
10053         }
10054     }
10055 });/*
10056  * Based on:
10057  * Ext JS Library 1.1.1
10058  * Copyright(c) 2006-2007, Ext JS, LLC.
10059  *
10060  * Originally Released Under LGPL - original licence link has changed is not relivant.
10061  *
10062  * Fork - LGPL
10063  * <script type="text/javascript">
10064  */
10065  
10066 /**
10067  * @class Roo.DatePicker
10068  * @extends Roo.Component
10069  * Simple date picker class.
10070  * @constructor
10071  * Create a new DatePicker
10072  * @param {Object} config The config object
10073  */
10074 Roo.DatePicker = function(config){
10075     Roo.DatePicker.superclass.constructor.call(this, config);
10076
10077     this.value = config && config.value ?
10078                  config.value.clearTime() : new Date().clearTime();
10079
10080     this.addEvents({
10081         /**
10082              * @event select
10083              * Fires when a date is selected
10084              * @param {DatePicker} this
10085              * @param {Date} date The selected date
10086              */
10087         select: true
10088     });
10089
10090     if(this.handler){
10091         this.on("select", this.handler,  this.scope || this);
10092     }
10093     // build the disabledDatesRE
10094     if(!this.disabledDatesRE && this.disabledDates){
10095         var dd = this.disabledDates;
10096         var re = "(?:";
10097         for(var i = 0; i < dd.length; i++){
10098             re += dd[i];
10099             if(i != dd.length-1) re += "|";
10100         }
10101         this.disabledDatesRE = new RegExp(re + ")");
10102     }
10103 };
10104
10105 Roo.extend(Roo.DatePicker, Roo.Component, {
10106     /**
10107      * @cfg {String} todayText
10108      * The text to display on the button that selects the current date (defaults to "Today")
10109      */
10110     todayText : "Today",
10111     /**
10112      * @cfg {String} okText
10113      * The text to display on the ok button
10114      */
10115     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10116     /**
10117      * @cfg {String} cancelText
10118      * The text to display on the cancel button
10119      */
10120     cancelText : "Cancel",
10121     /**
10122      * @cfg {String} todayTip
10123      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10124      */
10125     todayTip : "{0} (Spacebar)",
10126     /**
10127      * @cfg {Date} minDate
10128      * Minimum allowable date (JavaScript date object, defaults to null)
10129      */
10130     minDate : null,
10131     /**
10132      * @cfg {Date} maxDate
10133      * Maximum allowable date (JavaScript date object, defaults to null)
10134      */
10135     maxDate : null,
10136     /**
10137      * @cfg {String} minText
10138      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10139      */
10140     minText : "This date is before the minimum date",
10141     /**
10142      * @cfg {String} maxText
10143      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10144      */
10145     maxText : "This date is after the maximum date",
10146     /**
10147      * @cfg {String} format
10148      * The default date format string which can be overriden for localization support.  The format must be
10149      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10150      */
10151     format : "m/d/y",
10152     /**
10153      * @cfg {Array} disabledDays
10154      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10155      */
10156     disabledDays : null,
10157     /**
10158      * @cfg {String} disabledDaysText
10159      * The tooltip to display when the date falls on a disabled day (defaults to "")
10160      */
10161     disabledDaysText : "",
10162     /**
10163      * @cfg {RegExp} disabledDatesRE
10164      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10165      */
10166     disabledDatesRE : null,
10167     /**
10168      * @cfg {String} disabledDatesText
10169      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10170      */
10171     disabledDatesText : "",
10172     /**
10173      * @cfg {Boolean} constrainToViewport
10174      * True to constrain the date picker to the viewport (defaults to true)
10175      */
10176     constrainToViewport : true,
10177     /**
10178      * @cfg {Array} monthNames
10179      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10180      */
10181     monthNames : Date.monthNames,
10182     /**
10183      * @cfg {Array} dayNames
10184      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10185      */
10186     dayNames : Date.dayNames,
10187     /**
10188      * @cfg {String} nextText
10189      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10190      */
10191     nextText: 'Next Month (Control+Right)',
10192     /**
10193      * @cfg {String} prevText
10194      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10195      */
10196     prevText: 'Previous Month (Control+Left)',
10197     /**
10198      * @cfg {String} monthYearText
10199      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10200      */
10201     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10202     /**
10203      * @cfg {Number} startDay
10204      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10205      */
10206     startDay : 0,
10207     /**
10208      * @cfg {Bool} showClear
10209      * Show a clear button (usefull for date form elements that can be blank.)
10210      */
10211     
10212     showClear: false,
10213     
10214     /**
10215      * Sets the value of the date field
10216      * @param {Date} value The date to set
10217      */
10218     setValue : function(value){
10219         var old = this.value;
10220         this.value = value.clearTime(true);
10221         if(this.el){
10222             this.update(this.value);
10223         }
10224     },
10225
10226     /**
10227      * Gets the current selected value of the date field
10228      * @return {Date} The selected date
10229      */
10230     getValue : function(){
10231         return this.value;
10232     },
10233
10234     // private
10235     focus : function(){
10236         if(this.el){
10237             this.update(this.activeDate);
10238         }
10239     },
10240
10241     // private
10242     onRender : function(container, position){
10243         var m = [
10244              '<table cellspacing="0">',
10245                 '<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>',
10246                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10247         var dn = this.dayNames;
10248         for(var i = 0; i < 7; i++){
10249             var d = this.startDay+i;
10250             if(d > 6){
10251                 d = d-7;
10252             }
10253             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10254         }
10255         m[m.length] = "</tr></thead><tbody><tr>";
10256         for(var i = 0; i < 42; i++) {
10257             if(i % 7 == 0 && i != 0){
10258                 m[m.length] = "</tr><tr>";
10259             }
10260             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10261         }
10262         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10263             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10264
10265         var el = document.createElement("div");
10266         el.className = "x-date-picker";
10267         el.innerHTML = m.join("");
10268
10269         container.dom.insertBefore(el, position);
10270
10271         this.el = Roo.get(el);
10272         this.eventEl = Roo.get(el.firstChild);
10273
10274         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10275             handler: this.showPrevMonth,
10276             scope: this,
10277             preventDefault:true,
10278             stopDefault:true
10279         });
10280
10281         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10282             handler: this.showNextMonth,
10283             scope: this,
10284             preventDefault:true,
10285             stopDefault:true
10286         });
10287
10288         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10289
10290         this.monthPicker = this.el.down('div.x-date-mp');
10291         this.monthPicker.enableDisplayMode('block');
10292         
10293         var kn = new Roo.KeyNav(this.eventEl, {
10294             "left" : function(e){
10295                 e.ctrlKey ?
10296                     this.showPrevMonth() :
10297                     this.update(this.activeDate.add("d", -1));
10298             },
10299
10300             "right" : function(e){
10301                 e.ctrlKey ?
10302                     this.showNextMonth() :
10303                     this.update(this.activeDate.add("d", 1));
10304             },
10305
10306             "up" : function(e){
10307                 e.ctrlKey ?
10308                     this.showNextYear() :
10309                     this.update(this.activeDate.add("d", -7));
10310             },
10311
10312             "down" : function(e){
10313                 e.ctrlKey ?
10314                     this.showPrevYear() :
10315                     this.update(this.activeDate.add("d", 7));
10316             },
10317
10318             "pageUp" : function(e){
10319                 this.showNextMonth();
10320             },
10321
10322             "pageDown" : function(e){
10323                 this.showPrevMonth();
10324             },
10325
10326             "enter" : function(e){
10327                 e.stopPropagation();
10328                 return true;
10329             },
10330
10331             scope : this
10332         });
10333
10334         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10335
10336         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10337
10338         this.el.unselectable();
10339         
10340         this.cells = this.el.select("table.x-date-inner tbody td");
10341         this.textNodes = this.el.query("table.x-date-inner tbody span");
10342
10343         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10344             text: "&#160;",
10345             tooltip: this.monthYearText
10346         });
10347
10348         this.mbtn.on('click', this.showMonthPicker, this);
10349         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10350
10351
10352         var today = (new Date()).dateFormat(this.format);
10353         
10354         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10355         if (this.showClear) {
10356             baseTb.add( new Roo.Toolbar.Fill());
10357         }
10358         baseTb.add({
10359             text: String.format(this.todayText, today),
10360             tooltip: String.format(this.todayTip, today),
10361             handler: this.selectToday,
10362             scope: this
10363         });
10364         
10365         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10366             
10367         //});
10368         if (this.showClear) {
10369             
10370             baseTb.add( new Roo.Toolbar.Fill());
10371             baseTb.add({
10372                 text: '&#160;',
10373                 cls: 'x-btn-icon x-btn-clear',
10374                 handler: function() {
10375                     //this.value = '';
10376                     this.fireEvent("select", this, '');
10377                 },
10378                 scope: this
10379             });
10380         }
10381         
10382         
10383         if(Roo.isIE){
10384             this.el.repaint();
10385         }
10386         this.update(this.value);
10387     },
10388
10389     createMonthPicker : function(){
10390         if(!this.monthPicker.dom.firstChild){
10391             var buf = ['<table border="0" cellspacing="0">'];
10392             for(var i = 0; i < 6; i++){
10393                 buf.push(
10394                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10395                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10396                     i == 0 ?
10397                     '<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>' :
10398                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10399                 );
10400             }
10401             buf.push(
10402                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10403                     this.okText,
10404                     '</button><button type="button" class="x-date-mp-cancel">',
10405                     this.cancelText,
10406                     '</button></td></tr>',
10407                 '</table>'
10408             );
10409             this.monthPicker.update(buf.join(''));
10410             this.monthPicker.on('click', this.onMonthClick, this);
10411             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10412
10413             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10414             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10415
10416             this.mpMonths.each(function(m, a, i){
10417                 i += 1;
10418                 if((i%2) == 0){
10419                     m.dom.xmonth = 5 + Math.round(i * .5);
10420                 }else{
10421                     m.dom.xmonth = Math.round((i-1) * .5);
10422                 }
10423             });
10424         }
10425     },
10426
10427     showMonthPicker : function(){
10428         this.createMonthPicker();
10429         var size = this.el.getSize();
10430         this.monthPicker.setSize(size);
10431         this.monthPicker.child('table').setSize(size);
10432
10433         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10434         this.updateMPMonth(this.mpSelMonth);
10435         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10436         this.updateMPYear(this.mpSelYear);
10437
10438         this.monthPicker.slideIn('t', {duration:.2});
10439     },
10440
10441     updateMPYear : function(y){
10442         this.mpyear = y;
10443         var ys = this.mpYears.elements;
10444         for(var i = 1; i <= 10; i++){
10445             var td = ys[i-1], y2;
10446             if((i%2) == 0){
10447                 y2 = y + Math.round(i * .5);
10448                 td.firstChild.innerHTML = y2;
10449                 td.xyear = y2;
10450             }else{
10451                 y2 = y - (5-Math.round(i * .5));
10452                 td.firstChild.innerHTML = y2;
10453                 td.xyear = y2;
10454             }
10455             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10456         }
10457     },
10458
10459     updateMPMonth : function(sm){
10460         this.mpMonths.each(function(m, a, i){
10461             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10462         });
10463     },
10464
10465     selectMPMonth: function(m){
10466         
10467     },
10468
10469     onMonthClick : function(e, t){
10470         e.stopEvent();
10471         var el = new Roo.Element(t), pn;
10472         if(el.is('button.x-date-mp-cancel')){
10473             this.hideMonthPicker();
10474         }
10475         else if(el.is('button.x-date-mp-ok')){
10476             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10477             this.hideMonthPicker();
10478         }
10479         else if(pn = el.up('td.x-date-mp-month', 2)){
10480             this.mpMonths.removeClass('x-date-mp-sel');
10481             pn.addClass('x-date-mp-sel');
10482             this.mpSelMonth = pn.dom.xmonth;
10483         }
10484         else if(pn = el.up('td.x-date-mp-year', 2)){
10485             this.mpYears.removeClass('x-date-mp-sel');
10486             pn.addClass('x-date-mp-sel');
10487             this.mpSelYear = pn.dom.xyear;
10488         }
10489         else if(el.is('a.x-date-mp-prev')){
10490             this.updateMPYear(this.mpyear-10);
10491         }
10492         else if(el.is('a.x-date-mp-next')){
10493             this.updateMPYear(this.mpyear+10);
10494         }
10495     },
10496
10497     onMonthDblClick : function(e, t){
10498         e.stopEvent();
10499         var el = new Roo.Element(t), pn;
10500         if(pn = el.up('td.x-date-mp-month', 2)){
10501             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10502             this.hideMonthPicker();
10503         }
10504         else if(pn = el.up('td.x-date-mp-year', 2)){
10505             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10506             this.hideMonthPicker();
10507         }
10508     },
10509
10510     hideMonthPicker : function(disableAnim){
10511         if(this.monthPicker){
10512             if(disableAnim === true){
10513                 this.monthPicker.hide();
10514             }else{
10515                 this.monthPicker.slideOut('t', {duration:.2});
10516             }
10517         }
10518     },
10519
10520     // private
10521     showPrevMonth : function(e){
10522         this.update(this.activeDate.add("mo", -1));
10523     },
10524
10525     // private
10526     showNextMonth : function(e){
10527         this.update(this.activeDate.add("mo", 1));
10528     },
10529
10530     // private
10531     showPrevYear : function(){
10532         this.update(this.activeDate.add("y", -1));
10533     },
10534
10535     // private
10536     showNextYear : function(){
10537         this.update(this.activeDate.add("y", 1));
10538     },
10539
10540     // private
10541     handleMouseWheel : function(e){
10542         var delta = e.getWheelDelta();
10543         if(delta > 0){
10544             this.showPrevMonth();
10545             e.stopEvent();
10546         } else if(delta < 0){
10547             this.showNextMonth();
10548             e.stopEvent();
10549         }
10550     },
10551
10552     // private
10553     handleDateClick : function(e, t){
10554         e.stopEvent();
10555         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10556             this.setValue(new Date(t.dateValue));
10557             this.fireEvent("select", this, this.value);
10558         }
10559     },
10560
10561     // private
10562     selectToday : function(){
10563         this.setValue(new Date().clearTime());
10564         this.fireEvent("select", this, this.value);
10565     },
10566
10567     // private
10568     update : function(date){
10569         var vd = this.activeDate;
10570         this.activeDate = date;
10571         if(vd && this.el){
10572             var t = date.getTime();
10573             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10574                 this.cells.removeClass("x-date-selected");
10575                 this.cells.each(function(c){
10576                    if(c.dom.firstChild.dateValue == t){
10577                        c.addClass("x-date-selected");
10578                        setTimeout(function(){
10579                             try{c.dom.firstChild.focus();}catch(e){}
10580                        }, 50);
10581                        return false;
10582                    }
10583                 });
10584                 return;
10585             }
10586         }
10587         var days = date.getDaysInMonth();
10588         var firstOfMonth = date.getFirstDateOfMonth();
10589         var startingPos = firstOfMonth.getDay()-this.startDay;
10590
10591         if(startingPos <= this.startDay){
10592             startingPos += 7;
10593         }
10594
10595         var pm = date.add("mo", -1);
10596         var prevStart = pm.getDaysInMonth()-startingPos;
10597
10598         var cells = this.cells.elements;
10599         var textEls = this.textNodes;
10600         days += startingPos;
10601
10602         // convert everything to numbers so it's fast
10603         var day = 86400000;
10604         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10605         var today = new Date().clearTime().getTime();
10606         var sel = date.clearTime().getTime();
10607         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10608         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10609         var ddMatch = this.disabledDatesRE;
10610         var ddText = this.disabledDatesText;
10611         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10612         var ddaysText = this.disabledDaysText;
10613         var format = this.format;
10614
10615         var setCellClass = function(cal, cell){
10616             cell.title = "";
10617             var t = d.getTime();
10618             cell.firstChild.dateValue = t;
10619             if(t == today){
10620                 cell.className += " x-date-today";
10621                 cell.title = cal.todayText;
10622             }
10623             if(t == sel){
10624                 cell.className += " x-date-selected";
10625                 setTimeout(function(){
10626                     try{cell.firstChild.focus();}catch(e){}
10627                 }, 50);
10628             }
10629             // disabling
10630             if(t < min) {
10631                 cell.className = " x-date-disabled";
10632                 cell.title = cal.minText;
10633                 return;
10634             }
10635             if(t > max) {
10636                 cell.className = " x-date-disabled";
10637                 cell.title = cal.maxText;
10638                 return;
10639             }
10640             if(ddays){
10641                 if(ddays.indexOf(d.getDay()) != -1){
10642                     cell.title = ddaysText;
10643                     cell.className = " x-date-disabled";
10644                 }
10645             }
10646             if(ddMatch && format){
10647                 var fvalue = d.dateFormat(format);
10648                 if(ddMatch.test(fvalue)){
10649                     cell.title = ddText.replace("%0", fvalue);
10650                     cell.className = " x-date-disabled";
10651                 }
10652             }
10653         };
10654
10655         var i = 0;
10656         for(; i < startingPos; i++) {
10657             textEls[i].innerHTML = (++prevStart);
10658             d.setDate(d.getDate()+1);
10659             cells[i].className = "x-date-prevday";
10660             setCellClass(this, cells[i]);
10661         }
10662         for(; i < days; i++){
10663             intDay = i - startingPos + 1;
10664             textEls[i].innerHTML = (intDay);
10665             d.setDate(d.getDate()+1);
10666             cells[i].className = "x-date-active";
10667             setCellClass(this, cells[i]);
10668         }
10669         var extraDays = 0;
10670         for(; i < 42; i++) {
10671              textEls[i].innerHTML = (++extraDays);
10672              d.setDate(d.getDate()+1);
10673              cells[i].className = "x-date-nextday";
10674              setCellClass(this, cells[i]);
10675         }
10676
10677         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10678
10679         if(!this.internalRender){
10680             var main = this.el.dom.firstChild;
10681             var w = main.offsetWidth;
10682             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10683             Roo.fly(main).setWidth(w);
10684             this.internalRender = true;
10685             // opera does not respect the auto grow header center column
10686             // then, after it gets a width opera refuses to recalculate
10687             // without a second pass
10688             if(Roo.isOpera && !this.secondPass){
10689                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10690                 this.secondPass = true;
10691                 this.update.defer(10, this, [date]);
10692             }
10693         }
10694     }
10695 });        /*
10696  * Based on:
10697  * Ext JS Library 1.1.1
10698  * Copyright(c) 2006-2007, Ext JS, LLC.
10699  *
10700  * Originally Released Under LGPL - original licence link has changed is not relivant.
10701  *
10702  * Fork - LGPL
10703  * <script type="text/javascript">
10704  */
10705 /**
10706  * @class Roo.TabPanel
10707  * @extends Roo.util.Observable
10708  * A lightweight tab container.
10709  * <br><br>
10710  * Usage:
10711  * <pre><code>
10712 // basic tabs 1, built from existing content
10713 var tabs = new Roo.TabPanel("tabs1");
10714 tabs.addTab("script", "View Script");
10715 tabs.addTab("markup", "View Markup");
10716 tabs.activate("script");
10717
10718 // more advanced tabs, built from javascript
10719 var jtabs = new Roo.TabPanel("jtabs");
10720 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10721
10722 // set up the UpdateManager
10723 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10724 var updater = tab2.getUpdateManager();
10725 updater.setDefaultUrl("ajax1.htm");
10726 tab2.on('activate', updater.refresh, updater, true);
10727
10728 // Use setUrl for Ajax loading
10729 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10730 tab3.setUrl("ajax2.htm", null, true);
10731
10732 // Disabled tab
10733 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10734 tab4.disable();
10735
10736 jtabs.activate("jtabs-1");
10737  * </code></pre>
10738  * @constructor
10739  * Create a new TabPanel.
10740  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10741  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10742  */
10743 Roo.TabPanel = function(container, config){
10744     /**
10745     * The container element for this TabPanel.
10746     * @type Roo.Element
10747     */
10748     this.el = Roo.get(container, true);
10749     if(config){
10750         if(typeof config == "boolean"){
10751             this.tabPosition = config ? "bottom" : "top";
10752         }else{
10753             Roo.apply(this, config);
10754         }
10755     }
10756     if(this.tabPosition == "bottom"){
10757         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10758         this.el.addClass("x-tabs-bottom");
10759     }
10760     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10761     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10762     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10763     if(Roo.isIE){
10764         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10765     }
10766     if(this.tabPosition != "bottom"){
10767         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10768          * @type Roo.Element
10769          */
10770         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10771         this.el.addClass("x-tabs-top");
10772     }
10773     this.items = [];
10774
10775     this.bodyEl.setStyle("position", "relative");
10776
10777     this.active = null;
10778     this.activateDelegate = this.activate.createDelegate(this);
10779
10780     this.addEvents({
10781         /**
10782          * @event tabchange
10783          * Fires when the active tab changes
10784          * @param {Roo.TabPanel} this
10785          * @param {Roo.TabPanelItem} activePanel The new active tab
10786          */
10787         "tabchange": true,
10788         /**
10789          * @event beforetabchange
10790          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10791          * @param {Roo.TabPanel} this
10792          * @param {Object} e Set cancel to true on this object to cancel the tab change
10793          * @param {Roo.TabPanelItem} tab The tab being changed to
10794          */
10795         "beforetabchange" : true
10796     });
10797
10798     Roo.EventManager.onWindowResize(this.onResize, this);
10799     this.cpad = this.el.getPadding("lr");
10800     this.hiddenCount = 0;
10801
10802
10803     // toolbar on the tabbar support...
10804     if (this.toolbar) {
10805         var tcfg = this.toolbar;
10806         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10807         this.toolbar = new Roo.Toolbar(tcfg);
10808         if (Roo.isSafari) {
10809             var tbl = tcfg.container.child('table', true);
10810             tbl.setAttribute('width', '100%');
10811         }
10812         
10813     }
10814    
10815
10816
10817     Roo.TabPanel.superclass.constructor.call(this);
10818 };
10819
10820 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10821     /*
10822      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10823      */
10824     tabPosition : "top",
10825     /*
10826      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10827      */
10828     currentTabWidth : 0,
10829     /*
10830      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10831      */
10832     minTabWidth : 40,
10833     /*
10834      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10835      */
10836     maxTabWidth : 250,
10837     /*
10838      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10839      */
10840     preferredTabWidth : 175,
10841     /*
10842      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10843      */
10844     resizeTabs : false,
10845     /*
10846      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10847      */
10848     monitorResize : true,
10849     /*
10850      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10851      */
10852     toolbar : false,
10853
10854     /**
10855      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10856      * @param {String} id The id of the div to use <b>or create</b>
10857      * @param {String} text The text for the tab
10858      * @param {String} content (optional) Content to put in the TabPanelItem body
10859      * @param {Boolean} closable (optional) True to create a close icon on the tab
10860      * @return {Roo.TabPanelItem} The created TabPanelItem
10861      */
10862     addTab : function(id, text, content, closable){
10863         var item = new Roo.TabPanelItem(this, id, text, closable);
10864         this.addTabItem(item);
10865         if(content){
10866             item.setContent(content);
10867         }
10868         return item;
10869     },
10870
10871     /**
10872      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10873      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10874      * @return {Roo.TabPanelItem}
10875      */
10876     getTab : function(id){
10877         return this.items[id];
10878     },
10879
10880     /**
10881      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10882      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10883      */
10884     hideTab : function(id){
10885         var t = this.items[id];
10886         if(!t.isHidden()){
10887            t.setHidden(true);
10888            this.hiddenCount++;
10889            this.autoSizeTabs();
10890         }
10891     },
10892
10893     /**
10894      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10895      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10896      */
10897     unhideTab : function(id){
10898         var t = this.items[id];
10899         if(t.isHidden()){
10900            t.setHidden(false);
10901            this.hiddenCount--;
10902            this.autoSizeTabs();
10903         }
10904     },
10905
10906     /**
10907      * Adds an existing {@link Roo.TabPanelItem}.
10908      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10909      */
10910     addTabItem : function(item){
10911         this.items[item.id] = item;
10912         this.items.push(item);
10913         if(this.resizeTabs){
10914            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10915            this.autoSizeTabs();
10916         }else{
10917             item.autoSize();
10918         }
10919     },
10920
10921     /**
10922      * Removes a {@link Roo.TabPanelItem}.
10923      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10924      */
10925     removeTab : function(id){
10926         var items = this.items;
10927         var tab = items[id];
10928         if(!tab) { return; }
10929         var index = items.indexOf(tab);
10930         if(this.active == tab && items.length > 1){
10931             var newTab = this.getNextAvailable(index);
10932             if(newTab) {
10933                 newTab.activate();
10934             }
10935         }
10936         this.stripEl.dom.removeChild(tab.pnode.dom);
10937         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10938             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10939         }
10940         items.splice(index, 1);
10941         delete this.items[tab.id];
10942         tab.fireEvent("close", tab);
10943         tab.purgeListeners();
10944         this.autoSizeTabs();
10945     },
10946
10947     getNextAvailable : function(start){
10948         var items = this.items;
10949         var index = start;
10950         // look for a next tab that will slide over to
10951         // replace the one being removed
10952         while(index < items.length){
10953             var item = items[++index];
10954             if(item && !item.isHidden()){
10955                 return item;
10956             }
10957         }
10958         // if one isn't found select the previous tab (on the left)
10959         index = start;
10960         while(index >= 0){
10961             var item = items[--index];
10962             if(item && !item.isHidden()){
10963                 return item;
10964             }
10965         }
10966         return null;
10967     },
10968
10969     /**
10970      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10971      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10972      */
10973     disableTab : function(id){
10974         var tab = this.items[id];
10975         if(tab && this.active != tab){
10976             tab.disable();
10977         }
10978     },
10979
10980     /**
10981      * Enables a {@link Roo.TabPanelItem} that is disabled.
10982      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10983      */
10984     enableTab : function(id){
10985         var tab = this.items[id];
10986         tab.enable();
10987     },
10988
10989     /**
10990      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10991      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10992      * @return {Roo.TabPanelItem} The TabPanelItem.
10993      */
10994     activate : function(id){
10995         var tab = this.items[id];
10996         if(!tab){
10997             return null;
10998         }
10999         if(tab == this.active || tab.disabled){
11000             return tab;
11001         }
11002         var e = {};
11003         this.fireEvent("beforetabchange", this, e, tab);
11004         if(e.cancel !== true && !tab.disabled){
11005             if(this.active){
11006                 this.active.hide();
11007             }
11008             this.active = this.items[id];
11009             this.active.show();
11010             this.fireEvent("tabchange", this, this.active);
11011         }
11012         return tab;
11013     },
11014
11015     /**
11016      * Gets the active {@link Roo.TabPanelItem}.
11017      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11018      */
11019     getActiveTab : function(){
11020         return this.active;
11021     },
11022
11023     /**
11024      * Updates the tab body element to fit the height of the container element
11025      * for overflow scrolling
11026      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11027      */
11028     syncHeight : function(targetHeight){
11029         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11030         var bm = this.bodyEl.getMargins();
11031         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11032         this.bodyEl.setHeight(newHeight);
11033         return newHeight;
11034     },
11035
11036     onResize : function(){
11037         if(this.monitorResize){
11038             this.autoSizeTabs();
11039         }
11040     },
11041
11042     /**
11043      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11044      */
11045     beginUpdate : function(){
11046         this.updating = true;
11047     },
11048
11049     /**
11050      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11051      */
11052     endUpdate : function(){
11053         this.updating = false;
11054         this.autoSizeTabs();
11055     },
11056
11057     /**
11058      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11059      */
11060     autoSizeTabs : function(){
11061         var count = this.items.length;
11062         var vcount = count - this.hiddenCount;
11063         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11064         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11065         var availWidth = Math.floor(w / vcount);
11066         var b = this.stripBody;
11067         if(b.getWidth() > w){
11068             var tabs = this.items;
11069             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11070             if(availWidth < this.minTabWidth){
11071                 /*if(!this.sleft){    // incomplete scrolling code
11072                     this.createScrollButtons();
11073                 }
11074                 this.showScroll();
11075                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11076             }
11077         }else{
11078             if(this.currentTabWidth < this.preferredTabWidth){
11079                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11080             }
11081         }
11082     },
11083
11084     /**
11085      * Returns the number of tabs in this TabPanel.
11086      * @return {Number}
11087      */
11088      getCount : function(){
11089          return this.items.length;
11090      },
11091
11092     /**
11093      * Resizes all the tabs to the passed width
11094      * @param {Number} The new width
11095      */
11096     setTabWidth : function(width){
11097         this.currentTabWidth = width;
11098         for(var i = 0, len = this.items.length; i < len; i++) {
11099                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11100         }
11101     },
11102
11103     /**
11104      * Destroys this TabPanel
11105      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11106      */
11107     destroy : function(removeEl){
11108         Roo.EventManager.removeResizeListener(this.onResize, this);
11109         for(var i = 0, len = this.items.length; i < len; i++){
11110             this.items[i].purgeListeners();
11111         }
11112         if(removeEl === true){
11113             this.el.update("");
11114             this.el.remove();
11115         }
11116     }
11117 });
11118
11119 /**
11120  * @class Roo.TabPanelItem
11121  * @extends Roo.util.Observable
11122  * Represents an individual item (tab plus body) in a TabPanel.
11123  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11124  * @param {String} id The id of this TabPanelItem
11125  * @param {String} text The text for the tab of this TabPanelItem
11126  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11127  */
11128 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11129     /**
11130      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11131      * @type Roo.TabPanel
11132      */
11133     this.tabPanel = tabPanel;
11134     /**
11135      * The id for this TabPanelItem
11136      * @type String
11137      */
11138     this.id = id;
11139     /** @private */
11140     this.disabled = false;
11141     /** @private */
11142     this.text = text;
11143     /** @private */
11144     this.loaded = false;
11145     this.closable = closable;
11146
11147     /**
11148      * The body element for this TabPanelItem.
11149      * @type Roo.Element
11150      */
11151     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11152     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11153     this.bodyEl.setStyle("display", "block");
11154     this.bodyEl.setStyle("zoom", "1");
11155     this.hideAction();
11156
11157     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11158     /** @private */
11159     this.el = Roo.get(els.el, true);
11160     this.inner = Roo.get(els.inner, true);
11161     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11162     this.pnode = Roo.get(els.el.parentNode, true);
11163     this.el.on("mousedown", this.onTabMouseDown, this);
11164     this.el.on("click", this.onTabClick, this);
11165     /** @private */
11166     if(closable){
11167         var c = Roo.get(els.close, true);
11168         c.dom.title = this.closeText;
11169         c.addClassOnOver("close-over");
11170         c.on("click", this.closeClick, this);
11171      }
11172
11173     this.addEvents({
11174          /**
11175          * @event activate
11176          * Fires when this tab becomes the active tab.
11177          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11178          * @param {Roo.TabPanelItem} this
11179          */
11180         "activate": true,
11181         /**
11182          * @event beforeclose
11183          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11184          * @param {Roo.TabPanelItem} this
11185          * @param {Object} e Set cancel to true on this object to cancel the close.
11186          */
11187         "beforeclose": true,
11188         /**
11189          * @event close
11190          * Fires when this tab is closed.
11191          * @param {Roo.TabPanelItem} this
11192          */
11193          "close": true,
11194         /**
11195          * @event deactivate
11196          * Fires when this tab is no longer the active tab.
11197          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11198          * @param {Roo.TabPanelItem} this
11199          */
11200          "deactivate" : true
11201     });
11202     this.hidden = false;
11203
11204     Roo.TabPanelItem.superclass.constructor.call(this);
11205 };
11206
11207 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11208     purgeListeners : function(){
11209        Roo.util.Observable.prototype.purgeListeners.call(this);
11210        this.el.removeAllListeners();
11211     },
11212     /**
11213      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11214      */
11215     show : function(){
11216         this.pnode.addClass("on");
11217         this.showAction();
11218         if(Roo.isOpera){
11219             this.tabPanel.stripWrap.repaint();
11220         }
11221         this.fireEvent("activate", this.tabPanel, this);
11222     },
11223
11224     /**
11225      * Returns true if this tab is the active tab.
11226      * @return {Boolean}
11227      */
11228     isActive : function(){
11229         return this.tabPanel.getActiveTab() == this;
11230     },
11231
11232     /**
11233      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11234      */
11235     hide : function(){
11236         this.pnode.removeClass("on");
11237         this.hideAction();
11238         this.fireEvent("deactivate", this.tabPanel, this);
11239     },
11240
11241     hideAction : function(){
11242         this.bodyEl.hide();
11243         this.bodyEl.setStyle("position", "absolute");
11244         this.bodyEl.setLeft("-20000px");
11245         this.bodyEl.setTop("-20000px");
11246     },
11247
11248     showAction : function(){
11249         this.bodyEl.setStyle("position", "relative");
11250         this.bodyEl.setTop("");
11251         this.bodyEl.setLeft("");
11252         this.bodyEl.show();
11253     },
11254
11255     /**
11256      * Set the tooltip for the tab.
11257      * @param {String} tooltip The tab's tooltip
11258      */
11259     setTooltip : function(text){
11260         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11261             this.textEl.dom.qtip = text;
11262             this.textEl.dom.removeAttribute('title');
11263         }else{
11264             this.textEl.dom.title = text;
11265         }
11266     },
11267
11268     onTabClick : function(e){
11269         e.preventDefault();
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     onTabMouseDown : function(e){
11274         e.preventDefault();
11275         this.tabPanel.activate(this.id);
11276     },
11277
11278     getWidth : function(){
11279         return this.inner.getWidth();
11280     },
11281
11282     setWidth : function(width){
11283         var iwidth = width - this.pnode.getPadding("lr");
11284         this.inner.setWidth(iwidth);
11285         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11286         this.pnode.setWidth(width);
11287     },
11288
11289     /**
11290      * Show or hide the tab
11291      * @param {Boolean} hidden True to hide or false to show.
11292      */
11293     setHidden : function(hidden){
11294         this.hidden = hidden;
11295         this.pnode.setStyle("display", hidden ? "none" : "");
11296     },
11297
11298     /**
11299      * Returns true if this tab is "hidden"
11300      * @return {Boolean}
11301      */
11302     isHidden : function(){
11303         return this.hidden;
11304     },
11305
11306     /**
11307      * Returns the text for this tab
11308      * @return {String}
11309      */
11310     getText : function(){
11311         return this.text;
11312     },
11313
11314     autoSize : function(){
11315         //this.el.beginMeasure();
11316         this.textEl.setWidth(1);
11317         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11318         //this.el.endMeasure();
11319     },
11320
11321     /**
11322      * Sets the text for the tab (Note: this also sets the tooltip text)
11323      * @param {String} text The tab's text and tooltip
11324      */
11325     setText : function(text){
11326         this.text = text;
11327         this.textEl.update(text);
11328         this.setTooltip(text);
11329         if(!this.tabPanel.resizeTabs){
11330             this.autoSize();
11331         }
11332     },
11333     /**
11334      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11335      */
11336     activate : function(){
11337         this.tabPanel.activate(this.id);
11338     },
11339
11340     /**
11341      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11342      */
11343     disable : function(){
11344         if(this.tabPanel.active != this){
11345             this.disabled = true;
11346             this.pnode.addClass("disabled");
11347         }
11348     },
11349
11350     /**
11351      * Enables this TabPanelItem if it was previously disabled.
11352      */
11353     enable : function(){
11354         this.disabled = false;
11355         this.pnode.removeClass("disabled");
11356     },
11357
11358     /**
11359      * Sets the content for this TabPanelItem.
11360      * @param {String} content The content
11361      * @param {Boolean} loadScripts true to look for and load scripts
11362      */
11363     setContent : function(content, loadScripts){
11364         this.bodyEl.update(content, loadScripts);
11365     },
11366
11367     /**
11368      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11369      * @return {Roo.UpdateManager} The UpdateManager
11370      */
11371     getUpdateManager : function(){
11372         return this.bodyEl.getUpdateManager();
11373     },
11374
11375     /**
11376      * Set a URL to be used to load the content for this TabPanelItem.
11377      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11378      * @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)
11379      * @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)
11380      * @return {Roo.UpdateManager} The UpdateManager
11381      */
11382     setUrl : function(url, params, loadOnce){
11383         if(this.refreshDelegate){
11384             this.un('activate', this.refreshDelegate);
11385         }
11386         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11387         this.on("activate", this.refreshDelegate);
11388         return this.bodyEl.getUpdateManager();
11389     },
11390
11391     /** @private */
11392     _handleRefresh : function(url, params, loadOnce){
11393         if(!loadOnce || !this.loaded){
11394             var updater = this.bodyEl.getUpdateManager();
11395             updater.update(url, params, this._setLoaded.createDelegate(this));
11396         }
11397     },
11398
11399     /**
11400      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11401      *   Will fail silently if the setUrl method has not been called.
11402      *   This does not activate the panel, just updates its content.
11403      */
11404     refresh : function(){
11405         if(this.refreshDelegate){
11406            this.loaded = false;
11407            this.refreshDelegate();
11408         }
11409     },
11410
11411     /** @private */
11412     _setLoaded : function(){
11413         this.loaded = true;
11414     },
11415
11416     /** @private */
11417     closeClick : function(e){
11418         var o = {};
11419         e.stopEvent();
11420         this.fireEvent("beforeclose", this, o);
11421         if(o.cancel !== true){
11422             this.tabPanel.removeTab(this.id);
11423         }
11424     },
11425     /**
11426      * The text displayed in the tooltip for the close icon.
11427      * @type String
11428      */
11429     closeText : "Close this tab"
11430 });
11431
11432 /** @private */
11433 Roo.TabPanel.prototype.createStrip = function(container){
11434     var strip = document.createElement("div");
11435     strip.className = "x-tabs-wrap";
11436     container.appendChild(strip);
11437     return strip;
11438 };
11439 /** @private */
11440 Roo.TabPanel.prototype.createStripList = function(strip){
11441     // div wrapper for retard IE
11442     // returns the "tr" element.
11443     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11444         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11445         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11446     return strip.firstChild.firstChild.firstChild.firstChild;
11447 };
11448 /** @private */
11449 Roo.TabPanel.prototype.createBody = function(container){
11450     var body = document.createElement("div");
11451     Roo.id(body, "tab-body");
11452     Roo.fly(body).addClass("x-tabs-body");
11453     container.appendChild(body);
11454     return body;
11455 };
11456 /** @private */
11457 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11458     var body = Roo.getDom(id);
11459     if(!body){
11460         body = document.createElement("div");
11461         body.id = id;
11462     }
11463     Roo.fly(body).addClass("x-tabs-item-body");
11464     bodyEl.insertBefore(body, bodyEl.firstChild);
11465     return body;
11466 };
11467 /** @private */
11468 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11469     var td = document.createElement("td");
11470     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11471     //stripEl.appendChild(td);
11472     if(closable){
11473         td.className = "x-tabs-closable";
11474         if(!this.closeTpl){
11475             this.closeTpl = new Roo.Template(
11476                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11477                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11478                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11479             );
11480         }
11481         var el = this.closeTpl.overwrite(td, {"text": text});
11482         var close = el.getElementsByTagName("div")[0];
11483         var inner = el.getElementsByTagName("em")[0];
11484         return {"el": el, "close": close, "inner": inner};
11485     } else {
11486         if(!this.tabTpl){
11487             this.tabTpl = new Roo.Template(
11488                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11489                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11490             );
11491         }
11492         var el = this.tabTpl.overwrite(td, {"text": text});
11493         var inner = el.getElementsByTagName("em")[0];
11494         return {"el": el, "inner": inner};
11495     }
11496 };/*
11497  * Based on:
11498  * Ext JS Library 1.1.1
11499  * Copyright(c) 2006-2007, Ext JS, LLC.
11500  *
11501  * Originally Released Under LGPL - original licence link has changed is not relivant.
11502  *
11503  * Fork - LGPL
11504  * <script type="text/javascript">
11505  */
11506
11507 /**
11508  * @class Roo.Button
11509  * @extends Roo.util.Observable
11510  * Simple Button class
11511  * @cfg {String} text The button text
11512  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11513  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11514  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11515  * @cfg {Object} scope The scope of the handler
11516  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11517  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11518  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11519  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11520  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11521  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11522    applies if enableToggle = true)
11523  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11524  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11525   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11526  * @constructor
11527  * Create a new button
11528  * @param {Object} config The config object
11529  */
11530 Roo.Button = function(renderTo, config)
11531 {
11532     if (!config) {
11533         config = renderTo;
11534         renderTo = config.renderTo || false;
11535     }
11536     
11537     Roo.apply(this, config);
11538     this.addEvents({
11539         /**
11540              * @event click
11541              * Fires when this button is clicked
11542              * @param {Button} this
11543              * @param {EventObject} e The click event
11544              */
11545             "click" : true,
11546         /**
11547              * @event toggle
11548              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11549              * @param {Button} this
11550              * @param {Boolean} pressed
11551              */
11552             "toggle" : true,
11553         /**
11554              * @event mouseover
11555              * Fires when the mouse hovers over the button
11556              * @param {Button} this
11557              * @param {Event} e The event object
11558              */
11559         'mouseover' : true,
11560         /**
11561              * @event mouseout
11562              * Fires when the mouse exits the button
11563              * @param {Button} this
11564              * @param {Event} e The event object
11565              */
11566         'mouseout': true,
11567          /**
11568              * @event render
11569              * Fires when the button is rendered
11570              * @param {Button} this
11571              */
11572         'render': true
11573     });
11574     if(this.menu){
11575         this.menu = Roo.menu.MenuMgr.get(this.menu);
11576     }
11577     // register listeners first!!  - so render can be captured..
11578     Roo.util.Observable.call(this);
11579     if(renderTo){
11580         this.render(renderTo);
11581     }
11582     
11583   
11584 };
11585
11586 Roo.extend(Roo.Button, Roo.util.Observable, {
11587     /**
11588      * 
11589      */
11590     
11591     /**
11592      * Read-only. True if this button is hidden
11593      * @type Boolean
11594      */
11595     hidden : false,
11596     /**
11597      * Read-only. True if this button is disabled
11598      * @type Boolean
11599      */
11600     disabled : false,
11601     /**
11602      * Read-only. True if this button is pressed (only if enableToggle = true)
11603      * @type Boolean
11604      */
11605     pressed : false,
11606
11607     /**
11608      * @cfg {Number} tabIndex 
11609      * The DOM tabIndex for this button (defaults to undefined)
11610      */
11611     tabIndex : undefined,
11612
11613     /**
11614      * @cfg {Boolean} enableToggle
11615      * True to enable pressed/not pressed toggling (defaults to false)
11616      */
11617     enableToggle: false,
11618     /**
11619      * @cfg {Mixed} menu
11620      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11621      */
11622     menu : undefined,
11623     /**
11624      * @cfg {String} menuAlign
11625      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11626      */
11627     menuAlign : "tl-bl?",
11628
11629     /**
11630      * @cfg {String} iconCls
11631      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11632      */
11633     iconCls : undefined,
11634     /**
11635      * @cfg {String} type
11636      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11637      */
11638     type : 'button',
11639
11640     // private
11641     menuClassTarget: 'tr',
11642
11643     /**
11644      * @cfg {String} clickEvent
11645      * The type of event to map to the button's event handler (defaults to 'click')
11646      */
11647     clickEvent : 'click',
11648
11649     /**
11650      * @cfg {Boolean} handleMouseEvents
11651      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11652      */
11653     handleMouseEvents : true,
11654
11655     /**
11656      * @cfg {String} tooltipType
11657      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11658      */
11659     tooltipType : 'qtip',
11660
11661     /**
11662      * @cfg {String} cls
11663      * A CSS class to apply to the button's main element.
11664      */
11665     
11666     /**
11667      * @cfg {Roo.Template} template (Optional)
11668      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11669      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11670      * require code modifications if required elements (e.g. a button) aren't present.
11671      */
11672
11673     // private
11674     render : function(renderTo){
11675         var btn;
11676         if(this.hideParent){
11677             this.parentEl = Roo.get(renderTo);
11678         }
11679         if(!this.dhconfig){
11680             if(!this.template){
11681                 if(!Roo.Button.buttonTemplate){
11682                     // hideous table template
11683                     Roo.Button.buttonTemplate = new Roo.Template(
11684                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11685                         '<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>',
11686                         "</tr></tbody></table>");
11687                 }
11688                 this.template = Roo.Button.buttonTemplate;
11689             }
11690             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11691             var btnEl = btn.child("button:first");
11692             btnEl.on('focus', this.onFocus, this);
11693             btnEl.on('blur', this.onBlur, this);
11694             if(this.cls){
11695                 btn.addClass(this.cls);
11696             }
11697             if(this.icon){
11698                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11699             }
11700             if(this.iconCls){
11701                 btnEl.addClass(this.iconCls);
11702                 if(!this.cls){
11703                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11704                 }
11705             }
11706             if(this.tabIndex !== undefined){
11707                 btnEl.dom.tabIndex = this.tabIndex;
11708             }
11709             if(this.tooltip){
11710                 if(typeof this.tooltip == 'object'){
11711                     Roo.QuickTips.tips(Roo.apply({
11712                           target: btnEl.id
11713                     }, this.tooltip));
11714                 } else {
11715                     btnEl.dom[this.tooltipType] = this.tooltip;
11716                 }
11717             }
11718         }else{
11719             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11720         }
11721         this.el = btn;
11722         if(this.id){
11723             this.el.dom.id = this.el.id = this.id;
11724         }
11725         if(this.menu){
11726             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11727             this.menu.on("show", this.onMenuShow, this);
11728             this.menu.on("hide", this.onMenuHide, this);
11729         }
11730         btn.addClass("x-btn");
11731         if(Roo.isIE && !Roo.isIE7){
11732             this.autoWidth.defer(1, this);
11733         }else{
11734             this.autoWidth();
11735         }
11736         if(this.handleMouseEvents){
11737             btn.on("mouseover", this.onMouseOver, this);
11738             btn.on("mouseout", this.onMouseOut, this);
11739             btn.on("mousedown", this.onMouseDown, this);
11740         }
11741         btn.on(this.clickEvent, this.onClick, this);
11742         //btn.on("mouseup", this.onMouseUp, this);
11743         if(this.hidden){
11744             this.hide();
11745         }
11746         if(this.disabled){
11747             this.disable();
11748         }
11749         Roo.ButtonToggleMgr.register(this);
11750         if(this.pressed){
11751             this.el.addClass("x-btn-pressed");
11752         }
11753         if(this.repeat){
11754             var repeater = new Roo.util.ClickRepeater(btn,
11755                 typeof this.repeat == "object" ? this.repeat : {}
11756             );
11757             repeater.on("click", this.onClick,  this);
11758         }
11759         
11760         this.fireEvent('render', this);
11761         
11762     },
11763     /**
11764      * Returns the button's underlying element
11765      * @return {Roo.Element} The element
11766      */
11767     getEl : function(){
11768         return this.el;  
11769     },
11770     
11771     /**
11772      * Destroys this Button and removes any listeners.
11773      */
11774     destroy : function(){
11775         Roo.ButtonToggleMgr.unregister(this);
11776         this.el.removeAllListeners();
11777         this.purgeListeners();
11778         this.el.remove();
11779     },
11780
11781     // private
11782     autoWidth : function(){
11783         if(this.el){
11784             this.el.setWidth("auto");
11785             if(Roo.isIE7 && Roo.isStrict){
11786                 var ib = this.el.child('button');
11787                 if(ib && ib.getWidth() > 20){
11788                     ib.clip();
11789                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11790                 }
11791             }
11792             if(this.minWidth){
11793                 if(this.hidden){
11794                     this.el.beginMeasure();
11795                 }
11796                 if(this.el.getWidth() < this.minWidth){
11797                     this.el.setWidth(this.minWidth);
11798                 }
11799                 if(this.hidden){
11800                     this.el.endMeasure();
11801                 }
11802             }
11803         }
11804     },
11805
11806     /**
11807      * Assigns this button's click handler
11808      * @param {Function} handler The function to call when the button is clicked
11809      * @param {Object} scope (optional) Scope for the function passed in
11810      */
11811     setHandler : function(handler, scope){
11812         this.handler = handler;
11813         this.scope = scope;  
11814     },
11815     
11816     /**
11817      * Sets this button's text
11818      * @param {String} text The button text
11819      */
11820     setText : function(text){
11821         this.text = text;
11822         if(this.el){
11823             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11824         }
11825         this.autoWidth();
11826     },
11827     
11828     /**
11829      * Gets the text for this button
11830      * @return {String} The button text
11831      */
11832     getText : function(){
11833         return this.text;  
11834     },
11835     
11836     /**
11837      * Show this button
11838      */
11839     show: function(){
11840         this.hidden = false;
11841         if(this.el){
11842             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11843         }
11844     },
11845     
11846     /**
11847      * Hide this button
11848      */
11849     hide: function(){
11850         this.hidden = true;
11851         if(this.el){
11852             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11853         }
11854     },
11855     
11856     /**
11857      * Convenience function for boolean show/hide
11858      * @param {Boolean} visible True to show, false to hide
11859      */
11860     setVisible: function(visible){
11861         if(visible) {
11862             this.show();
11863         }else{
11864             this.hide();
11865         }
11866     },
11867     
11868     /**
11869      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11870      * @param {Boolean} state (optional) Force a particular state
11871      */
11872     toggle : function(state){
11873         state = state === undefined ? !this.pressed : state;
11874         if(state != this.pressed){
11875             if(state){
11876                 this.el.addClass("x-btn-pressed");
11877                 this.pressed = true;
11878                 this.fireEvent("toggle", this, true);
11879             }else{
11880                 this.el.removeClass("x-btn-pressed");
11881                 this.pressed = false;
11882                 this.fireEvent("toggle", this, false);
11883             }
11884             if(this.toggleHandler){
11885                 this.toggleHandler.call(this.scope || this, this, state);
11886             }
11887         }
11888     },
11889     
11890     /**
11891      * Focus the button
11892      */
11893     focus : function(){
11894         this.el.child('button:first').focus();
11895     },
11896     
11897     /**
11898      * Disable this button
11899      */
11900     disable : function(){
11901         if(this.el){
11902             this.el.addClass("x-btn-disabled");
11903         }
11904         this.disabled = true;
11905     },
11906     
11907     /**
11908      * Enable this button
11909      */
11910     enable : function(){
11911         if(this.el){
11912             this.el.removeClass("x-btn-disabled");
11913         }
11914         this.disabled = false;
11915     },
11916
11917     /**
11918      * Convenience function for boolean enable/disable
11919      * @param {Boolean} enabled True to enable, false to disable
11920      */
11921     setDisabled : function(v){
11922         this[v !== true ? "enable" : "disable"]();
11923     },
11924
11925     // private
11926     onClick : function(e){
11927         if(e){
11928             e.preventDefault();
11929         }
11930         if(e.button != 0){
11931             return;
11932         }
11933         if(!this.disabled){
11934             if(this.enableToggle){
11935                 this.toggle();
11936             }
11937             if(this.menu && !this.menu.isVisible()){
11938                 this.menu.show(this.el, this.menuAlign);
11939             }
11940             this.fireEvent("click", this, e);
11941             if(this.handler){
11942                 this.el.removeClass("x-btn-over");
11943                 this.handler.call(this.scope || this, this, e);
11944             }
11945         }
11946     },
11947     // private
11948     onMouseOver : function(e){
11949         if(!this.disabled){
11950             this.el.addClass("x-btn-over");
11951             this.fireEvent('mouseover', this, e);
11952         }
11953     },
11954     // private
11955     onMouseOut : function(e){
11956         if(!e.within(this.el,  true)){
11957             this.el.removeClass("x-btn-over");
11958             this.fireEvent('mouseout', this, e);
11959         }
11960     },
11961     // private
11962     onFocus : function(e){
11963         if(!this.disabled){
11964             this.el.addClass("x-btn-focus");
11965         }
11966     },
11967     // private
11968     onBlur : function(e){
11969         this.el.removeClass("x-btn-focus");
11970     },
11971     // private
11972     onMouseDown : function(e){
11973         if(!this.disabled && e.button == 0){
11974             this.el.addClass("x-btn-click");
11975             Roo.get(document).on('mouseup', this.onMouseUp, this);
11976         }
11977     },
11978     // private
11979     onMouseUp : function(e){
11980         if(e.button == 0){
11981             this.el.removeClass("x-btn-click");
11982             Roo.get(document).un('mouseup', this.onMouseUp, this);
11983         }
11984     },
11985     // private
11986     onMenuShow : function(e){
11987         this.el.addClass("x-btn-menu-active");
11988     },
11989     // private
11990     onMenuHide : function(e){
11991         this.el.removeClass("x-btn-menu-active");
11992     }   
11993 });
11994
11995 // Private utility class used by Button
11996 Roo.ButtonToggleMgr = function(){
11997    var groups = {};
11998    
11999    function toggleGroup(btn, state){
12000        if(state){
12001            var g = groups[btn.toggleGroup];
12002            for(var i = 0, l = g.length; i < l; i++){
12003                if(g[i] != btn){
12004                    g[i].toggle(false);
12005                }
12006            }
12007        }
12008    }
12009    
12010    return {
12011        register : function(btn){
12012            if(!btn.toggleGroup){
12013                return;
12014            }
12015            var g = groups[btn.toggleGroup];
12016            if(!g){
12017                g = groups[btn.toggleGroup] = [];
12018            }
12019            g.push(btn);
12020            btn.on("toggle", toggleGroup);
12021        },
12022        
12023        unregister : function(btn){
12024            if(!btn.toggleGroup){
12025                return;
12026            }
12027            var g = groups[btn.toggleGroup];
12028            if(g){
12029                g.remove(btn);
12030                btn.un("toggle", toggleGroup);
12031            }
12032        }
12033    };
12034 }();/*
12035  * Based on:
12036  * Ext JS Library 1.1.1
12037  * Copyright(c) 2006-2007, Ext JS, LLC.
12038  *
12039  * Originally Released Under LGPL - original licence link has changed is not relivant.
12040  *
12041  * Fork - LGPL
12042  * <script type="text/javascript">
12043  */
12044  
12045 /**
12046  * @class Roo.SplitButton
12047  * @extends Roo.Button
12048  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12049  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12050  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12051  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12052  * @cfg {String} arrowTooltip The title attribute of the arrow
12053  * @constructor
12054  * Create a new menu button
12055  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12056  * @param {Object} config The config object
12057  */
12058 Roo.SplitButton = function(renderTo, config){
12059     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12060     /**
12061      * @event arrowclick
12062      * Fires when this button's arrow is clicked
12063      * @param {SplitButton} this
12064      * @param {EventObject} e The click event
12065      */
12066     this.addEvents({"arrowclick":true});
12067 };
12068
12069 Roo.extend(Roo.SplitButton, Roo.Button, {
12070     render : function(renderTo){
12071         // this is one sweet looking template!
12072         var tpl = new Roo.Template(
12073             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12074             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12075             '<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>',
12076             "</tbody></table></td><td>",
12077             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12078             '<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>',
12079             "</tbody></table></td></tr></table>"
12080         );
12081         var btn = tpl.append(renderTo, [this.text, this.type], true);
12082         var btnEl = btn.child("button");
12083         if(this.cls){
12084             btn.addClass(this.cls);
12085         }
12086         if(this.icon){
12087             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12088         }
12089         if(this.iconCls){
12090             btnEl.addClass(this.iconCls);
12091             if(!this.cls){
12092                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12093             }
12094         }
12095         this.el = btn;
12096         if(this.handleMouseEvents){
12097             btn.on("mouseover", this.onMouseOver, this);
12098             btn.on("mouseout", this.onMouseOut, this);
12099             btn.on("mousedown", this.onMouseDown, this);
12100             btn.on("mouseup", this.onMouseUp, this);
12101         }
12102         btn.on(this.clickEvent, this.onClick, this);
12103         if(this.tooltip){
12104             if(typeof this.tooltip == 'object'){
12105                 Roo.QuickTips.tips(Roo.apply({
12106                       target: btnEl.id
12107                 }, this.tooltip));
12108             } else {
12109                 btnEl.dom[this.tooltipType] = this.tooltip;
12110             }
12111         }
12112         if(this.arrowTooltip){
12113             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12114         }
12115         if(this.hidden){
12116             this.hide();
12117         }
12118         if(this.disabled){
12119             this.disable();
12120         }
12121         if(this.pressed){
12122             this.el.addClass("x-btn-pressed");
12123         }
12124         if(Roo.isIE && !Roo.isIE7){
12125             this.autoWidth.defer(1, this);
12126         }else{
12127             this.autoWidth();
12128         }
12129         if(this.menu){
12130             this.menu.on("show", this.onMenuShow, this);
12131             this.menu.on("hide", this.onMenuHide, this);
12132         }
12133         this.fireEvent('render', this);
12134     },
12135
12136     // private
12137     autoWidth : function(){
12138         if(this.el){
12139             var tbl = this.el.child("table:first");
12140             var tbl2 = this.el.child("table:last");
12141             this.el.setWidth("auto");
12142             tbl.setWidth("auto");
12143             if(Roo.isIE7 && Roo.isStrict){
12144                 var ib = this.el.child('button:first');
12145                 if(ib && ib.getWidth() > 20){
12146                     ib.clip();
12147                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12148                 }
12149             }
12150             if(this.minWidth){
12151                 if(this.hidden){
12152                     this.el.beginMeasure();
12153                 }
12154                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12155                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12156                 }
12157                 if(this.hidden){
12158                     this.el.endMeasure();
12159                 }
12160             }
12161             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12162         } 
12163     },
12164     /**
12165      * Sets this button's click handler
12166      * @param {Function} handler The function to call when the button is clicked
12167      * @param {Object} scope (optional) Scope for the function passed above
12168      */
12169     setHandler : function(handler, scope){
12170         this.handler = handler;
12171         this.scope = scope;  
12172     },
12173     
12174     /**
12175      * Sets this button's arrow click handler
12176      * @param {Function} handler The function to call when the arrow is clicked
12177      * @param {Object} scope (optional) Scope for the function passed above
12178      */
12179     setArrowHandler : function(handler, scope){
12180         this.arrowHandler = handler;
12181         this.scope = scope;  
12182     },
12183     
12184     /**
12185      * Focus the button
12186      */
12187     focus : function(){
12188         if(this.el){
12189             this.el.child("button:first").focus();
12190         }
12191     },
12192
12193     // private
12194     onClick : function(e){
12195         e.preventDefault();
12196         if(!this.disabled){
12197             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12198                 if(this.menu && !this.menu.isVisible()){
12199                     this.menu.show(this.el, this.menuAlign);
12200                 }
12201                 this.fireEvent("arrowclick", this, e);
12202                 if(this.arrowHandler){
12203                     this.arrowHandler.call(this.scope || this, this, e);
12204                 }
12205             }else{
12206                 this.fireEvent("click", this, e);
12207                 if(this.handler){
12208                     this.handler.call(this.scope || this, this, e);
12209                 }
12210             }
12211         }
12212     },
12213     // private
12214     onMouseDown : function(e){
12215         if(!this.disabled){
12216             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12217         }
12218     },
12219     // private
12220     onMouseUp : function(e){
12221         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12222     }   
12223 });
12224
12225
12226 // backwards compat
12227 Roo.MenuButton = Roo.SplitButton;/*
12228  * Based on:
12229  * Ext JS Library 1.1.1
12230  * Copyright(c) 2006-2007, Ext JS, LLC.
12231  *
12232  * Originally Released Under LGPL - original licence link has changed is not relivant.
12233  *
12234  * Fork - LGPL
12235  * <script type="text/javascript">
12236  */
12237
12238 /**
12239  * @class Roo.Toolbar
12240  * Basic Toolbar class.
12241  * @constructor
12242  * Creates a new Toolbar
12243  * @param {Object} config The config object
12244  */ 
12245 Roo.Toolbar = function(container, buttons, config)
12246 {
12247     /// old consturctor format still supported..
12248     if(container instanceof Array){ // omit the container for later rendering
12249         buttons = container;
12250         config = buttons;
12251         container = null;
12252     }
12253     if (typeof(container) == 'object' && container.xtype) {
12254         config = container;
12255         container = config.container;
12256         buttons = config.buttons; // not really - use items!!
12257     }
12258     var xitems = [];
12259     if (config && config.items) {
12260         xitems = config.items;
12261         delete config.items;
12262     }
12263     Roo.apply(this, config);
12264     this.buttons = buttons;
12265     
12266     if(container){
12267         this.render(container);
12268     }
12269     Roo.each(xitems, function(b) {
12270         this.add(b);
12271     }, this);
12272     
12273 };
12274
12275 Roo.Toolbar.prototype = {
12276     /**
12277      * @cfg {Roo.data.Store} items
12278      * array of button configs or elements to add
12279      */
12280     
12281     /**
12282      * @cfg {String/HTMLElement/Element} container
12283      * The id or element that will contain the toolbar
12284      */
12285     // private
12286     render : function(ct){
12287         this.el = Roo.get(ct);
12288         if(this.cls){
12289             this.el.addClass(this.cls);
12290         }
12291         // using a table allows for vertical alignment
12292         // 100% width is needed by Safari...
12293         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12294         this.tr = this.el.child("tr", true);
12295         var autoId = 0;
12296         this.items = new Roo.util.MixedCollection(false, function(o){
12297             return o.id || ("item" + (++autoId));
12298         });
12299         if(this.buttons){
12300             this.add.apply(this, this.buttons);
12301             delete this.buttons;
12302         }
12303     },
12304
12305     /**
12306      * Adds element(s) to the toolbar -- this function takes a variable number of 
12307      * arguments of mixed type and adds them to the toolbar.
12308      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12309      * <ul>
12310      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12311      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12312      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12313      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12314      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12315      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12316      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12317      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12318      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12319      * </ul>
12320      * @param {Mixed} arg2
12321      * @param {Mixed} etc.
12322      */
12323     add : function(){
12324         var a = arguments, l = a.length;
12325         for(var i = 0; i < l; i++){
12326             this._add(a[i]);
12327         }
12328     },
12329     // private..
12330     _add : function(el) {
12331         
12332         if (el.xtype) {
12333             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12334         }
12335         
12336         if (el.applyTo){ // some kind of form field
12337             return this.addField(el);
12338         } 
12339         if (el.render){ // some kind of Toolbar.Item
12340             return this.addItem(el);
12341         }
12342         if (typeof el == "string"){ // string
12343             if(el == "separator" || el == "-"){
12344                 return this.addSeparator();
12345             }
12346             if (el == " "){
12347                 return this.addSpacer();
12348             }
12349             if(el == "->"){
12350                 return this.addFill();
12351             }
12352             return this.addText(el);
12353             
12354         }
12355         if(el.tagName){ // element
12356             return this.addElement(el);
12357         }
12358         if(typeof el == "object"){ // must be button config?
12359             return this.addButton(el);
12360         }
12361         // and now what?!?!
12362         return false;
12363         
12364     },
12365     
12366     /**
12367      * Add an Xtype element
12368      * @param {Object} xtype Xtype Object
12369      * @return {Object} created Object
12370      */
12371     addxtype : function(e){
12372         return this.add(e);  
12373     },
12374     
12375     /**
12376      * Returns the Element for this toolbar.
12377      * @return {Roo.Element}
12378      */
12379     getEl : function(){
12380         return this.el;  
12381     },
12382     
12383     /**
12384      * Adds a separator
12385      * @return {Roo.Toolbar.Item} The separator item
12386      */
12387     addSeparator : function(){
12388         return this.addItem(new Roo.Toolbar.Separator());
12389     },
12390
12391     /**
12392      * Adds a spacer element
12393      * @return {Roo.Toolbar.Spacer} The spacer item
12394      */
12395     addSpacer : function(){
12396         return this.addItem(new Roo.Toolbar.Spacer());
12397     },
12398
12399     /**
12400      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12401      * @return {Roo.Toolbar.Fill} The fill item
12402      */
12403     addFill : function(){
12404         return this.addItem(new Roo.Toolbar.Fill());
12405     },
12406
12407     /**
12408      * Adds any standard HTML element to the toolbar
12409      * @param {String/HTMLElement/Element} el The element or id of the element to add
12410      * @return {Roo.Toolbar.Item} The element's item
12411      */
12412     addElement : function(el){
12413         return this.addItem(new Roo.Toolbar.Item(el));
12414     },
12415     /**
12416      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12417      * @type Roo.util.MixedCollection  
12418      */
12419     items : false,
12420      
12421     /**
12422      * Adds any Toolbar.Item or subclass
12423      * @param {Roo.Toolbar.Item} item
12424      * @return {Roo.Toolbar.Item} The item
12425      */
12426     addItem : function(item){
12427         var td = this.nextBlock();
12428         item.render(td);
12429         this.items.add(item);
12430         return item;
12431     },
12432     
12433     /**
12434      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12435      * @param {Object/Array} config A button config or array of configs
12436      * @return {Roo.Toolbar.Button/Array}
12437      */
12438     addButton : function(config){
12439         if(config instanceof Array){
12440             var buttons = [];
12441             for(var i = 0, len = config.length; i < len; i++) {
12442                 buttons.push(this.addButton(config[i]));
12443             }
12444             return buttons;
12445         }
12446         var b = config;
12447         if(!(config instanceof Roo.Toolbar.Button)){
12448             b = config.split ?
12449                 new Roo.Toolbar.SplitButton(config) :
12450                 new Roo.Toolbar.Button(config);
12451         }
12452         var td = this.nextBlock();
12453         b.render(td);
12454         this.items.add(b);
12455         return b;
12456     },
12457     
12458     /**
12459      * Adds text to the toolbar
12460      * @param {String} text The text to add
12461      * @return {Roo.Toolbar.Item} The element's item
12462      */
12463     addText : function(text){
12464         return this.addItem(new Roo.Toolbar.TextItem(text));
12465     },
12466     
12467     /**
12468      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12469      * @param {Number} index The index where the item is to be inserted
12470      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12471      * @return {Roo.Toolbar.Button/Item}
12472      */
12473     insertButton : function(index, item){
12474         if(item instanceof Array){
12475             var buttons = [];
12476             for(var i = 0, len = item.length; i < len; i++) {
12477                buttons.push(this.insertButton(index + i, item[i]));
12478             }
12479             return buttons;
12480         }
12481         if (!(item instanceof Roo.Toolbar.Button)){
12482            item = new Roo.Toolbar.Button(item);
12483         }
12484         var td = document.createElement("td");
12485         this.tr.insertBefore(td, this.tr.childNodes[index]);
12486         item.render(td);
12487         this.items.insert(index, item);
12488         return item;
12489     },
12490     
12491     /**
12492      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12493      * @param {Object} config
12494      * @return {Roo.Toolbar.Item} The element's item
12495      */
12496     addDom : function(config, returnEl){
12497         var td = this.nextBlock();
12498         Roo.DomHelper.overwrite(td, config);
12499         var ti = new Roo.Toolbar.Item(td.firstChild);
12500         ti.render(td);
12501         this.items.add(ti);
12502         return ti;
12503     },
12504
12505     /**
12506      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12507      * @type Roo.util.MixedCollection  
12508      */
12509     fields : false,
12510     
12511     /**
12512      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12513      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12514      * @param {Roo.form.Field} field
12515      * @return {Roo.ToolbarItem}
12516      */
12517      
12518       
12519     addField : function(field) {
12520         if (!this.fields) {
12521             var autoId = 0;
12522             this.fields = new Roo.util.MixedCollection(false, function(o){
12523                 return o.id || ("item" + (++autoId));
12524             });
12525
12526         }
12527         
12528         var td = this.nextBlock();
12529         field.render(td);
12530         var ti = new Roo.Toolbar.Item(td.firstChild);
12531         ti.render(td);
12532         this.items.add(ti);
12533         this.fields.add(field);
12534         return ti;
12535     },
12536     /**
12537      * Hide the toolbar
12538      * @method hide
12539      */
12540      
12541       
12542     hide : function()
12543     {
12544         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12545         this.el.child('div').hide();
12546     },
12547     /**
12548      * Show the toolbar
12549      * @method show
12550      */
12551     show : function()
12552     {
12553         this.el.child('div').show();
12554     },
12555       
12556     // private
12557     nextBlock : function(){
12558         var td = document.createElement("td");
12559         this.tr.appendChild(td);
12560         return td;
12561     },
12562
12563     // private
12564     destroy : function(){
12565         if(this.items){ // rendered?
12566             Roo.destroy.apply(Roo, this.items.items);
12567         }
12568         if(this.fields){ // rendered?
12569             Roo.destroy.apply(Roo, this.fields.items);
12570         }
12571         Roo.Element.uncache(this.el, this.tr);
12572     }
12573 };
12574
12575 /**
12576  * @class Roo.Toolbar.Item
12577  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12578  * @constructor
12579  * Creates a new Item
12580  * @param {HTMLElement} el 
12581  */
12582 Roo.Toolbar.Item = function(el){
12583     this.el = Roo.getDom(el);
12584     this.id = Roo.id(this.el);
12585     this.hidden = false;
12586 };
12587
12588 Roo.Toolbar.Item.prototype = {
12589     
12590     /**
12591      * Get this item's HTML Element
12592      * @return {HTMLElement}
12593      */
12594     getEl : function(){
12595        return this.el;  
12596     },
12597
12598     // private
12599     render : function(td){
12600         this.td = td;
12601         td.appendChild(this.el);
12602     },
12603     
12604     /**
12605      * Removes and destroys this item.
12606      */
12607     destroy : function(){
12608         this.td.parentNode.removeChild(this.td);
12609     },
12610     
12611     /**
12612      * Shows this item.
12613      */
12614     show: function(){
12615         this.hidden = false;
12616         this.td.style.display = "";
12617     },
12618     
12619     /**
12620      * Hides this item.
12621      */
12622     hide: function(){
12623         this.hidden = true;
12624         this.td.style.display = "none";
12625     },
12626     
12627     /**
12628      * Convenience function for boolean show/hide.
12629      * @param {Boolean} visible true to show/false to hide
12630      */
12631     setVisible: function(visible){
12632         if(visible) {
12633             this.show();
12634         }else{
12635             this.hide();
12636         }
12637     },
12638     
12639     /**
12640      * Try to focus this item.
12641      */
12642     focus : function(){
12643         Roo.fly(this.el).focus();
12644     },
12645     
12646     /**
12647      * Disables this item.
12648      */
12649     disable : function(){
12650         Roo.fly(this.td).addClass("x-item-disabled");
12651         this.disabled = true;
12652         this.el.disabled = true;
12653     },
12654     
12655     /**
12656      * Enables this item.
12657      */
12658     enable : function(){
12659         Roo.fly(this.td).removeClass("x-item-disabled");
12660         this.disabled = false;
12661         this.el.disabled = false;
12662     }
12663 };
12664
12665
12666 /**
12667  * @class Roo.Toolbar.Separator
12668  * @extends Roo.Toolbar.Item
12669  * A simple toolbar separator class
12670  * @constructor
12671  * Creates a new Separator
12672  */
12673 Roo.Toolbar.Separator = function(){
12674     var s = document.createElement("span");
12675     s.className = "ytb-sep";
12676     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12677 };
12678 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12679     enable:Roo.emptyFn,
12680     disable:Roo.emptyFn,
12681     focus:Roo.emptyFn
12682 });
12683
12684 /**
12685  * @class Roo.Toolbar.Spacer
12686  * @extends Roo.Toolbar.Item
12687  * A simple element that adds extra horizontal space to a toolbar.
12688  * @constructor
12689  * Creates a new Spacer
12690  */
12691 Roo.Toolbar.Spacer = function(){
12692     var s = document.createElement("div");
12693     s.className = "ytb-spacer";
12694     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12695 };
12696 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12697     enable:Roo.emptyFn,
12698     disable:Roo.emptyFn,
12699     focus:Roo.emptyFn
12700 });
12701
12702 /**
12703  * @class Roo.Toolbar.Fill
12704  * @extends Roo.Toolbar.Spacer
12705  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12706  * @constructor
12707  * Creates a new Spacer
12708  */
12709 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12710     // private
12711     render : function(td){
12712         td.style.width = '100%';
12713         Roo.Toolbar.Fill.superclass.render.call(this, td);
12714     }
12715 });
12716
12717 /**
12718  * @class Roo.Toolbar.TextItem
12719  * @extends Roo.Toolbar.Item
12720  * A simple class that renders text directly into a toolbar.
12721  * @constructor
12722  * Creates a new TextItem
12723  * @param {String} text
12724  */
12725 Roo.Toolbar.TextItem = function(text){
12726     if (typeof(text) == 'object') {
12727         text = text.text;
12728     }
12729     var s = document.createElement("span");
12730     s.className = "ytb-text";
12731     s.innerHTML = text;
12732     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12733 };
12734 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12735     enable:Roo.emptyFn,
12736     disable:Roo.emptyFn,
12737     focus:Roo.emptyFn
12738 });
12739
12740 /**
12741  * @class Roo.Toolbar.Button
12742  * @extends Roo.Button
12743  * A button that renders into a toolbar.
12744  * @constructor
12745  * Creates a new Button
12746  * @param {Object} config A standard {@link Roo.Button} config object
12747  */
12748 Roo.Toolbar.Button = function(config){
12749     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12750 };
12751 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12752     render : function(td){
12753         this.td = td;
12754         Roo.Toolbar.Button.superclass.render.call(this, td);
12755     },
12756     
12757     /**
12758      * Removes and destroys this button
12759      */
12760     destroy : function(){
12761         Roo.Toolbar.Button.superclass.destroy.call(this);
12762         this.td.parentNode.removeChild(this.td);
12763     },
12764     
12765     /**
12766      * Shows this button
12767      */
12768     show: function(){
12769         this.hidden = false;
12770         this.td.style.display = "";
12771     },
12772     
12773     /**
12774      * Hides this button
12775      */
12776     hide: function(){
12777         this.hidden = true;
12778         this.td.style.display = "none";
12779     },
12780
12781     /**
12782      * Disables this item
12783      */
12784     disable : function(){
12785         Roo.fly(this.td).addClass("x-item-disabled");
12786         this.disabled = true;
12787     },
12788
12789     /**
12790      * Enables this item
12791      */
12792     enable : function(){
12793         Roo.fly(this.td).removeClass("x-item-disabled");
12794         this.disabled = false;
12795     }
12796 });
12797 // backwards compat
12798 Roo.ToolbarButton = Roo.Toolbar.Button;
12799
12800 /**
12801  * @class Roo.Toolbar.SplitButton
12802  * @extends Roo.SplitButton
12803  * A menu button that renders into a toolbar.
12804  * @constructor
12805  * Creates a new SplitButton
12806  * @param {Object} config A standard {@link Roo.SplitButton} config object
12807  */
12808 Roo.Toolbar.SplitButton = function(config){
12809     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12810 };
12811 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12812     render : function(td){
12813         this.td = td;
12814         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12815     },
12816     
12817     /**
12818      * Removes and destroys this button
12819      */
12820     destroy : function(){
12821         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12822         this.td.parentNode.removeChild(this.td);
12823     },
12824     
12825     /**
12826      * Shows this button
12827      */
12828     show: function(){
12829         this.hidden = false;
12830         this.td.style.display = "";
12831     },
12832     
12833     /**
12834      * Hides this button
12835      */
12836     hide: function(){
12837         this.hidden = true;
12838         this.td.style.display = "none";
12839     }
12840 });
12841
12842 // backwards compat
12843 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12844  * Based on:
12845  * Ext JS Library 1.1.1
12846  * Copyright(c) 2006-2007, Ext JS, LLC.
12847  *
12848  * Originally Released Under LGPL - original licence link has changed is not relivant.
12849  *
12850  * Fork - LGPL
12851  * <script type="text/javascript">
12852  */
12853  
12854 /**
12855  * @class Roo.PagingToolbar
12856  * @extends Roo.Toolbar
12857  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12858  * @constructor
12859  * Create a new PagingToolbar
12860  * @param {Object} config The config object
12861  */
12862 Roo.PagingToolbar = function(el, ds, config)
12863 {
12864     // old args format still supported... - xtype is prefered..
12865     if (typeof(el) == 'object' && el.xtype) {
12866         // created from xtype...
12867         config = el;
12868         ds = el.dataSource;
12869         el = config.container;
12870     }
12871     var items = [];
12872     if (config.items) {
12873         items = config.items;
12874         config.items = [];
12875     }
12876     
12877     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12878     this.ds = ds;
12879     this.cursor = 0;
12880     this.renderButtons(this.el);
12881     this.bind(ds);
12882     
12883     // supprot items array.
12884    
12885     Roo.each(items, function(e) {
12886         this.add(Roo.factory(e));
12887     },this);
12888     
12889 };
12890
12891 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12892     /**
12893      * @cfg {Roo.data.Store} dataSource
12894      * The underlying data store providing the paged data
12895      */
12896     /**
12897      * @cfg {String/HTMLElement/Element} container
12898      * container The id or element that will contain the toolbar
12899      */
12900     /**
12901      * @cfg {Boolean} displayInfo
12902      * True to display the displayMsg (defaults to false)
12903      */
12904     /**
12905      * @cfg {Number} pageSize
12906      * The number of records to display per page (defaults to 20)
12907      */
12908     pageSize: 20,
12909     /**
12910      * @cfg {String} displayMsg
12911      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12912      */
12913     displayMsg : 'Displaying {0} - {1} of {2}',
12914     /**
12915      * @cfg {String} emptyMsg
12916      * The message to display when no records are found (defaults to "No data to display")
12917      */
12918     emptyMsg : 'No data to display',
12919     /**
12920      * Customizable piece of the default paging text (defaults to "Page")
12921      * @type String
12922      */
12923     beforePageText : "Page",
12924     /**
12925      * Customizable piece of the default paging text (defaults to "of %0")
12926      * @type String
12927      */
12928     afterPageText : "of {0}",
12929     /**
12930      * Customizable piece of the default paging text (defaults to "First Page")
12931      * @type String
12932      */
12933     firstText : "First Page",
12934     /**
12935      * Customizable piece of the default paging text (defaults to "Previous Page")
12936      * @type String
12937      */
12938     prevText : "Previous Page",
12939     /**
12940      * Customizable piece of the default paging text (defaults to "Next Page")
12941      * @type String
12942      */
12943     nextText : "Next Page",
12944     /**
12945      * Customizable piece of the default paging text (defaults to "Last Page")
12946      * @type String
12947      */
12948     lastText : "Last Page",
12949     /**
12950      * Customizable piece of the default paging text (defaults to "Refresh")
12951      * @type String
12952      */
12953     refreshText : "Refresh",
12954
12955     // private
12956     renderButtons : function(el){
12957         Roo.PagingToolbar.superclass.render.call(this, el);
12958         this.first = this.addButton({
12959             tooltip: this.firstText,
12960             cls: "x-btn-icon x-grid-page-first",
12961             disabled: true,
12962             handler: this.onClick.createDelegate(this, ["first"])
12963         });
12964         this.prev = this.addButton({
12965             tooltip: this.prevText,
12966             cls: "x-btn-icon x-grid-page-prev",
12967             disabled: true,
12968             handler: this.onClick.createDelegate(this, ["prev"])
12969         });
12970         //this.addSeparator();
12971         this.add(this.beforePageText);
12972         this.field = Roo.get(this.addDom({
12973            tag: "input",
12974            type: "text",
12975            size: "3",
12976            value: "1",
12977            cls: "x-grid-page-number"
12978         }).el);
12979         this.field.on("keydown", this.onPagingKeydown, this);
12980         this.field.on("focus", function(){this.dom.select();});
12981         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12982         this.field.setHeight(18);
12983         //this.addSeparator();
12984         this.next = this.addButton({
12985             tooltip: this.nextText,
12986             cls: "x-btn-icon x-grid-page-next",
12987             disabled: true,
12988             handler: this.onClick.createDelegate(this, ["next"])
12989         });
12990         this.last = this.addButton({
12991             tooltip: this.lastText,
12992             cls: "x-btn-icon x-grid-page-last",
12993             disabled: true,
12994             handler: this.onClick.createDelegate(this, ["last"])
12995         });
12996         //this.addSeparator();
12997         this.loading = this.addButton({
12998             tooltip: this.refreshText,
12999             cls: "x-btn-icon x-grid-loading",
13000             handler: this.onClick.createDelegate(this, ["refresh"])
13001         });
13002
13003         if(this.displayInfo){
13004             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13005         }
13006     },
13007
13008     // private
13009     updateInfo : function(){
13010         if(this.displayEl){
13011             var count = this.ds.getCount();
13012             var msg = count == 0 ?
13013                 this.emptyMsg :
13014                 String.format(
13015                     this.displayMsg,
13016                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13017                 );
13018             this.displayEl.update(msg);
13019         }
13020     },
13021
13022     // private
13023     onLoad : function(ds, r, o){
13024        this.cursor = o.params ? o.params.start : 0;
13025        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13026
13027        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13028        this.field.dom.value = ap;
13029        this.first.setDisabled(ap == 1);
13030        this.prev.setDisabled(ap == 1);
13031        this.next.setDisabled(ap == ps);
13032        this.last.setDisabled(ap == ps);
13033        this.loading.enable();
13034        this.updateInfo();
13035     },
13036
13037     // private
13038     getPageData : function(){
13039         var total = this.ds.getTotalCount();
13040         return {
13041             total : total,
13042             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13043             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13044         };
13045     },
13046
13047     // private
13048     onLoadError : function(){
13049         this.loading.enable();
13050     },
13051
13052     // private
13053     onPagingKeydown : function(e){
13054         var k = e.getKey();
13055         var d = this.getPageData();
13056         if(k == e.RETURN){
13057             var v = this.field.dom.value, pageNum;
13058             if(!v || isNaN(pageNum = parseInt(v, 10))){
13059                 this.field.dom.value = d.activePage;
13060                 return;
13061             }
13062             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13063             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13064             e.stopEvent();
13065         }
13066         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))
13067         {
13068           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13069           this.field.dom.value = pageNum;
13070           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13071           e.stopEvent();
13072         }
13073         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13074         {
13075           var v = this.field.dom.value, pageNum; 
13076           var increment = (e.shiftKey) ? 10 : 1;
13077           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13078             increment *= -1;
13079           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13080             this.field.dom.value = d.activePage;
13081             return;
13082           }
13083           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13084           {
13085             this.field.dom.value = parseInt(v, 10) + increment;
13086             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13087             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13088           }
13089           e.stopEvent();
13090         }
13091     },
13092
13093     // private
13094     beforeLoad : function(){
13095         if(this.loading){
13096             this.loading.disable();
13097         }
13098     },
13099
13100     // private
13101     onClick : function(which){
13102         var ds = this.ds;
13103         switch(which){
13104             case "first":
13105                 ds.load({params:{start: 0, limit: this.pageSize}});
13106             break;
13107             case "prev":
13108                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13109             break;
13110             case "next":
13111                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13112             break;
13113             case "last":
13114                 var total = ds.getTotalCount();
13115                 var extra = total % this.pageSize;
13116                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13117                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13118             break;
13119             case "refresh":
13120                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13121             break;
13122         }
13123     },
13124
13125     /**
13126      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13127      * @param {Roo.data.Store} store The data store to unbind
13128      */
13129     unbind : function(ds){
13130         ds.un("beforeload", this.beforeLoad, this);
13131         ds.un("load", this.onLoad, this);
13132         ds.un("loadexception", this.onLoadError, this);
13133         ds.un("remove", this.updateInfo, this);
13134         ds.un("add", this.updateInfo, this);
13135         this.ds = undefined;
13136     },
13137
13138     /**
13139      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13140      * @param {Roo.data.Store} store The data store to bind
13141      */
13142     bind : function(ds){
13143         ds.on("beforeload", this.beforeLoad, this);
13144         ds.on("load", this.onLoad, this);
13145         ds.on("loadexception", this.onLoadError, this);
13146         ds.on("remove", this.updateInfo, this);
13147         ds.on("add", this.updateInfo, this);
13148         this.ds = ds;
13149     }
13150 });/*
13151  * Based on:
13152  * Ext JS Library 1.1.1
13153  * Copyright(c) 2006-2007, Ext JS, LLC.
13154  *
13155  * Originally Released Under LGPL - original licence link has changed is not relivant.
13156  *
13157  * Fork - LGPL
13158  * <script type="text/javascript">
13159  */
13160
13161 /**
13162  * @class Roo.Resizable
13163  * @extends Roo.util.Observable
13164  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13165  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13166  * 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
13167  * the element will be wrapped for you automatically.</p>
13168  * <p>Here is the list of valid resize handles:</p>
13169  * <pre>
13170 Value   Description
13171 ------  -------------------
13172  'n'     north
13173  's'     south
13174  'e'     east
13175  'w'     west
13176  'nw'    northwest
13177  'sw'    southwest
13178  'se'    southeast
13179  'ne'    northeast
13180  'hd'    horizontal drag
13181  'all'   all
13182 </pre>
13183  * <p>Here's an example showing the creation of a typical Resizable:</p>
13184  * <pre><code>
13185 var resizer = new Roo.Resizable("element-id", {
13186     handles: 'all',
13187     minWidth: 200,
13188     minHeight: 100,
13189     maxWidth: 500,
13190     maxHeight: 400,
13191     pinned: true
13192 });
13193 resizer.on("resize", myHandler);
13194 </code></pre>
13195  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13196  * resizer.east.setDisplayed(false);</p>
13197  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13198  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13199  * resize operation's new size (defaults to [0, 0])
13200  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13201  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13202  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13203  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13204  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13205  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13206  * @cfg {Number} width The width of the element in pixels (defaults to null)
13207  * @cfg {Number} height The height of the element in pixels (defaults to null)
13208  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13209  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13210  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13211  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13212  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13213  * in favor of the handles config option (defaults to false)
13214  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13215  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13216  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13217  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13218  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13219  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13220  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13221  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13222  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13223  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13224  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13225  * @constructor
13226  * Create a new resizable component
13227  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13228  * @param {Object} config configuration options
13229   */
13230 Roo.Resizable = function(el, config)
13231 {
13232     this.el = Roo.get(el);
13233
13234     if(config && config.wrap){
13235         config.resizeChild = this.el;
13236         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13237         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13238         this.el.setStyle("overflow", "hidden");
13239         this.el.setPositioning(config.resizeChild.getPositioning());
13240         config.resizeChild.clearPositioning();
13241         if(!config.width || !config.height){
13242             var csize = config.resizeChild.getSize();
13243             this.el.setSize(csize.width, csize.height);
13244         }
13245         if(config.pinned && !config.adjustments){
13246             config.adjustments = "auto";
13247         }
13248     }
13249
13250     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13251     this.proxy.unselectable();
13252     this.proxy.enableDisplayMode('block');
13253
13254     Roo.apply(this, config);
13255
13256     if(this.pinned){
13257         this.disableTrackOver = true;
13258         this.el.addClass("x-resizable-pinned");
13259     }
13260     // if the element isn't positioned, make it relative
13261     var position = this.el.getStyle("position");
13262     if(position != "absolute" && position != "fixed"){
13263         this.el.setStyle("position", "relative");
13264     }
13265     if(!this.handles){ // no handles passed, must be legacy style
13266         this.handles = 's,e,se';
13267         if(this.multiDirectional){
13268             this.handles += ',n,w';
13269         }
13270     }
13271     if(this.handles == "all"){
13272         this.handles = "n s e w ne nw se sw";
13273     }
13274     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13275     var ps = Roo.Resizable.positions;
13276     for(var i = 0, len = hs.length; i < len; i++){
13277         if(hs[i] && ps[hs[i]]){
13278             var pos = ps[hs[i]];
13279             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13280         }
13281     }
13282     // legacy
13283     this.corner = this.southeast;
13284     
13285     // updateBox = the box can move..
13286     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13287         this.updateBox = true;
13288     }
13289
13290     this.activeHandle = null;
13291
13292     if(this.resizeChild){
13293         if(typeof this.resizeChild == "boolean"){
13294             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13295         }else{
13296             this.resizeChild = Roo.get(this.resizeChild, true);
13297         }
13298     }
13299     
13300     if(this.adjustments == "auto"){
13301         var rc = this.resizeChild;
13302         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13303         if(rc && (hw || hn)){
13304             rc.position("relative");
13305             rc.setLeft(hw ? hw.el.getWidth() : 0);
13306             rc.setTop(hn ? hn.el.getHeight() : 0);
13307         }
13308         this.adjustments = [
13309             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13310             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13311         ];
13312     }
13313
13314     if(this.draggable){
13315         this.dd = this.dynamic ?
13316             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13317         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13318     }
13319
13320     // public events
13321     this.addEvents({
13322         /**
13323          * @event beforeresize
13324          * Fired before resize is allowed. Set enabled to false to cancel resize.
13325          * @param {Roo.Resizable} this
13326          * @param {Roo.EventObject} e The mousedown event
13327          */
13328         "beforeresize" : true,
13329         /**
13330          * @event resize
13331          * Fired after a resize.
13332          * @param {Roo.Resizable} this
13333          * @param {Number} width The new width
13334          * @param {Number} height The new height
13335          * @param {Roo.EventObject} e The mouseup event
13336          */
13337         "resize" : true
13338     });
13339
13340     if(this.width !== null && this.height !== null){
13341         this.resizeTo(this.width, this.height);
13342     }else{
13343         this.updateChildSize();
13344     }
13345     if(Roo.isIE){
13346         this.el.dom.style.zoom = 1;
13347     }
13348     Roo.Resizable.superclass.constructor.call(this);
13349 };
13350
13351 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13352         resizeChild : false,
13353         adjustments : [0, 0],
13354         minWidth : 5,
13355         minHeight : 5,
13356         maxWidth : 10000,
13357         maxHeight : 10000,
13358         enabled : true,
13359         animate : false,
13360         duration : .35,
13361         dynamic : false,
13362         handles : false,
13363         multiDirectional : false,
13364         disableTrackOver : false,
13365         easing : 'easeOutStrong',
13366         widthIncrement : 0,
13367         heightIncrement : 0,
13368         pinned : false,
13369         width : null,
13370         height : null,
13371         preserveRatio : false,
13372         transparent: false,
13373         minX: 0,
13374         minY: 0,
13375         draggable: false,
13376
13377         /**
13378          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13379          */
13380         constrainTo: undefined,
13381         /**
13382          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13383          */
13384         resizeRegion: undefined,
13385
13386
13387     /**
13388      * Perform a manual resize
13389      * @param {Number} width
13390      * @param {Number} height
13391      */
13392     resizeTo : function(width, height){
13393         this.el.setSize(width, height);
13394         this.updateChildSize();
13395         this.fireEvent("resize", this, width, height, null);
13396     },
13397
13398     // private
13399     startSizing : function(e, handle){
13400         this.fireEvent("beforeresize", this, e);
13401         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13402
13403             if(!this.overlay){
13404                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13405                 this.overlay.unselectable();
13406                 this.overlay.enableDisplayMode("block");
13407                 this.overlay.on("mousemove", this.onMouseMove, this);
13408                 this.overlay.on("mouseup", this.onMouseUp, this);
13409             }
13410             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13411
13412             this.resizing = true;
13413             this.startBox = this.el.getBox();
13414             this.startPoint = e.getXY();
13415             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13416                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13417
13418             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13419             this.overlay.show();
13420
13421             if(this.constrainTo) {
13422                 var ct = Roo.get(this.constrainTo);
13423                 this.resizeRegion = ct.getRegion().adjust(
13424                     ct.getFrameWidth('t'),
13425                     ct.getFrameWidth('l'),
13426                     -ct.getFrameWidth('b'),
13427                     -ct.getFrameWidth('r')
13428                 );
13429             }
13430
13431             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13432             this.proxy.show();
13433             this.proxy.setBox(this.startBox);
13434             if(!this.dynamic){
13435                 this.proxy.setStyle('visibility', 'visible');
13436             }
13437         }
13438     },
13439
13440     // private
13441     onMouseDown : function(handle, e){
13442         if(this.enabled){
13443             e.stopEvent();
13444             this.activeHandle = handle;
13445             this.startSizing(e, handle);
13446         }
13447     },
13448
13449     // private
13450     onMouseUp : function(e){
13451         var size = this.resizeElement();
13452         this.resizing = false;
13453         this.handleOut();
13454         this.overlay.hide();
13455         this.proxy.hide();
13456         this.fireEvent("resize", this, size.width, size.height, e);
13457     },
13458
13459     // private
13460     updateChildSize : function(){
13461         if(this.resizeChild){
13462             var el = this.el;
13463             var child = this.resizeChild;
13464             var adj = this.adjustments;
13465             if(el.dom.offsetWidth){
13466                 var b = el.getSize(true);
13467                 child.setSize(b.width+adj[0], b.height+adj[1]);
13468             }
13469             // Second call here for IE
13470             // The first call enables instant resizing and
13471             // the second call corrects scroll bars if they
13472             // exist
13473             if(Roo.isIE){
13474                 setTimeout(function(){
13475                     if(el.dom.offsetWidth){
13476                         var b = el.getSize(true);
13477                         child.setSize(b.width+adj[0], b.height+adj[1]);
13478                     }
13479                 }, 10);
13480             }
13481         }
13482     },
13483
13484     // private
13485     snap : function(value, inc, min){
13486         if(!inc || !value) return value;
13487         var newValue = value;
13488         var m = value % inc;
13489         if(m > 0){
13490             if(m > (inc/2)){
13491                 newValue = value + (inc-m);
13492             }else{
13493                 newValue = value - m;
13494             }
13495         }
13496         return Math.max(min, newValue);
13497     },
13498
13499     // private
13500     resizeElement : function(){
13501         var box = this.proxy.getBox();
13502         if(this.updateBox){
13503             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13504         }else{
13505             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13506         }
13507         this.updateChildSize();
13508         if(!this.dynamic){
13509             this.proxy.hide();
13510         }
13511         return box;
13512     },
13513
13514     // private
13515     constrain : function(v, diff, m, mx){
13516         if(v - diff < m){
13517             diff = v - m;
13518         }else if(v - diff > mx){
13519             diff = mx - v;
13520         }
13521         return diff;
13522     },
13523
13524     // private
13525     onMouseMove : function(e){
13526         if(this.enabled){
13527             try{// try catch so if something goes wrong the user doesn't get hung
13528
13529             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13530                 return;
13531             }
13532
13533             //var curXY = this.startPoint;
13534             var curSize = this.curSize || this.startBox;
13535             var x = this.startBox.x, y = this.startBox.y;
13536             var ox = x, oy = y;
13537             var w = curSize.width, h = curSize.height;
13538             var ow = w, oh = h;
13539             var mw = this.minWidth, mh = this.minHeight;
13540             var mxw = this.maxWidth, mxh = this.maxHeight;
13541             var wi = this.widthIncrement;
13542             var hi = this.heightIncrement;
13543
13544             var eventXY = e.getXY();
13545             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13546             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13547
13548             var pos = this.activeHandle.position;
13549
13550             switch(pos){
13551                 case "east":
13552                     w += diffX;
13553                     w = Math.min(Math.max(mw, w), mxw);
13554                     break;
13555              
13556                 case "south":
13557                     h += diffY;
13558                     h = Math.min(Math.max(mh, h), mxh);
13559                     break;
13560                 case "southeast":
13561                     w += diffX;
13562                     h += diffY;
13563                     w = Math.min(Math.max(mw, w), mxw);
13564                     h = Math.min(Math.max(mh, h), mxh);
13565                     break;
13566                 case "north":
13567                     diffY = this.constrain(h, diffY, mh, mxh);
13568                     y += diffY;
13569                     h -= diffY;
13570                     break;
13571                 case "hdrag":
13572                     
13573                     if (wi) {
13574                         var adiffX = Math.abs(diffX);
13575                         var sub = (adiffX % wi); // how much 
13576                         if (sub > (wi/2)) { // far enough to snap
13577                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13578                         } else {
13579                             // remove difference.. 
13580                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13581                         }
13582                     }
13583                     x += diffX;
13584                     x = Math.max(this.minX, x);
13585                     break;
13586                 case "west":
13587                     diffX = this.constrain(w, diffX, mw, mxw);
13588                     x += diffX;
13589                     w -= diffX;
13590                     break;
13591                 case "northeast":
13592                     w += diffX;
13593                     w = Math.min(Math.max(mw, w), mxw);
13594                     diffY = this.constrain(h, diffY, mh, mxh);
13595                     y += diffY;
13596                     h -= diffY;
13597                     break;
13598                 case "northwest":
13599                     diffX = this.constrain(w, diffX, mw, mxw);
13600                     diffY = this.constrain(h, diffY, mh, mxh);
13601                     y += diffY;
13602                     h -= diffY;
13603                     x += diffX;
13604                     w -= diffX;
13605                     break;
13606                case "southwest":
13607                     diffX = this.constrain(w, diffX, mw, mxw);
13608                     h += diffY;
13609                     h = Math.min(Math.max(mh, h), mxh);
13610                     x += diffX;
13611                     w -= diffX;
13612                     break;
13613             }
13614
13615             var sw = this.snap(w, wi, mw);
13616             var sh = this.snap(h, hi, mh);
13617             if(sw != w || sh != h){
13618                 switch(pos){
13619                     case "northeast":
13620                         y -= sh - h;
13621                     break;
13622                     case "north":
13623                         y -= sh - h;
13624                         break;
13625                     case "southwest":
13626                         x -= sw - w;
13627                     break;
13628                     case "west":
13629                         x -= sw - w;
13630                         break;
13631                     case "northwest":
13632                         x -= sw - w;
13633                         y -= sh - h;
13634                     break;
13635                 }
13636                 w = sw;
13637                 h = sh;
13638             }
13639
13640             if(this.preserveRatio){
13641                 switch(pos){
13642                     case "southeast":
13643                     case "east":
13644                         h = oh * (w/ow);
13645                         h = Math.min(Math.max(mh, h), mxh);
13646                         w = ow * (h/oh);
13647                        break;
13648                     case "south":
13649                         w = ow * (h/oh);
13650                         w = Math.min(Math.max(mw, w), mxw);
13651                         h = oh * (w/ow);
13652                         break;
13653                     case "northeast":
13654                         w = ow * (h/oh);
13655                         w = Math.min(Math.max(mw, w), mxw);
13656                         h = oh * (w/ow);
13657                     break;
13658                     case "north":
13659                         var tw = w;
13660                         w = ow * (h/oh);
13661                         w = Math.min(Math.max(mw, w), mxw);
13662                         h = oh * (w/ow);
13663                         x += (tw - w) / 2;
13664                         break;
13665                     case "southwest":
13666                         h = oh * (w/ow);
13667                         h = Math.min(Math.max(mh, h), mxh);
13668                         var tw = w;
13669                         w = ow * (h/oh);
13670                         x += tw - w;
13671                         break;
13672                     case "west":
13673                         var th = h;
13674                         h = oh * (w/ow);
13675                         h = Math.min(Math.max(mh, h), mxh);
13676                         y += (th - h) / 2;
13677                         var tw = w;
13678                         w = ow * (h/oh);
13679                         x += tw - w;
13680                        break;
13681                     case "northwest":
13682                         var tw = w;
13683                         var th = h;
13684                         h = oh * (w/ow);
13685                         h = Math.min(Math.max(mh, h), mxh);
13686                         w = ow * (h/oh);
13687                         y += th - h;
13688                         x += tw - w;
13689                        break;
13690
13691                 }
13692             }
13693             if (pos == 'hdrag') {
13694                 w = ow;
13695             }
13696             this.proxy.setBounds(x, y, w, h);
13697             if(this.dynamic){
13698                 this.resizeElement();
13699             }
13700             }catch(e){}
13701         }
13702     },
13703
13704     // private
13705     handleOver : function(){
13706         if(this.enabled){
13707             this.el.addClass("x-resizable-over");
13708         }
13709     },
13710
13711     // private
13712     handleOut : function(){
13713         if(!this.resizing){
13714             this.el.removeClass("x-resizable-over");
13715         }
13716     },
13717
13718     /**
13719      * Returns the element this component is bound to.
13720      * @return {Roo.Element}
13721      */
13722     getEl : function(){
13723         return this.el;
13724     },
13725
13726     /**
13727      * Returns the resizeChild element (or null).
13728      * @return {Roo.Element}
13729      */
13730     getResizeChild : function(){
13731         return this.resizeChild;
13732     },
13733
13734     /**
13735      * Destroys this resizable. If the element was wrapped and
13736      * removeEl is not true then the element remains.
13737      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13738      */
13739     destroy : function(removeEl){
13740         this.proxy.remove();
13741         if(this.overlay){
13742             this.overlay.removeAllListeners();
13743             this.overlay.remove();
13744         }
13745         var ps = Roo.Resizable.positions;
13746         for(var k in ps){
13747             if(typeof ps[k] != "function" && this[ps[k]]){
13748                 var h = this[ps[k]];
13749                 h.el.removeAllListeners();
13750                 h.el.remove();
13751             }
13752         }
13753         if(removeEl){
13754             this.el.update("");
13755             this.el.remove();
13756         }
13757     }
13758 });
13759
13760 // private
13761 // hash to map config positions to true positions
13762 Roo.Resizable.positions = {
13763     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13764     hd: "hdrag"
13765 };
13766
13767 // private
13768 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13769     if(!this.tpl){
13770         // only initialize the template if resizable is used
13771         var tpl = Roo.DomHelper.createTemplate(
13772             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13773         );
13774         tpl.compile();
13775         Roo.Resizable.Handle.prototype.tpl = tpl;
13776     }
13777     this.position = pos;
13778     this.rz = rz;
13779     // show north drag fro topdra
13780     var handlepos = pos == 'hdrag' ? 'north' : pos;
13781     
13782     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13783     if (pos == 'hdrag') {
13784         this.el.setStyle('cursor', 'pointer');
13785     }
13786     this.el.unselectable();
13787     if(transparent){
13788         this.el.setOpacity(0);
13789     }
13790     this.el.on("mousedown", this.onMouseDown, this);
13791     if(!disableTrackOver){
13792         this.el.on("mouseover", this.onMouseOver, this);
13793         this.el.on("mouseout", this.onMouseOut, this);
13794     }
13795 };
13796
13797 // private
13798 Roo.Resizable.Handle.prototype = {
13799     afterResize : function(rz){
13800         // do nothing
13801     },
13802     // private
13803     onMouseDown : function(e){
13804         this.rz.onMouseDown(this, e);
13805     },
13806     // private
13807     onMouseOver : function(e){
13808         this.rz.handleOver(this, e);
13809     },
13810     // private
13811     onMouseOut : function(e){
13812         this.rz.handleOut(this, e);
13813     }
13814 };/*
13815  * Based on:
13816  * Ext JS Library 1.1.1
13817  * Copyright(c) 2006-2007, Ext JS, LLC.
13818  *
13819  * Originally Released Under LGPL - original licence link has changed is not relivant.
13820  *
13821  * Fork - LGPL
13822  * <script type="text/javascript">
13823  */
13824
13825 /**
13826  * @class Roo.Editor
13827  * @extends Roo.Component
13828  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13829  * @constructor
13830  * Create a new Editor
13831  * @param {Roo.form.Field} field The Field object (or descendant)
13832  * @param {Object} config The config object
13833  */
13834 Roo.Editor = function(field, config){
13835     Roo.Editor.superclass.constructor.call(this, config);
13836     this.field = field;
13837     this.addEvents({
13838         /**
13839              * @event beforestartedit
13840              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13841              * false from the handler of this event.
13842              * @param {Editor} this
13843              * @param {Roo.Element} boundEl The underlying element bound to this editor
13844              * @param {Mixed} value The field value being set
13845              */
13846         "beforestartedit" : true,
13847         /**
13848              * @event startedit
13849              * Fires when this editor is displayed
13850              * @param {Roo.Element} boundEl The underlying element bound to this editor
13851              * @param {Mixed} value The starting field value
13852              */
13853         "startedit" : true,
13854         /**
13855              * @event beforecomplete
13856              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13857              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13858              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13859              * event will not fire since no edit actually occurred.
13860              * @param {Editor} this
13861              * @param {Mixed} value The current field value
13862              * @param {Mixed} startValue The original field value
13863              */
13864         "beforecomplete" : true,
13865         /**
13866              * @event complete
13867              * Fires after editing is complete and any changed value has been written to the underlying field.
13868              * @param {Editor} this
13869              * @param {Mixed} value The current field value
13870              * @param {Mixed} startValue The original field value
13871              */
13872         "complete" : true,
13873         /**
13874          * @event specialkey
13875          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13876          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13877          * @param {Roo.form.Field} this
13878          * @param {Roo.EventObject} e The event object
13879          */
13880         "specialkey" : true
13881     });
13882 };
13883
13884 Roo.extend(Roo.Editor, Roo.Component, {
13885     /**
13886      * @cfg {Boolean/String} autosize
13887      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13888      * or "height" to adopt the height only (defaults to false)
13889      */
13890     /**
13891      * @cfg {Boolean} revertInvalid
13892      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13893      * validation fails (defaults to true)
13894      */
13895     /**
13896      * @cfg {Boolean} ignoreNoChange
13897      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13898      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13899      * will never be ignored.
13900      */
13901     /**
13902      * @cfg {Boolean} hideEl
13903      * False to keep the bound element visible while the editor is displayed (defaults to true)
13904      */
13905     /**
13906      * @cfg {Mixed} value
13907      * The data value of the underlying field (defaults to "")
13908      */
13909     value : "",
13910     /**
13911      * @cfg {String} alignment
13912      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13913      */
13914     alignment: "c-c?",
13915     /**
13916      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13917      * for bottom-right shadow (defaults to "frame")
13918      */
13919     shadow : "frame",
13920     /**
13921      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13922      */
13923     constrain : false,
13924     /**
13925      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13926      */
13927     completeOnEnter : false,
13928     /**
13929      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13930      */
13931     cancelOnEsc : false,
13932     /**
13933      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13934      */
13935     updateEl : false,
13936
13937     // private
13938     onRender : function(ct, position){
13939         this.el = new Roo.Layer({
13940             shadow: this.shadow,
13941             cls: "x-editor",
13942             parentEl : ct,
13943             shim : this.shim,
13944             shadowOffset:4,
13945             id: this.id,
13946             constrain: this.constrain
13947         });
13948         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13949         if(this.field.msgTarget != 'title'){
13950             this.field.msgTarget = 'qtip';
13951         }
13952         this.field.render(this.el);
13953         if(Roo.isGecko){
13954             this.field.el.dom.setAttribute('autocomplete', 'off');
13955         }
13956         this.field.on("specialkey", this.onSpecialKey, this);
13957         if(this.swallowKeys){
13958             this.field.el.swallowEvent(['keydown','keypress']);
13959         }
13960         this.field.show();
13961         this.field.on("blur", this.onBlur, this);
13962         if(this.field.grow){
13963             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13964         }
13965     },
13966
13967     onSpecialKey : function(field, e)
13968     {
13969         //Roo.log('editor onSpecialKey');
13970         if(this.completeOnEnter && e.getKey() == e.ENTER){
13971             e.stopEvent();
13972             this.completeEdit();
13973             return;
13974         }
13975         // do not fire special key otherwise it might hide close the editor...
13976         if(e.getKey() == e.ENTER){    
13977             return;
13978         }
13979         if(this.cancelOnEsc && e.getKey() == e.ESC){
13980             this.cancelEdit();
13981             return;
13982         } 
13983         this.fireEvent('specialkey', field, e);
13984     
13985     },
13986
13987     /**
13988      * Starts the editing process and shows the editor.
13989      * @param {String/HTMLElement/Element} el The element to edit
13990      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13991       * to the innerHTML of el.
13992      */
13993     startEdit : function(el, value){
13994         if(this.editing){
13995             this.completeEdit();
13996         }
13997         this.boundEl = Roo.get(el);
13998         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13999         if(!this.rendered){
14000             this.render(this.parentEl || document.body);
14001         }
14002         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14003             return;
14004         }
14005         this.startValue = v;
14006         this.field.setValue(v);
14007         if(this.autoSize){
14008             var sz = this.boundEl.getSize();
14009             switch(this.autoSize){
14010                 case "width":
14011                 this.setSize(sz.width,  "");
14012                 break;
14013                 case "height":
14014                 this.setSize("",  sz.height);
14015                 break;
14016                 default:
14017                 this.setSize(sz.width,  sz.height);
14018             }
14019         }
14020         this.el.alignTo(this.boundEl, this.alignment);
14021         this.editing = true;
14022         if(Roo.QuickTips){
14023             Roo.QuickTips.disable();
14024         }
14025         this.show();
14026     },
14027
14028     /**
14029      * Sets the height and width of this editor.
14030      * @param {Number} width The new width
14031      * @param {Number} height The new height
14032      */
14033     setSize : function(w, h){
14034         this.field.setSize(w, h);
14035         if(this.el){
14036             this.el.sync();
14037         }
14038     },
14039
14040     /**
14041      * Realigns the editor to the bound field based on the current alignment config value.
14042      */
14043     realign : function(){
14044         this.el.alignTo(this.boundEl, this.alignment);
14045     },
14046
14047     /**
14048      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14049      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14050      */
14051     completeEdit : function(remainVisible){
14052         if(!this.editing){
14053             return;
14054         }
14055         var v = this.getValue();
14056         if(this.revertInvalid !== false && !this.field.isValid()){
14057             v = this.startValue;
14058             this.cancelEdit(true);
14059         }
14060         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14061             this.editing = false;
14062             this.hide();
14063             return;
14064         }
14065         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14066             this.editing = false;
14067             if(this.updateEl && this.boundEl){
14068                 this.boundEl.update(v);
14069             }
14070             if(remainVisible !== true){
14071                 this.hide();
14072             }
14073             this.fireEvent("complete", this, v, this.startValue);
14074         }
14075     },
14076
14077     // private
14078     onShow : function(){
14079         this.el.show();
14080         if(this.hideEl !== false){
14081             this.boundEl.hide();
14082         }
14083         this.field.show();
14084         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14085             this.fixIEFocus = true;
14086             this.deferredFocus.defer(50, this);
14087         }else{
14088             this.field.focus();
14089         }
14090         this.fireEvent("startedit", this.boundEl, this.startValue);
14091     },
14092
14093     deferredFocus : function(){
14094         if(this.editing){
14095             this.field.focus();
14096         }
14097     },
14098
14099     /**
14100      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14101      * reverted to the original starting value.
14102      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14103      * cancel (defaults to false)
14104      */
14105     cancelEdit : function(remainVisible){
14106         if(this.editing){
14107             this.setValue(this.startValue);
14108             if(remainVisible !== true){
14109                 this.hide();
14110             }
14111         }
14112     },
14113
14114     // private
14115     onBlur : function(){
14116         if(this.allowBlur !== true && this.editing){
14117             this.completeEdit();
14118         }
14119     },
14120
14121     // private
14122     onHide : function(){
14123         if(this.editing){
14124             this.completeEdit();
14125             return;
14126         }
14127         this.field.blur();
14128         if(this.field.collapse){
14129             this.field.collapse();
14130         }
14131         this.el.hide();
14132         if(this.hideEl !== false){
14133             this.boundEl.show();
14134         }
14135         if(Roo.QuickTips){
14136             Roo.QuickTips.enable();
14137         }
14138     },
14139
14140     /**
14141      * Sets the data value of the editor
14142      * @param {Mixed} value Any valid value supported by the underlying field
14143      */
14144     setValue : function(v){
14145         this.field.setValue(v);
14146     },
14147
14148     /**
14149      * Gets the data value of the editor
14150      * @return {Mixed} The data value
14151      */
14152     getValue : function(){
14153         return this.field.getValue();
14154     }
14155 });/*
14156  * Based on:
14157  * Ext JS Library 1.1.1
14158  * Copyright(c) 2006-2007, Ext JS, LLC.
14159  *
14160  * Originally Released Under LGPL - original licence link has changed is not relivant.
14161  *
14162  * Fork - LGPL
14163  * <script type="text/javascript">
14164  */
14165  
14166 /**
14167  * @class Roo.BasicDialog
14168  * @extends Roo.util.Observable
14169  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14170  * <pre><code>
14171 var dlg = new Roo.BasicDialog("my-dlg", {
14172     height: 200,
14173     width: 300,
14174     minHeight: 100,
14175     minWidth: 150,
14176     modal: true,
14177     proxyDrag: true,
14178     shadow: true
14179 });
14180 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14181 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14182 dlg.addButton('Cancel', dlg.hide, dlg);
14183 dlg.show();
14184 </code></pre>
14185   <b>A Dialog should always be a direct child of the body element.</b>
14186  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14187  * @cfg {String} title Default text to display in the title bar (defaults to null)
14188  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14189  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14190  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14191  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14192  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14193  * (defaults to null with no animation)
14194  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14195  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14196  * property for valid values (defaults to 'all')
14197  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14198  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14199  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14200  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14201  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14202  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14203  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14204  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14205  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14206  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14207  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14208  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14209  * draggable = true (defaults to false)
14210  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14211  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14212  * shadow (defaults to false)
14213  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14214  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14215  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14216  * @cfg {Array} buttons Array of buttons
14217  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14218  * @constructor
14219  * Create a new BasicDialog.
14220  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14221  * @param {Object} config Configuration options
14222  */
14223 Roo.BasicDialog = function(el, config){
14224     this.el = Roo.get(el);
14225     var dh = Roo.DomHelper;
14226     if(!this.el && config && config.autoCreate){
14227         if(typeof config.autoCreate == "object"){
14228             if(!config.autoCreate.id){
14229                 config.autoCreate.id = el;
14230             }
14231             this.el = dh.append(document.body,
14232                         config.autoCreate, true);
14233         }else{
14234             this.el = dh.append(document.body,
14235                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14236         }
14237     }
14238     el = this.el;
14239     el.setDisplayed(true);
14240     el.hide = this.hideAction;
14241     this.id = el.id;
14242     el.addClass("x-dlg");
14243
14244     Roo.apply(this, config);
14245
14246     this.proxy = el.createProxy("x-dlg-proxy");
14247     this.proxy.hide = this.hideAction;
14248     this.proxy.setOpacity(.5);
14249     this.proxy.hide();
14250
14251     if(config.width){
14252         el.setWidth(config.width);
14253     }
14254     if(config.height){
14255         el.setHeight(config.height);
14256     }
14257     this.size = el.getSize();
14258     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14259         this.xy = [config.x,config.y];
14260     }else{
14261         this.xy = el.getCenterXY(true);
14262     }
14263     /** The header element @type Roo.Element */
14264     this.header = el.child("> .x-dlg-hd");
14265     /** The body element @type Roo.Element */
14266     this.body = el.child("> .x-dlg-bd");
14267     /** The footer element @type Roo.Element */
14268     this.footer = el.child("> .x-dlg-ft");
14269
14270     if(!this.header){
14271         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14272     }
14273     if(!this.body){
14274         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14275     }
14276
14277     this.header.unselectable();
14278     if(this.title){
14279         this.header.update(this.title);
14280     }
14281     // this element allows the dialog to be focused for keyboard event
14282     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14283     this.focusEl.swallowEvent("click", true);
14284
14285     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14286
14287     // wrap the body and footer for special rendering
14288     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14289     if(this.footer){
14290         this.bwrap.dom.appendChild(this.footer.dom);
14291     }
14292
14293     this.bg = this.el.createChild({
14294         tag: "div", cls:"x-dlg-bg",
14295         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14296     });
14297     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14298
14299
14300     if(this.autoScroll !== false && !this.autoTabs){
14301         this.body.setStyle("overflow", "auto");
14302     }
14303
14304     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14305
14306     if(this.closable !== false){
14307         this.el.addClass("x-dlg-closable");
14308         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14309         this.close.on("click", this.closeClick, this);
14310         this.close.addClassOnOver("x-dlg-close-over");
14311     }
14312     if(this.collapsible !== false){
14313         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14314         this.collapseBtn.on("click", this.collapseClick, this);
14315         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14316         this.header.on("dblclick", this.collapseClick, this);
14317     }
14318     if(this.resizable !== false){
14319         this.el.addClass("x-dlg-resizable");
14320         this.resizer = new Roo.Resizable(el, {
14321             minWidth: this.minWidth || 80,
14322             minHeight:this.minHeight || 80,
14323             handles: this.resizeHandles || "all",
14324             pinned: true
14325         });
14326         this.resizer.on("beforeresize", this.beforeResize, this);
14327         this.resizer.on("resize", this.onResize, this);
14328     }
14329     if(this.draggable !== false){
14330         el.addClass("x-dlg-draggable");
14331         if (!this.proxyDrag) {
14332             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14333         }
14334         else {
14335             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14336         }
14337         dd.setHandleElId(this.header.id);
14338         dd.endDrag = this.endMove.createDelegate(this);
14339         dd.startDrag = this.startMove.createDelegate(this);
14340         dd.onDrag = this.onDrag.createDelegate(this);
14341         dd.scroll = false;
14342         this.dd = dd;
14343     }
14344     if(this.modal){
14345         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14346         this.mask.enableDisplayMode("block");
14347         this.mask.hide();
14348         this.el.addClass("x-dlg-modal");
14349     }
14350     if(this.shadow){
14351         this.shadow = new Roo.Shadow({
14352             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14353             offset : this.shadowOffset
14354         });
14355     }else{
14356         this.shadowOffset = 0;
14357     }
14358     if(Roo.useShims && this.shim !== false){
14359         this.shim = this.el.createShim();
14360         this.shim.hide = this.hideAction;
14361         this.shim.hide();
14362     }else{
14363         this.shim = false;
14364     }
14365     if(this.autoTabs){
14366         this.initTabs();
14367     }
14368     if (this.buttons) { 
14369         var bts= this.buttons;
14370         this.buttons = [];
14371         Roo.each(bts, function(b) {
14372             this.addButton(b);
14373         }, this);
14374     }
14375     
14376     
14377     this.addEvents({
14378         /**
14379          * @event keydown
14380          * Fires when a key is pressed
14381          * @param {Roo.BasicDialog} this
14382          * @param {Roo.EventObject} e
14383          */
14384         "keydown" : true,
14385         /**
14386          * @event move
14387          * Fires when this dialog is moved by the user.
14388          * @param {Roo.BasicDialog} this
14389          * @param {Number} x The new page X
14390          * @param {Number} y The new page Y
14391          */
14392         "move" : true,
14393         /**
14394          * @event resize
14395          * Fires when this dialog is resized by the user.
14396          * @param {Roo.BasicDialog} this
14397          * @param {Number} width The new width
14398          * @param {Number} height The new height
14399          */
14400         "resize" : true,
14401         /**
14402          * @event beforehide
14403          * Fires before this dialog is hidden.
14404          * @param {Roo.BasicDialog} this
14405          */
14406         "beforehide" : true,
14407         /**
14408          * @event hide
14409          * Fires when this dialog is hidden.
14410          * @param {Roo.BasicDialog} this
14411          */
14412         "hide" : true,
14413         /**
14414          * @event beforeshow
14415          * Fires before this dialog is shown.
14416          * @param {Roo.BasicDialog} this
14417          */
14418         "beforeshow" : true,
14419         /**
14420          * @event show
14421          * Fires when this dialog is shown.
14422          * @param {Roo.BasicDialog} this
14423          */
14424         "show" : true
14425     });
14426     el.on("keydown", this.onKeyDown, this);
14427     el.on("mousedown", this.toFront, this);
14428     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14429     this.el.hide();
14430     Roo.DialogManager.register(this);
14431     Roo.BasicDialog.superclass.constructor.call(this);
14432 };
14433
14434 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14435     shadowOffset: Roo.isIE ? 6 : 5,
14436     minHeight: 80,
14437     minWidth: 200,
14438     minButtonWidth: 75,
14439     defaultButton: null,
14440     buttonAlign: "right",
14441     tabTag: 'div',
14442     firstShow: true,
14443
14444     /**
14445      * Sets the dialog title text
14446      * @param {String} text The title text to display
14447      * @return {Roo.BasicDialog} this
14448      */
14449     setTitle : function(text){
14450         this.header.update(text);
14451         return this;
14452     },
14453
14454     // private
14455     closeClick : function(){
14456         this.hide();
14457     },
14458
14459     // private
14460     collapseClick : function(){
14461         this[this.collapsed ? "expand" : "collapse"]();
14462     },
14463
14464     /**
14465      * Collapses the dialog to its minimized state (only the title bar is visible).
14466      * Equivalent to the user clicking the collapse dialog button.
14467      */
14468     collapse : function(){
14469         if(!this.collapsed){
14470             this.collapsed = true;
14471             this.el.addClass("x-dlg-collapsed");
14472             this.restoreHeight = this.el.getHeight();
14473             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14474         }
14475     },
14476
14477     /**
14478      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14479      * clicking the expand dialog button.
14480      */
14481     expand : function(){
14482         if(this.collapsed){
14483             this.collapsed = false;
14484             this.el.removeClass("x-dlg-collapsed");
14485             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14486         }
14487     },
14488
14489     /**
14490      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14491      * @return {Roo.TabPanel} The tabs component
14492      */
14493     initTabs : function(){
14494         var tabs = this.getTabs();
14495         while(tabs.getTab(0)){
14496             tabs.removeTab(0);
14497         }
14498         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14499             var dom = el.dom;
14500             tabs.addTab(Roo.id(dom), dom.title);
14501             dom.title = "";
14502         });
14503         tabs.activate(0);
14504         return tabs;
14505     },
14506
14507     // private
14508     beforeResize : function(){
14509         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14510     },
14511
14512     // private
14513     onResize : function(){
14514         this.refreshSize();
14515         this.syncBodyHeight();
14516         this.adjustAssets();
14517         this.focus();
14518         this.fireEvent("resize", this, this.size.width, this.size.height);
14519     },
14520
14521     // private
14522     onKeyDown : function(e){
14523         if(this.isVisible()){
14524             this.fireEvent("keydown", this, e);
14525         }
14526     },
14527
14528     /**
14529      * Resizes the dialog.
14530      * @param {Number} width
14531      * @param {Number} height
14532      * @return {Roo.BasicDialog} this
14533      */
14534     resizeTo : function(width, height){
14535         this.el.setSize(width, height);
14536         this.size = {width: width, height: height};
14537         this.syncBodyHeight();
14538         if(this.fixedcenter){
14539             this.center();
14540         }
14541         if(this.isVisible()){
14542             this.constrainXY();
14543             this.adjustAssets();
14544         }
14545         this.fireEvent("resize", this, width, height);
14546         return this;
14547     },
14548
14549
14550     /**
14551      * Resizes the dialog to fit the specified content size.
14552      * @param {Number} width
14553      * @param {Number} height
14554      * @return {Roo.BasicDialog} this
14555      */
14556     setContentSize : function(w, h){
14557         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14558         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14559         //if(!this.el.isBorderBox()){
14560             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14561             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14562         //}
14563         if(this.tabs){
14564             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14565             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14566         }
14567         this.resizeTo(w, h);
14568         return this;
14569     },
14570
14571     /**
14572      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14573      * executed in response to a particular key being pressed while the dialog is active.
14574      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14575      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14576      * @param {Function} fn The function to call
14577      * @param {Object} scope (optional) The scope of the function
14578      * @return {Roo.BasicDialog} this
14579      */
14580     addKeyListener : function(key, fn, scope){
14581         var keyCode, shift, ctrl, alt;
14582         if(typeof key == "object" && !(key instanceof Array)){
14583             keyCode = key["key"];
14584             shift = key["shift"];
14585             ctrl = key["ctrl"];
14586             alt = key["alt"];
14587         }else{
14588             keyCode = key;
14589         }
14590         var handler = function(dlg, e){
14591             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14592                 var k = e.getKey();
14593                 if(keyCode instanceof Array){
14594                     for(var i = 0, len = keyCode.length; i < len; i++){
14595                         if(keyCode[i] == k){
14596                           fn.call(scope || window, dlg, k, e);
14597                           return;
14598                         }
14599                     }
14600                 }else{
14601                     if(k == keyCode){
14602                         fn.call(scope || window, dlg, k, e);
14603                     }
14604                 }
14605             }
14606         };
14607         this.on("keydown", handler);
14608         return this;
14609     },
14610
14611     /**
14612      * Returns the TabPanel component (creates it if it doesn't exist).
14613      * Note: If you wish to simply check for the existence of tabs without creating them,
14614      * check for a null 'tabs' property.
14615      * @return {Roo.TabPanel} The tabs component
14616      */
14617     getTabs : function(){
14618         if(!this.tabs){
14619             this.el.addClass("x-dlg-auto-tabs");
14620             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14621             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14622         }
14623         return this.tabs;
14624     },
14625
14626     /**
14627      * Adds a button to the footer section of the dialog.
14628      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14629      * object or a valid Roo.DomHelper element config
14630      * @param {Function} handler The function called when the button is clicked
14631      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14632      * @return {Roo.Button} The new button
14633      */
14634     addButton : function(config, handler, scope){
14635         var dh = Roo.DomHelper;
14636         if(!this.footer){
14637             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14638         }
14639         if(!this.btnContainer){
14640             var tb = this.footer.createChild({
14641
14642                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14643                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14644             }, null, true);
14645             this.btnContainer = tb.firstChild.firstChild.firstChild;
14646         }
14647         var bconfig = {
14648             handler: handler,
14649             scope: scope,
14650             minWidth: this.minButtonWidth,
14651             hideParent:true
14652         };
14653         if(typeof config == "string"){
14654             bconfig.text = config;
14655         }else{
14656             if(config.tag){
14657                 bconfig.dhconfig = config;
14658             }else{
14659                 Roo.apply(bconfig, config);
14660             }
14661         }
14662         var fc = false;
14663         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14664             bconfig.position = Math.max(0, bconfig.position);
14665             fc = this.btnContainer.childNodes[bconfig.position];
14666         }
14667          
14668         var btn = new Roo.Button(
14669             fc ? 
14670                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14671                 : this.btnContainer.appendChild(document.createElement("td")),
14672             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14673             bconfig
14674         );
14675         this.syncBodyHeight();
14676         if(!this.buttons){
14677             /**
14678              * Array of all the buttons that have been added to this dialog via addButton
14679              * @type Array
14680              */
14681             this.buttons = [];
14682         }
14683         this.buttons.push(btn);
14684         return btn;
14685     },
14686
14687     /**
14688      * Sets the default button to be focused when the dialog is displayed.
14689      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14690      * @return {Roo.BasicDialog} this
14691      */
14692     setDefaultButton : function(btn){
14693         this.defaultButton = btn;
14694         return this;
14695     },
14696
14697     // private
14698     getHeaderFooterHeight : function(safe){
14699         var height = 0;
14700         if(this.header){
14701            height += this.header.getHeight();
14702         }
14703         if(this.footer){
14704            var fm = this.footer.getMargins();
14705             height += (this.footer.getHeight()+fm.top+fm.bottom);
14706         }
14707         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14708         height += this.centerBg.getPadding("tb");
14709         return height;
14710     },
14711
14712     // private
14713     syncBodyHeight : function(){
14714         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14715         var height = this.size.height - this.getHeaderFooterHeight(false);
14716         bd.setHeight(height-bd.getMargins("tb"));
14717         var hh = this.header.getHeight();
14718         var h = this.size.height-hh;
14719         cb.setHeight(h);
14720         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14721         bw.setHeight(h-cb.getPadding("tb"));
14722         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14723         bd.setWidth(bw.getWidth(true));
14724         if(this.tabs){
14725             this.tabs.syncHeight();
14726             if(Roo.isIE){
14727                 this.tabs.el.repaint();
14728             }
14729         }
14730     },
14731
14732     /**
14733      * Restores the previous state of the dialog if Roo.state is configured.
14734      * @return {Roo.BasicDialog} this
14735      */
14736     restoreState : function(){
14737         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14738         if(box && box.width){
14739             this.xy = [box.x, box.y];
14740             this.resizeTo(box.width, box.height);
14741         }
14742         return this;
14743     },
14744
14745     // private
14746     beforeShow : function(){
14747         this.expand();
14748         if(this.fixedcenter){
14749             this.xy = this.el.getCenterXY(true);
14750         }
14751         if(this.modal){
14752             Roo.get(document.body).addClass("x-body-masked");
14753             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14754             this.mask.show();
14755         }
14756         this.constrainXY();
14757     },
14758
14759     // private
14760     animShow : function(){
14761         var b = Roo.get(this.animateTarget).getBox();
14762         this.proxy.setSize(b.width, b.height);
14763         this.proxy.setLocation(b.x, b.y);
14764         this.proxy.show();
14765         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14766                     true, .35, this.showEl.createDelegate(this));
14767     },
14768
14769     /**
14770      * Shows the dialog.
14771      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14772      * @return {Roo.BasicDialog} this
14773      */
14774     show : function(animateTarget){
14775         if (this.fireEvent("beforeshow", this) === false){
14776             return;
14777         }
14778         if(this.syncHeightBeforeShow){
14779             this.syncBodyHeight();
14780         }else if(this.firstShow){
14781             this.firstShow = false;
14782             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14783         }
14784         this.animateTarget = animateTarget || this.animateTarget;
14785         if(!this.el.isVisible()){
14786             this.beforeShow();
14787             if(this.animateTarget && Roo.get(this.animateTarget)){
14788                 this.animShow();
14789             }else{
14790                 this.showEl();
14791             }
14792         }
14793         return this;
14794     },
14795
14796     // private
14797     showEl : function(){
14798         this.proxy.hide();
14799         this.el.setXY(this.xy);
14800         this.el.show();
14801         this.adjustAssets(true);
14802         this.toFront();
14803         this.focus();
14804         // IE peekaboo bug - fix found by Dave Fenwick
14805         if(Roo.isIE){
14806             this.el.repaint();
14807         }
14808         this.fireEvent("show", this);
14809     },
14810
14811     /**
14812      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14813      * dialog itself will receive focus.
14814      */
14815     focus : function(){
14816         if(this.defaultButton){
14817             this.defaultButton.focus();
14818         }else{
14819             this.focusEl.focus();
14820         }
14821     },
14822
14823     // private
14824     constrainXY : function(){
14825         if(this.constraintoviewport !== false){
14826             if(!this.viewSize){
14827                 if(this.container){
14828                     var s = this.container.getSize();
14829                     this.viewSize = [s.width, s.height];
14830                 }else{
14831                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14832                 }
14833             }
14834             var s = Roo.get(this.container||document).getScroll();
14835
14836             var x = this.xy[0], y = this.xy[1];
14837             var w = this.size.width, h = this.size.height;
14838             var vw = this.viewSize[0], vh = this.viewSize[1];
14839             // only move it if it needs it
14840             var moved = false;
14841             // first validate right/bottom
14842             if(x + w > vw+s.left){
14843                 x = vw - w;
14844                 moved = true;
14845             }
14846             if(y + h > vh+s.top){
14847                 y = vh - h;
14848                 moved = true;
14849             }
14850             // then make sure top/left isn't negative
14851             if(x < s.left){
14852                 x = s.left;
14853                 moved = true;
14854             }
14855             if(y < s.top){
14856                 y = s.top;
14857                 moved = true;
14858             }
14859             if(moved){
14860                 // cache xy
14861                 this.xy = [x, y];
14862                 if(this.isVisible()){
14863                     this.el.setLocation(x, y);
14864                     this.adjustAssets();
14865                 }
14866             }
14867         }
14868     },
14869
14870     // private
14871     onDrag : function(){
14872         if(!this.proxyDrag){
14873             this.xy = this.el.getXY();
14874             this.adjustAssets();
14875         }
14876     },
14877
14878     // private
14879     adjustAssets : function(doShow){
14880         var x = this.xy[0], y = this.xy[1];
14881         var w = this.size.width, h = this.size.height;
14882         if(doShow === true){
14883             if(this.shadow){
14884                 this.shadow.show(this.el);
14885             }
14886             if(this.shim){
14887                 this.shim.show();
14888             }
14889         }
14890         if(this.shadow && this.shadow.isVisible()){
14891             this.shadow.show(this.el);
14892         }
14893         if(this.shim && this.shim.isVisible()){
14894             this.shim.setBounds(x, y, w, h);
14895         }
14896     },
14897
14898     // private
14899     adjustViewport : function(w, h){
14900         if(!w || !h){
14901             w = Roo.lib.Dom.getViewWidth();
14902             h = Roo.lib.Dom.getViewHeight();
14903         }
14904         // cache the size
14905         this.viewSize = [w, h];
14906         if(this.modal && this.mask.isVisible()){
14907             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14908             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14909         }
14910         if(this.isVisible()){
14911             this.constrainXY();
14912         }
14913     },
14914
14915     /**
14916      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14917      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14918      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14919      */
14920     destroy : function(removeEl){
14921         if(this.isVisible()){
14922             this.animateTarget = null;
14923             this.hide();
14924         }
14925         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14926         if(this.tabs){
14927             this.tabs.destroy(removeEl);
14928         }
14929         Roo.destroy(
14930              this.shim,
14931              this.proxy,
14932              this.resizer,
14933              this.close,
14934              this.mask
14935         );
14936         if(this.dd){
14937             this.dd.unreg();
14938         }
14939         if(this.buttons){
14940            for(var i = 0, len = this.buttons.length; i < len; i++){
14941                this.buttons[i].destroy();
14942            }
14943         }
14944         this.el.removeAllListeners();
14945         if(removeEl === true){
14946             this.el.update("");
14947             this.el.remove();
14948         }
14949         Roo.DialogManager.unregister(this);
14950     },
14951
14952     // private
14953     startMove : function(){
14954         if(this.proxyDrag){
14955             this.proxy.show();
14956         }
14957         if(this.constraintoviewport !== false){
14958             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14959         }
14960     },
14961
14962     // private
14963     endMove : function(){
14964         if(!this.proxyDrag){
14965             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14966         }else{
14967             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14968             this.proxy.hide();
14969         }
14970         this.refreshSize();
14971         this.adjustAssets();
14972         this.focus();
14973         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14974     },
14975
14976     /**
14977      * Brings this dialog to the front of any other visible dialogs
14978      * @return {Roo.BasicDialog} this
14979      */
14980     toFront : function(){
14981         Roo.DialogManager.bringToFront(this);
14982         return this;
14983     },
14984
14985     /**
14986      * Sends this dialog to the back (under) of any other visible dialogs
14987      * @return {Roo.BasicDialog} this
14988      */
14989     toBack : function(){
14990         Roo.DialogManager.sendToBack(this);
14991         return this;
14992     },
14993
14994     /**
14995      * Centers this dialog in the viewport
14996      * @return {Roo.BasicDialog} this
14997      */
14998     center : function(){
14999         var xy = this.el.getCenterXY(true);
15000         this.moveTo(xy[0], xy[1]);
15001         return this;
15002     },
15003
15004     /**
15005      * Moves the dialog's top-left corner to the specified point
15006      * @param {Number} x
15007      * @param {Number} y
15008      * @return {Roo.BasicDialog} this
15009      */
15010     moveTo : function(x, y){
15011         this.xy = [x,y];
15012         if(this.isVisible()){
15013             this.el.setXY(this.xy);
15014             this.adjustAssets();
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Aligns the dialog to the specified element
15021      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15022      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15023      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15024      * @return {Roo.BasicDialog} this
15025      */
15026     alignTo : function(element, position, offsets){
15027         this.xy = this.el.getAlignToXY(element, position, offsets);
15028         if(this.isVisible()){
15029             this.el.setXY(this.xy);
15030             this.adjustAssets();
15031         }
15032         return this;
15033     },
15034
15035     /**
15036      * Anchors an element to another element and realigns it when the window is resized.
15037      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15038      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15039      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15040      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15041      * is a number, it is used as the buffer delay (defaults to 50ms).
15042      * @return {Roo.BasicDialog} this
15043      */
15044     anchorTo : function(el, alignment, offsets, monitorScroll){
15045         var action = function(){
15046             this.alignTo(el, alignment, offsets);
15047         };
15048         Roo.EventManager.onWindowResize(action, this);
15049         var tm = typeof monitorScroll;
15050         if(tm != 'undefined'){
15051             Roo.EventManager.on(window, 'scroll', action, this,
15052                 {buffer: tm == 'number' ? monitorScroll : 50});
15053         }
15054         action.call(this);
15055         return this;
15056     },
15057
15058     /**
15059      * Returns true if the dialog is visible
15060      * @return {Boolean}
15061      */
15062     isVisible : function(){
15063         return this.el.isVisible();
15064     },
15065
15066     // private
15067     animHide : function(callback){
15068         var b = Roo.get(this.animateTarget).getBox();
15069         this.proxy.show();
15070         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15071         this.el.hide();
15072         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15073                     this.hideEl.createDelegate(this, [callback]));
15074     },
15075
15076     /**
15077      * Hides the dialog.
15078      * @param {Function} callback (optional) Function to call when the dialog is hidden
15079      * @return {Roo.BasicDialog} this
15080      */
15081     hide : function(callback){
15082         if (this.fireEvent("beforehide", this) === false){
15083             return;
15084         }
15085         if(this.shadow){
15086             this.shadow.hide();
15087         }
15088         if(this.shim) {
15089           this.shim.hide();
15090         }
15091         // sometimes animateTarget seems to get set.. causing problems...
15092         // this just double checks..
15093         if(this.animateTarget && Roo.get(this.animateTarget)) {
15094            this.animHide(callback);
15095         }else{
15096             this.el.hide();
15097             this.hideEl(callback);
15098         }
15099         return this;
15100     },
15101
15102     // private
15103     hideEl : function(callback){
15104         this.proxy.hide();
15105         if(this.modal){
15106             this.mask.hide();
15107             Roo.get(document.body).removeClass("x-body-masked");
15108         }
15109         this.fireEvent("hide", this);
15110         if(typeof callback == "function"){
15111             callback();
15112         }
15113     },
15114
15115     // private
15116     hideAction : function(){
15117         this.setLeft("-10000px");
15118         this.setTop("-10000px");
15119         this.setStyle("visibility", "hidden");
15120     },
15121
15122     // private
15123     refreshSize : function(){
15124         this.size = this.el.getSize();
15125         this.xy = this.el.getXY();
15126         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15127     },
15128
15129     // private
15130     // z-index is managed by the DialogManager and may be overwritten at any time
15131     setZIndex : function(index){
15132         if(this.modal){
15133             this.mask.setStyle("z-index", index);
15134         }
15135         if(this.shim){
15136             this.shim.setStyle("z-index", ++index);
15137         }
15138         if(this.shadow){
15139             this.shadow.setZIndex(++index);
15140         }
15141         this.el.setStyle("z-index", ++index);
15142         if(this.proxy){
15143             this.proxy.setStyle("z-index", ++index);
15144         }
15145         if(this.resizer){
15146             this.resizer.proxy.setStyle("z-index", ++index);
15147         }
15148
15149         this.lastZIndex = index;
15150     },
15151
15152     /**
15153      * Returns the element for this dialog
15154      * @return {Roo.Element} The underlying dialog Element
15155      */
15156     getEl : function(){
15157         return this.el;
15158     }
15159 });
15160
15161 /**
15162  * @class Roo.DialogManager
15163  * Provides global access to BasicDialogs that have been created and
15164  * support for z-indexing (layering) multiple open dialogs.
15165  */
15166 Roo.DialogManager = function(){
15167     var list = {};
15168     var accessList = [];
15169     var front = null;
15170
15171     // private
15172     var sortDialogs = function(d1, d2){
15173         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15174     };
15175
15176     // private
15177     var orderDialogs = function(){
15178         accessList.sort(sortDialogs);
15179         var seed = Roo.DialogManager.zseed;
15180         for(var i = 0, len = accessList.length; i < len; i++){
15181             var dlg = accessList[i];
15182             if(dlg){
15183                 dlg.setZIndex(seed + (i*10));
15184             }
15185         }
15186     };
15187
15188     return {
15189         /**
15190          * The starting z-index for BasicDialogs (defaults to 9000)
15191          * @type Number The z-index value
15192          */
15193         zseed : 9000,
15194
15195         // private
15196         register : function(dlg){
15197             list[dlg.id] = dlg;
15198             accessList.push(dlg);
15199         },
15200
15201         // private
15202         unregister : function(dlg){
15203             delete list[dlg.id];
15204             var i=0;
15205             var len=0;
15206             if(!accessList.indexOf){
15207                 for(  i = 0, len = accessList.length; i < len; i++){
15208                     if(accessList[i] == dlg){
15209                         accessList.splice(i, 1);
15210                         return;
15211                     }
15212                 }
15213             }else{
15214                  i = accessList.indexOf(dlg);
15215                 if(i != -1){
15216                     accessList.splice(i, 1);
15217                 }
15218             }
15219         },
15220
15221         /**
15222          * Gets a registered dialog by id
15223          * @param {String/Object} id The id of the dialog or a dialog
15224          * @return {Roo.BasicDialog} this
15225          */
15226         get : function(id){
15227             return typeof id == "object" ? id : list[id];
15228         },
15229
15230         /**
15231          * Brings the specified dialog to the front
15232          * @param {String/Object} dlg The id of the dialog or a dialog
15233          * @return {Roo.BasicDialog} this
15234          */
15235         bringToFront : function(dlg){
15236             dlg = this.get(dlg);
15237             if(dlg != front){
15238                 front = dlg;
15239                 dlg._lastAccess = new Date().getTime();
15240                 orderDialogs();
15241             }
15242             return dlg;
15243         },
15244
15245         /**
15246          * Sends the specified dialog to the back
15247          * @param {String/Object} dlg The id of the dialog or a dialog
15248          * @return {Roo.BasicDialog} this
15249          */
15250         sendToBack : function(dlg){
15251             dlg = this.get(dlg);
15252             dlg._lastAccess = -(new Date().getTime());
15253             orderDialogs();
15254             return dlg;
15255         },
15256
15257         /**
15258          * Hides all dialogs
15259          */
15260         hideAll : function(){
15261             for(var id in list){
15262                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15263                     list[id].hide();
15264                 }
15265             }
15266         }
15267     };
15268 }();
15269
15270 /**
15271  * @class Roo.LayoutDialog
15272  * @extends Roo.BasicDialog
15273  * Dialog which provides adjustments for working with a layout in a Dialog.
15274  * Add your necessary layout config options to the dialog's config.<br>
15275  * Example usage (including a nested layout):
15276  * <pre><code>
15277 if(!dialog){
15278     dialog = new Roo.LayoutDialog("download-dlg", {
15279         modal: true,
15280         width:600,
15281         height:450,
15282         shadow:true,
15283         minWidth:500,
15284         minHeight:350,
15285         autoTabs:true,
15286         proxyDrag:true,
15287         // layout config merges with the dialog config
15288         center:{
15289             tabPosition: "top",
15290             alwaysShowTabs: true
15291         }
15292     });
15293     dialog.addKeyListener(27, dialog.hide, dialog);
15294     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15295     dialog.addButton("Build It!", this.getDownload, this);
15296
15297     // we can even add nested layouts
15298     var innerLayout = new Roo.BorderLayout("dl-inner", {
15299         east: {
15300             initialSize: 200,
15301             autoScroll:true,
15302             split:true
15303         },
15304         center: {
15305             autoScroll:true
15306         }
15307     });
15308     innerLayout.beginUpdate();
15309     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15310     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15311     innerLayout.endUpdate(true);
15312
15313     var layout = dialog.getLayout();
15314     layout.beginUpdate();
15315     layout.add("center", new Roo.ContentPanel("standard-panel",
15316                         {title: "Download the Source", fitToFrame:true}));
15317     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15318                {title: "Build your own roo.js"}));
15319     layout.getRegion("center").showPanel(sp);
15320     layout.endUpdate();
15321 }
15322 </code></pre>
15323     * @constructor
15324     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15325     * @param {Object} config configuration options
15326   */
15327 Roo.LayoutDialog = function(el, cfg){
15328     
15329     var config=  cfg;
15330     if (typeof(cfg) == 'undefined') {
15331         config = Roo.apply({}, el);
15332         // not sure why we use documentElement here.. - it should always be body.
15333         // IE7 borks horribly if we use documentElement.
15334         // webkit also does not like documentElement - it creates a body element...
15335         el = Roo.get( document.body || document.documentElement ).createChild();
15336         //config.autoCreate = true;
15337     }
15338     
15339     
15340     config.autoTabs = false;
15341     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15342     this.body.setStyle({overflow:"hidden", position:"relative"});
15343     this.layout = new Roo.BorderLayout(this.body.dom, config);
15344     this.layout.monitorWindowResize = false;
15345     this.el.addClass("x-dlg-auto-layout");
15346     // fix case when center region overwrites center function
15347     this.center = Roo.BasicDialog.prototype.center;
15348     this.on("show", this.layout.layout, this.layout, true);
15349     if (config.items) {
15350         var xitems = config.items;
15351         delete config.items;
15352         Roo.each(xitems, this.addxtype, this);
15353     }
15354     
15355     
15356 };
15357 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15358     /**
15359      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15360      * @deprecated
15361      */
15362     endUpdate : function(){
15363         this.layout.endUpdate();
15364     },
15365
15366     /**
15367      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15368      *  @deprecated
15369      */
15370     beginUpdate : function(){
15371         this.layout.beginUpdate();
15372     },
15373
15374     /**
15375      * Get the BorderLayout for this dialog
15376      * @return {Roo.BorderLayout}
15377      */
15378     getLayout : function(){
15379         return this.layout;
15380     },
15381
15382     showEl : function(){
15383         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15384         if(Roo.isIE7){
15385             this.layout.layout();
15386         }
15387     },
15388
15389     // private
15390     // Use the syncHeightBeforeShow config option to control this automatically
15391     syncBodyHeight : function(){
15392         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15393         if(this.layout){this.layout.layout();}
15394     },
15395     
15396       /**
15397      * Add an xtype element (actually adds to the layout.)
15398      * @return {Object} xdata xtype object data.
15399      */
15400     
15401     addxtype : function(c) {
15402         return this.layout.addxtype(c);
15403     }
15404 });