roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     
76 };
77
78 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
79
80     /**
81      * The id of the element associated with this object.  This is what we
82      * refer to as the "linked element" because the size and position of
83      * this element is used to determine when the drag and drop objects have
84      * interacted.
85      * @property id
86      * @type String
87      */
88     id: null,
89
90     /**
91      * Configuration attributes passed into the constructor
92      * @property config
93      * @type object
94      */
95     config: null,
96
97     /**
98      * The id of the element that will be dragged.  By default this is same
99      * as the linked element , but could be changed to another element. Ex:
100      * Roo.dd.DDProxy
101      * @property dragElId
102      * @type String
103      * @private
104      */
105     dragElId: null,
106
107     /**
108      * the id of the element that initiates the drag operation.  By default
109      * this is the linked element, but could be changed to be a child of this
110      * element.  This lets us do things like only starting the drag when the
111      * header element within the linked html element is clicked.
112      * @property handleElId
113      * @type String
114      * @private
115      */
116     handleElId: null,
117
118     /**
119      * An associative array of HTML tags that will be ignored if clicked.
120      * @property invalidHandleTypes
121      * @type {string: string}
122      */
123     invalidHandleTypes: null,
124
125     /**
126      * An associative array of ids for elements that will be ignored if clicked
127      * @property invalidHandleIds
128      * @type {string: string}
129      */
130     invalidHandleIds: null,
131
132     /**
133      * An indexted array of css class names for elements that will be ignored
134      * if clicked.
135      * @property invalidHandleClasses
136      * @type string[]
137      */
138     invalidHandleClasses: null,
139
140     /**
141      * The linked element's absolute X position at the time the drag was
142      * started
143      * @property startPageX
144      * @type int
145      * @private
146      */
147     startPageX: 0,
148
149     /**
150      * The linked element's absolute X position at the time the drag was
151      * started
152      * @property startPageY
153      * @type int
154      * @private
155      */
156     startPageY: 0,
157
158     /**
159      * The group defines a logical collection of DragDrop objects that are
160      * related.  Instances only get events when interacting with other
161      * DragDrop object in the same group.  This lets us define multiple
162      * groups using a single DragDrop subclass if we want.
163      * @property groups
164      * @type {string: string}
165      */
166     groups: null,
167
168     /**
169      * Individual drag/drop instances can be locked.  This will prevent
170      * onmousedown start drag.
171      * @property locked
172      * @type boolean
173      * @private
174      */
175     locked: false,
176
177     /**
178      * Lock this instance
179      * @method lock
180      */
181     lock: function() { this.locked = true; },
182
183     /**
184      * Unlock this instace
185      * @method unlock
186      */
187     unlock: function() { this.locked = false; },
188
189     /**
190      * By default, all insances can be a drop target.  This can be disabled by
191      * setting isTarget to false.
192      * @method isTarget
193      * @type boolean
194      */
195     isTarget: true,
196
197     /**
198      * The padding configured for this drag and drop object for calculating
199      * the drop zone intersection with this object.
200      * @method padding
201      * @type int[]
202      */
203     padding: null,
204
205     /**
206      * Cached reference to the linked element
207      * @property _domRef
208      * @private
209      */
210     _domRef: null,
211
212     /**
213      * Internal typeof flag
214      * @property __ygDragDrop
215      * @private
216      */
217     __ygDragDrop: true,
218
219     /**
220      * Set to true when horizontal contraints are applied
221      * @property constrainX
222      * @type boolean
223      * @private
224      */
225     constrainX: false,
226
227     /**
228      * Set to true when vertical contraints are applied
229      * @property constrainY
230      * @type boolean
231      * @private
232      */
233     constrainY: false,
234
235     /**
236      * The left constraint
237      * @property minX
238      * @type int
239      * @private
240      */
241     minX: 0,
242
243     /**
244      * The right constraint
245      * @property maxX
246      * @type int
247      * @private
248      */
249     maxX: 0,
250
251     /**
252      * The up constraint
253      * @property minY
254      * @type int
255      * @type int
256      * @private
257      */
258     minY: 0,
259
260     /**
261      * The down constraint
262      * @property maxY
263      * @type int
264      * @private
265      */
266     maxY: 0,
267
268     /**
269      * Maintain offsets when we resetconstraints.  Set to true when you want
270      * the position of the element relative to its parent to stay the same
271      * when the page changes
272      *
273      * @property maintainOffset
274      * @type boolean
275      */
276     maintainOffset: false,
277
278     /**
279      * Array of pixel locations the element will snap to if we specified a
280      * horizontal graduation/interval.  This array is generated automatically
281      * when you define a tick interval.
282      * @property xTicks
283      * @type int[]
284      */
285     xTicks: null,
286
287     /**
288      * Array of pixel locations the element will snap to if we specified a
289      * vertical graduation/interval.  This array is generated automatically
290      * when you define a tick interval.
291      * @property yTicks
292      * @type int[]
293      */
294     yTicks: null,
295
296     /**
297      * By default the drag and drop instance will only respond to the primary
298      * button click (left button for a right-handed mouse).  Set to true to
299      * allow drag and drop to start with any mouse click that is propogated
300      * by the browser
301      * @property primaryButtonOnly
302      * @type boolean
303      */
304     primaryButtonOnly: true,
305
306     /**
307      * The availabe property is false until the linked dom element is accessible.
308      * @property available
309      * @type boolean
310      */
311     available: false,
312
313     /**
314      * By default, drags can only be initiated if the mousedown occurs in the
315      * region the linked element is.  This is done in part to work around a
316      * bug in some browsers that mis-report the mousedown if the previous
317      * mouseup happened outside of the window.  This property is set to true
318      * if outer handles are defined.
319      *
320      * @property hasOuterHandles
321      * @type boolean
322      * @default false
323      */
324     hasOuterHandles: false,
325
326     /**
327      * Code that executes immediately before the startDrag event
328      * @method b4StartDrag
329      * @private
330      */
331     b4StartDrag: function(x, y) { },
332
333     /**
334      * Abstract method called after a drag/drop object is clicked
335      * and the drag or mousedown time thresholds have beeen met.
336      * @method startDrag
337      * @param {int} X click location
338      * @param {int} Y click location
339      */
340     startDrag: function(x, y) { /* override this */ },
341
342     /**
343      * Code that executes immediately before the onDrag event
344      * @method b4Drag
345      * @private
346      */
347     b4Drag: function(e) { },
348
349     /**
350      * Abstract method called during the onMouseMove event while dragging an
351      * object.
352      * @method onDrag
353      * @param {Event} e the mousemove event
354      */
355     onDrag: function(e) { /* override this */ },
356
357     /**
358      * Abstract method called when this element fist begins hovering over
359      * another DragDrop obj
360      * @method onDragEnter
361      * @param {Event} e the mousemove event
362      * @param {String|DragDrop[]} id In POINT mode, the element
363      * id this is hovering over.  In INTERSECT mode, an array of one or more
364      * dragdrop items being hovered over.
365      */
366     onDragEnter: function(e, id) { /* override this */ },
367
368     /**
369      * Code that executes immediately before the onDragOver event
370      * @method b4DragOver
371      * @private
372      */
373     b4DragOver: function(e) { },
374
375     /**
376      * Abstract method called when this element is hovering over another
377      * DragDrop obj
378      * @method onDragOver
379      * @param {Event} e the mousemove event
380      * @param {String|DragDrop[]} id In POINT mode, the element
381      * id this is hovering over.  In INTERSECT mode, an array of dd items
382      * being hovered over.
383      */
384     onDragOver: function(e, id) { /* override this */ },
385
386     /**
387      * Code that executes immediately before the onDragOut event
388      * @method b4DragOut
389      * @private
390      */
391     b4DragOut: function(e) { },
392
393     /**
394      * Abstract method called when we are no longer hovering over an element
395      * @method onDragOut
396      * @param {Event} e the mousemove event
397      * @param {String|DragDrop[]} id In POINT mode, the element
398      * id this was hovering over.  In INTERSECT mode, an array of dd items
399      * that the mouse is no longer over.
400      */
401     onDragOut: function(e, id) { /* override this */ },
402
403     /**
404      * Code that executes immediately before the onDragDrop event
405      * @method b4DragDrop
406      * @private
407      */
408     b4DragDrop: function(e) { },
409
410     /**
411      * Abstract method called when this item is dropped on another DragDrop
412      * obj
413      * @method onDragDrop
414      * @param {Event} e the mouseup event
415      * @param {String|DragDrop[]} id In POINT mode, the element
416      * id this was dropped on.  In INTERSECT mode, an array of dd items this
417      * was dropped on.
418      */
419     onDragDrop: function(e, id) { /* override this */ },
420
421     /**
422      * Abstract method called when this item is dropped on an area with no
423      * drop target
424      * @method onInvalidDrop
425      * @param {Event} e the mouseup event
426      */
427     onInvalidDrop: function(e) { /* override this */ },
428
429     /**
430      * Code that executes immediately before the endDrag event
431      * @method b4EndDrag
432      * @private
433      */
434     b4EndDrag: function(e) { },
435
436     /**
437      * Fired when we are done dragging the object
438      * @method endDrag
439      * @param {Event} e the mouseup event
440      */
441     endDrag: function(e) { /* override this */ },
442
443     /**
444      * Code executed immediately before the onMouseDown event
445      * @method b4MouseDown
446      * @param {Event} e the mousedown event
447      * @private
448      */
449     b4MouseDown: function(e) {  },
450
451     /**
452      * Event handler that fires when a drag/drop obj gets a mousedown
453      * @method onMouseDown
454      * @param {Event} e the mousedown event
455      */
456     onMouseDown: function(e) { /* override this */ },
457
458     /**
459      * Event handler that fires when a drag/drop obj gets a mouseup
460      * @method onMouseUp
461      * @param {Event} e the mouseup event
462      */
463     onMouseUp: function(e) { /* override this */ },
464
465     /**
466      * Override the onAvailable method to do what is needed after the initial
467      * position was determined.
468      * @method onAvailable
469      */
470     onAvailable: function () {
471     },
472
473     /*
474      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
475      * @type Object
476      */
477     defaultPadding : {left:0, right:0, top:0, bottom:0},
478
479     /*
480      * Initializes the drag drop object's constraints to restrict movement to a certain element.
481  *
482  * Usage:
483  <pre><code>
484  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
485                 { dragElId: "existingProxyDiv" });
486  dd.startDrag = function(){
487      this.constrainTo("parent-id");
488  };
489  </code></pre>
490  * Or you can initalize it using the {@link Roo.Element} object:
491  <pre><code>
492  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
493      startDrag : function(){
494          this.constrainTo("parent-id");
495      }
496  });
497  </code></pre>
498      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
499      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
500      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
501      * an object containing the sides to pad. For example: {right:10, bottom:10}
502      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
503      */
504     constrainTo : function(constrainTo, pad, inContent){
505         if(typeof pad == "number"){
506             pad = {left: pad, right:pad, top:pad, bottom:pad};
507         }
508         pad = pad || this.defaultPadding;
509         var b = Roo.get(this.getEl()).getBox();
510         var ce = Roo.get(constrainTo);
511         var s = ce.getScroll();
512         var c, cd = ce.dom;
513         if(cd == document.body){
514             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
515         }else{
516             xy = ce.getXY();
517             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
518         }
519
520
521         var topSpace = b.y - c.y;
522         var leftSpace = b.x - c.x;
523
524         this.resetConstraints();
525         this.setXConstraint(leftSpace - (pad.left||0), // left
526                 c.width - leftSpace - b.width - (pad.right||0) //right
527         );
528         this.setYConstraint(topSpace - (pad.top||0), //top
529                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
530         );
531     },
532
533     /**
534      * Returns a reference to the linked element
535      * @method getEl
536      * @return {HTMLElement} the html element
537      */
538     getEl: function() {
539         if (!this._domRef) {
540             this._domRef = Roo.getDom(this.id);
541         }
542
543         return this._domRef;
544     },
545
546     /**
547      * Returns a reference to the actual element to drag.  By default this is
548      * the same as the html element, but it can be assigned to another
549      * element. An example of this can be found in Roo.dd.DDProxy
550      * @method getDragEl
551      * @return {HTMLElement} the html element
552      */
553     getDragEl: function() {
554         return Roo.getDom(this.dragElId);
555     },
556
557     /**
558      * Sets up the DragDrop object.  Must be called in the constructor of any
559      * Roo.dd.DragDrop subclass
560      * @method init
561      * @param id the id of the linked element
562      * @param {String} sGroup the group of related items
563      * @param {object} config configuration attributes
564      */
565     init: function(id, sGroup, config) {
566         this.initTarget(id, sGroup, config);
567         Event.on(this.id, "mousedown", this.handleMouseDown, this);
568         // Event.on(this.id, "selectstart", Event.preventDefault);
569     },
570
571     /**
572      * Initializes Targeting functionality only... the object does not
573      * get a mousedown handler.
574      * @method initTarget
575      * @param id the id of the linked element
576      * @param {String} sGroup the group of related items
577      * @param {object} config configuration attributes
578      */
579     initTarget: function(id, sGroup, config) {
580
581         // configuration attributes
582         this.config = config || {};
583
584         // create a local reference to the drag and drop manager
585         this.DDM = Roo.dd.DDM;
586         // initialize the groups array
587         this.groups = {};
588
589         // assume that we have an element reference instead of an id if the
590         // parameter is not a string
591         if (typeof id !== "string") {
592             id = Roo.id(id);
593         }
594
595         // set the id
596         this.id = id;
597
598         // add to an interaction group
599         this.addToGroup((sGroup) ? sGroup : "default");
600
601         // We don't want to register this as the handle with the manager
602         // so we just set the id rather than calling the setter.
603         this.handleElId = id;
604
605         // the linked element is the element that gets dragged by default
606         this.setDragElId(id);
607
608         // by default, clicked anchors will not start drag operations.
609         this.invalidHandleTypes = { A: "A" };
610         this.invalidHandleIds = {};
611         this.invalidHandleClasses = [];
612
613         this.applyConfig();
614
615         this.handleOnAvailable();
616     },
617
618     /**
619      * Applies the configuration parameters that were passed into the constructor.
620      * This is supposed to happen at each level through the inheritance chain.  So
621      * a DDProxy implentation will execute apply config on DDProxy, DD, and
622      * DragDrop in order to get all of the parameters that are available in
623      * each object.
624      * @method applyConfig
625      */
626     applyConfig: function() {
627
628         // configurable properties:
629         //    padding, isTarget, maintainOffset, primaryButtonOnly
630         this.padding           = this.config.padding || [0, 0, 0, 0];
631         this.isTarget          = (this.config.isTarget !== false);
632         this.maintainOffset    = (this.config.maintainOffset);
633         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
634
635     },
636
637     /**
638      * Executed when the linked element is available
639      * @method handleOnAvailable
640      * @private
641      */
642     handleOnAvailable: function() {
643         this.available = true;
644         this.resetConstraints();
645         this.onAvailable();
646     },
647
648      /**
649      * Configures the padding for the target zone in px.  Effectively expands
650      * (or reduces) the virtual object size for targeting calculations.
651      * Supports css-style shorthand; if only one parameter is passed, all sides
652      * will have that padding, and if only two are passed, the top and bottom
653      * will have the first param, the left and right the second.
654      * @method setPadding
655      * @param {int} iTop    Top pad
656      * @param {int} iRight  Right pad
657      * @param {int} iBot    Bot pad
658      * @param {int} iLeft   Left pad
659      */
660     setPadding: function(iTop, iRight, iBot, iLeft) {
661         // this.padding = [iLeft, iRight, iTop, iBot];
662         if (!iRight && 0 !== iRight) {
663             this.padding = [iTop, iTop, iTop, iTop];
664         } else if (!iBot && 0 !== iBot) {
665             this.padding = [iTop, iRight, iTop, iRight];
666         } else {
667             this.padding = [iTop, iRight, iBot, iLeft];
668         }
669     },
670
671     /**
672      * Stores the initial placement of the linked element.
673      * @method setInitialPosition
674      * @param {int} diffX   the X offset, default 0
675      * @param {int} diffY   the Y offset, default 0
676      */
677     setInitPosition: function(diffX, diffY) {
678         var el = this.getEl();
679
680         if (!this.DDM.verifyEl(el)) {
681             return;
682         }
683
684         var dx = diffX || 0;
685         var dy = diffY || 0;
686
687         var p = Dom.getXY( el );
688
689         this.initPageX = p[0] - dx;
690         this.initPageY = p[1] - dy;
691
692         this.lastPageX = p[0];
693         this.lastPageY = p[1];
694
695
696         this.setStartPosition(p);
697     },
698
699     /**
700      * Sets the start position of the element.  This is set when the obj
701      * is initialized, the reset when a drag is started.
702      * @method setStartPosition
703      * @param pos current position (from previous lookup)
704      * @private
705      */
706     setStartPosition: function(pos) {
707         var p = pos || Dom.getXY( this.getEl() );
708         this.deltaSetXY = null;
709
710         this.startPageX = p[0];
711         this.startPageY = p[1];
712     },
713
714     /**
715      * Add this instance to a group of related drag/drop objects.  All
716      * instances belong to at least one group, and can belong to as many
717      * groups as needed.
718      * @method addToGroup
719      * @param sGroup {string} the name of the group
720      */
721     addToGroup: function(sGroup) {
722         this.groups[sGroup] = true;
723         this.DDM.regDragDrop(this, sGroup);
724     },
725
726     /**
727      * Remove's this instance from the supplied interaction group
728      * @method removeFromGroup
729      * @param {string}  sGroup  The group to drop
730      */
731     removeFromGroup: function(sGroup) {
732         if (this.groups[sGroup]) {
733             delete this.groups[sGroup];
734         }
735
736         this.DDM.removeDDFromGroup(this, sGroup);
737     },
738
739     /**
740      * Allows you to specify that an element other than the linked element
741      * will be moved with the cursor during a drag
742      * @method setDragElId
743      * @param id {string} the id of the element that will be used to initiate the drag
744      */
745     setDragElId: function(id) {
746         this.dragElId = id;
747     },
748
749     /**
750      * Allows you to specify a child of the linked element that should be
751      * used to initiate the drag operation.  An example of this would be if
752      * you have a content div with text and links.  Clicking anywhere in the
753      * content area would normally start the drag operation.  Use this method
754      * to specify that an element inside of the content div is the element
755      * that starts the drag operation.
756      * @method setHandleElId
757      * @param id {string} the id of the element that will be used to
758      * initiate the drag.
759      */
760     setHandleElId: function(id) {
761         if (typeof id !== "string") {
762             id = Roo.id(id);
763         }
764         this.handleElId = id;
765         this.DDM.regHandle(this.id, id);
766     },
767
768     /**
769      * Allows you to set an element outside of the linked element as a drag
770      * handle
771      * @method setOuterHandleElId
772      * @param id the id of the element that will be used to initiate the drag
773      */
774     setOuterHandleElId: function(id) {
775         if (typeof id !== "string") {
776             id = Roo.id(id);
777         }
778         Event.on(id, "mousedown",
779                 this.handleMouseDown, this);
780         this.setHandleElId(id);
781
782         this.hasOuterHandles = true;
783     },
784
785     /**
786      * Remove all drag and drop hooks for this element
787      * @method unreg
788      */
789     unreg: function() {
790         Event.un(this.id, "mousedown",
791                 this.handleMouseDown);
792         this._domRef = null;
793         this.DDM._remove(this);
794     },
795
796     destroy : function(){
797         this.unreg();
798     },
799
800     /**
801      * Returns true if this instance is locked, or the drag drop mgr is locked
802      * (meaning that all drag/drop is disabled on the page.)
803      * @method isLocked
804      * @return {boolean} true if this obj or all drag/drop is locked, else
805      * false
806      */
807     isLocked: function() {
808         return (this.DDM.isLocked() || this.locked);
809     },
810
811     /**
812      * Fired when this object is clicked
813      * @method handleMouseDown
814      * @param {Event} e
815      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
816      * @private
817      */
818     handleMouseDown: function(e, oDD){
819         if (this.primaryButtonOnly && e.button != 0) {
820             return;
821         }
822
823         if (this.isLocked()) {
824             return;
825         }
826
827         this.DDM.refreshCache(this.groups);
828
829         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
830         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
831         } else {
832             if (this.clickValidator(e)) {
833
834                 // set the initial element position
835                 this.setStartPosition();
836
837
838                 this.b4MouseDown(e);
839                 this.onMouseDown(e);
840
841                 this.DDM.handleMouseDown(e, this);
842
843                 this.DDM.stopEvent(e);
844             } else {
845
846
847             }
848         }
849     },
850
851     clickValidator: function(e) {
852         var target = e.getTarget();
853         return ( this.isValidHandleChild(target) &&
854                     (this.id == this.handleElId ||
855                         this.DDM.handleWasClicked(target, this.id)) );
856     },
857
858     /**
859      * Allows you to specify a tag name that should not start a drag operation
860      * when clicked.  This is designed to facilitate embedding links within a
861      * drag handle that do something other than start the drag.
862      * @method addInvalidHandleType
863      * @param {string} tagName the type of element to exclude
864      */
865     addInvalidHandleType: function(tagName) {
866         var type = tagName.toUpperCase();
867         this.invalidHandleTypes[type] = type;
868     },
869
870     /**
871      * Lets you to specify an element id for a child of a drag handle
872      * that should not initiate a drag
873      * @method addInvalidHandleId
874      * @param {string} id the element id of the element you wish to ignore
875      */
876     addInvalidHandleId: function(id) {
877         if (typeof id !== "string") {
878             id = Roo.id(id);
879         }
880         this.invalidHandleIds[id] = id;
881     },
882
883     /**
884      * Lets you specify a css class of elements that will not initiate a drag
885      * @method addInvalidHandleClass
886      * @param {string} cssClass the class of the elements you wish to ignore
887      */
888     addInvalidHandleClass: function(cssClass) {
889         this.invalidHandleClasses.push(cssClass);
890     },
891
892     /**
893      * Unsets an excluded tag name set by addInvalidHandleType
894      * @method removeInvalidHandleType
895      * @param {string} tagName the type of element to unexclude
896      */
897     removeInvalidHandleType: function(tagName) {
898         var type = tagName.toUpperCase();
899         // this.invalidHandleTypes[type] = null;
900         delete this.invalidHandleTypes[type];
901     },
902
903     /**
904      * Unsets an invalid handle id
905      * @method removeInvalidHandleId
906      * @param {string} id the id of the element to re-enable
907      */
908     removeInvalidHandleId: function(id) {
909         if (typeof id !== "string") {
910             id = Roo.id(id);
911         }
912         delete this.invalidHandleIds[id];
913     },
914
915     /**
916      * Unsets an invalid css class
917      * @method removeInvalidHandleClass
918      * @param {string} cssClass the class of the element(s) you wish to
919      * re-enable
920      */
921     removeInvalidHandleClass: function(cssClass) {
922         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
923             if (this.invalidHandleClasses[i] == cssClass) {
924                 delete this.invalidHandleClasses[i];
925             }
926         }
927     },
928
929     /**
930      * Checks the tag exclusion list to see if this click should be ignored
931      * @method isValidHandleChild
932      * @param {HTMLElement} node the HTMLElement to evaluate
933      * @return {boolean} true if this is a valid tag type, false if not
934      */
935     isValidHandleChild: function(node) {
936
937         var valid = true;
938         // var n = (node.nodeName == "#text") ? node.parentNode : node;
939         var nodeName;
940         try {
941             nodeName = node.nodeName.toUpperCase();
942         } catch(e) {
943             nodeName = node.nodeName;
944         }
945         valid = valid && !this.invalidHandleTypes[nodeName];
946         valid = valid && !this.invalidHandleIds[node.id];
947
948         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
949             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
950         }
951
952
953         return valid;
954
955     },
956
957     /**
958      * Create the array of horizontal tick marks if an interval was specified
959      * in setXConstraint().
960      * @method setXTicks
961      * @private
962      */
963     setXTicks: function(iStartX, iTickSize) {
964         this.xTicks = [];
965         this.xTickSize = iTickSize;
966
967         var tickMap = {};
968
969         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
970             if (!tickMap[i]) {
971                 this.xTicks[this.xTicks.length] = i;
972                 tickMap[i] = true;
973             }
974         }
975
976         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
977             if (!tickMap[i]) {
978                 this.xTicks[this.xTicks.length] = i;
979                 tickMap[i] = true;
980             }
981         }
982
983         this.xTicks.sort(this.DDM.numericSort) ;
984     },
985
986     /**
987      * Create the array of vertical tick marks if an interval was specified in
988      * setYConstraint().
989      * @method setYTicks
990      * @private
991      */
992     setYTicks: function(iStartY, iTickSize) {
993         this.yTicks = [];
994         this.yTickSize = iTickSize;
995
996         var tickMap = {};
997
998         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
999             if (!tickMap[i]) {
1000                 this.yTicks[this.yTicks.length] = i;
1001                 tickMap[i] = true;
1002             }
1003         }
1004
1005         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1006             if (!tickMap[i]) {
1007                 this.yTicks[this.yTicks.length] = i;
1008                 tickMap[i] = true;
1009             }
1010         }
1011
1012         this.yTicks.sort(this.DDM.numericSort) ;
1013     },
1014
1015     /**
1016      * By default, the element can be dragged any place on the screen.  Use
1017      * this method to limit the horizontal travel of the element.  Pass in
1018      * 0,0 for the parameters if you want to lock the drag to the y axis.
1019      * @method setXConstraint
1020      * @param {int} iLeft the number of pixels the element can move to the left
1021      * @param {int} iRight the number of pixels the element can move to the
1022      * right
1023      * @param {int} iTickSize optional parameter for specifying that the
1024      * element
1025      * should move iTickSize pixels at a time.
1026      */
1027     setXConstraint: function(iLeft, iRight, iTickSize) {
1028         this.leftConstraint = iLeft;
1029         this.rightConstraint = iRight;
1030
1031         this.minX = this.initPageX - iLeft;
1032         this.maxX = this.initPageX + iRight;
1033         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1034
1035         this.constrainX = true;
1036     },
1037
1038     /**
1039      * Clears any constraints applied to this instance.  Also clears ticks
1040      * since they can't exist independent of a constraint at this time.
1041      * @method clearConstraints
1042      */
1043     clearConstraints: function() {
1044         this.constrainX = false;
1045         this.constrainY = false;
1046         this.clearTicks();
1047     },
1048
1049     /**
1050      * Clears any tick interval defined for this instance
1051      * @method clearTicks
1052      */
1053     clearTicks: function() {
1054         this.xTicks = null;
1055         this.yTicks = null;
1056         this.xTickSize = 0;
1057         this.yTickSize = 0;
1058     },
1059
1060     /**
1061      * By default, the element can be dragged any place on the screen.  Set
1062      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1063      * parameters if you want to lock the drag to the x axis.
1064      * @method setYConstraint
1065      * @param {int} iUp the number of pixels the element can move up
1066      * @param {int} iDown the number of pixels the element can move down
1067      * @param {int} iTickSize optional parameter for specifying that the
1068      * element should move iTickSize pixels at a time.
1069      */
1070     setYConstraint: function(iUp, iDown, iTickSize) {
1071         this.topConstraint = iUp;
1072         this.bottomConstraint = iDown;
1073
1074         this.minY = this.initPageY - iUp;
1075         this.maxY = this.initPageY + iDown;
1076         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1077
1078         this.constrainY = true;
1079
1080     },
1081
1082     /**
1083      * resetConstraints must be called if you manually reposition a dd element.
1084      * @method resetConstraints
1085      * @param {boolean} maintainOffset
1086      */
1087     resetConstraints: function() {
1088
1089
1090         // Maintain offsets if necessary
1091         if (this.initPageX || this.initPageX === 0) {
1092             // figure out how much this thing has moved
1093             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1094             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1095
1096             this.setInitPosition(dx, dy);
1097
1098         // This is the first time we have detected the element's position
1099         } else {
1100             this.setInitPosition();
1101         }
1102
1103         if (this.constrainX) {
1104             this.setXConstraint( this.leftConstraint,
1105                                  this.rightConstraint,
1106                                  this.xTickSize        );
1107         }
1108
1109         if (this.constrainY) {
1110             this.setYConstraint( this.topConstraint,
1111                                  this.bottomConstraint,
1112                                  this.yTickSize         );
1113         }
1114     },
1115
1116     /**
1117      * Normally the drag element is moved pixel by pixel, but we can specify
1118      * that it move a number of pixels at a time.  This method resolves the
1119      * location when we have it set up like this.
1120      * @method getTick
1121      * @param {int} val where we want to place the object
1122      * @param {int[]} tickArray sorted array of valid points
1123      * @return {int} the closest tick
1124      * @private
1125      */
1126     getTick: function(val, tickArray) {
1127
1128         if (!tickArray) {
1129             // If tick interval is not defined, it is effectively 1 pixel,
1130             // so we return the value passed to us.
1131             return val;
1132         } else if (tickArray[0] >= val) {
1133             // The value is lower than the first tick, so we return the first
1134             // tick.
1135             return tickArray[0];
1136         } else {
1137             for (var i=0, len=tickArray.length; i<len; ++i) {
1138                 var next = i + 1;
1139                 if (tickArray[next] && tickArray[next] >= val) {
1140                     var diff1 = val - tickArray[i];
1141                     var diff2 = tickArray[next] - val;
1142                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1143                 }
1144             }
1145
1146             // The value is larger than the last tick, so we return the last
1147             // tick.
1148             return tickArray[tickArray.length - 1];
1149         }
1150     },
1151
1152     /**
1153      * toString method
1154      * @method toString
1155      * @return {string} string representation of the dd obj
1156      */
1157     toString: function() {
1158         return ("DragDrop " + this.id);
1159     }
1160
1161 });
1162
1163 })();
1164 /*
1165  * Based on:
1166  * Ext JS Library 1.1.1
1167  * Copyright(c) 2006-2007, Ext JS, LLC.
1168  *
1169  * Originally Released Under LGPL - original licence link has changed is not relivant.
1170  *
1171  * Fork - LGPL
1172  * <script type="text/javascript">
1173  */
1174
1175
1176 /**
1177  * The drag and drop utility provides a framework for building drag and drop
1178  * applications.  In addition to enabling drag and drop for specific elements,
1179  * the drag and drop elements are tracked by the manager class, and the
1180  * interactions between the various elements are tracked during the drag and
1181  * the implementing code is notified about these important moments.
1182  */
1183
1184 // Only load the library once.  Rewriting the manager class would orphan
1185 // existing drag and drop instances.
1186 if (!Roo.dd.DragDropMgr) {
1187
1188 /**
1189  * @class Roo.dd.DragDropMgr
1190  * DragDropMgr is a singleton that tracks the element interaction for
1191  * all DragDrop items in the window.  Generally, you will not call
1192  * this class directly, but it does have helper methods that could
1193  * be useful in your DragDrop implementations.
1194  * @singleton
1195  */
1196 Roo.dd.DragDropMgr = function() {
1197
1198     var Event = Roo.EventManager;
1199
1200     return {
1201
1202         /**
1203          * Two dimensional Array of registered DragDrop objects.  The first
1204          * dimension is the DragDrop item group, the second the DragDrop
1205          * object.
1206          * @property ids
1207          * @type {string: string}
1208          * @private
1209          * @static
1210          */
1211         ids: {},
1212
1213         /**
1214          * Array of element ids defined as drag handles.  Used to determine
1215          * if the element that generated the mousedown event is actually the
1216          * handle and not the html element itself.
1217          * @property handleIds
1218          * @type {string: string}
1219          * @private
1220          * @static
1221          */
1222         handleIds: {},
1223
1224         /**
1225          * the DragDrop object that is currently being dragged
1226          * @property dragCurrent
1227          * @type DragDrop
1228          * @private
1229          * @static
1230          **/
1231         dragCurrent: null,
1232
1233         /**
1234          * the DragDrop object(s) that are being hovered over
1235          * @property dragOvers
1236          * @type Array
1237          * @private
1238          * @static
1239          */
1240         dragOvers: {},
1241
1242         /**
1243          * the X distance between the cursor and the object being dragged
1244          * @property deltaX
1245          * @type int
1246          * @private
1247          * @static
1248          */
1249         deltaX: 0,
1250
1251         /**
1252          * the Y distance between the cursor and the object being dragged
1253          * @property deltaY
1254          * @type int
1255          * @private
1256          * @static
1257          */
1258         deltaY: 0,
1259
1260         /**
1261          * Flag to determine if we should prevent the default behavior of the
1262          * events we define. By default this is true, but this can be set to
1263          * false if you need the default behavior (not recommended)
1264          * @property preventDefault
1265          * @type boolean
1266          * @static
1267          */
1268         preventDefault: true,
1269
1270         /**
1271          * Flag to determine if we should stop the propagation of the events
1272          * we generate. This is true by default but you may want to set it to
1273          * false if the html element contains other features that require the
1274          * mouse click.
1275          * @property stopPropagation
1276          * @type boolean
1277          * @static
1278          */
1279         stopPropagation: true,
1280
1281         /**
1282          * Internal flag that is set to true when drag and drop has been
1283          * intialized
1284          * @property initialized
1285          * @private
1286          * @static
1287          */
1288         initalized: false,
1289
1290         /**
1291          * All drag and drop can be disabled.
1292          * @property locked
1293          * @private
1294          * @static
1295          */
1296         locked: false,
1297
1298         /**
1299          * Called the first time an element is registered.
1300          * @method init
1301          * @private
1302          * @static
1303          */
1304         init: function() {
1305             this.initialized = true;
1306         },
1307
1308         /**
1309          * In point mode, drag and drop interaction is defined by the
1310          * location of the cursor during the drag/drop
1311          * @property POINT
1312          * @type int
1313          * @static
1314          */
1315         POINT: 0,
1316
1317         /**
1318          * In intersect mode, drag and drop interactio nis defined by the
1319          * overlap of two or more drag and drop objects.
1320          * @property INTERSECT
1321          * @type int
1322          * @static
1323          */
1324         INTERSECT: 1,
1325
1326         /**
1327          * The current drag and drop mode.  Default: POINT
1328          * @property mode
1329          * @type int
1330          * @static
1331          */
1332         mode: 0,
1333
1334         /**
1335          * Runs method on all drag and drop objects
1336          * @method _execOnAll
1337          * @private
1338          * @static
1339          */
1340         _execOnAll: function(sMethod, args) {
1341             for (var i in this.ids) {
1342                 for (var j in this.ids[i]) {
1343                     var oDD = this.ids[i][j];
1344                     if (! this.isTypeOfDD(oDD)) {
1345                         continue;
1346                     }
1347                     oDD[sMethod].apply(oDD, args);
1348                 }
1349             }
1350         },
1351
1352         /**
1353          * Drag and drop initialization.  Sets up the global event handlers
1354          * @method _onLoad
1355          * @private
1356          * @static
1357          */
1358         _onLoad: function() {
1359
1360             this.init();
1361
1362
1363             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1364             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1365             Event.on(window,   "unload",    this._onUnload, this, true);
1366             Event.on(window,   "resize",    this._onResize, this, true);
1367             // Event.on(window,   "mouseout",    this._test);
1368
1369         },
1370
1371         /**
1372          * Reset constraints on all drag and drop objs
1373          * @method _onResize
1374          * @private
1375          * @static
1376          */
1377         _onResize: function(e) {
1378             this._execOnAll("resetConstraints", []);
1379         },
1380
1381         /**
1382          * Lock all drag and drop functionality
1383          * @method lock
1384          * @static
1385          */
1386         lock: function() { this.locked = true; },
1387
1388         /**
1389          * Unlock all drag and drop functionality
1390          * @method unlock
1391          * @static
1392          */
1393         unlock: function() { this.locked = false; },
1394
1395         /**
1396          * Is drag and drop locked?
1397          * @method isLocked
1398          * @return {boolean} True if drag and drop is locked, false otherwise.
1399          * @static
1400          */
1401         isLocked: function() { return this.locked; },
1402
1403         /**
1404          * Location cache that is set for all drag drop objects when a drag is
1405          * initiated, cleared when the drag is finished.
1406          * @property locationCache
1407          * @private
1408          * @static
1409          */
1410         locationCache: {},
1411
1412         /**
1413          * Set useCache to false if you want to force object the lookup of each
1414          * drag and drop linked element constantly during a drag.
1415          * @property useCache
1416          * @type boolean
1417          * @static
1418          */
1419         useCache: true,
1420
1421         /**
1422          * The number of pixels that the mouse needs to move after the
1423          * mousedown before the drag is initiated.  Default=3;
1424          * @property clickPixelThresh
1425          * @type int
1426          * @static
1427          */
1428         clickPixelThresh: 3,
1429
1430         /**
1431          * The number of milliseconds after the mousedown event to initiate the
1432          * drag if we don't get a mouseup event. Default=1000
1433          * @property clickTimeThresh
1434          * @type int
1435          * @static
1436          */
1437         clickTimeThresh: 350,
1438
1439         /**
1440          * Flag that indicates that either the drag pixel threshold or the
1441          * mousdown time threshold has been met
1442          * @property dragThreshMet
1443          * @type boolean
1444          * @private
1445          * @static
1446          */
1447         dragThreshMet: false,
1448
1449         /**
1450          * Timeout used for the click time threshold
1451          * @property clickTimeout
1452          * @type Object
1453          * @private
1454          * @static
1455          */
1456         clickTimeout: null,
1457
1458         /**
1459          * The X position of the mousedown event stored for later use when a
1460          * drag threshold is met.
1461          * @property startX
1462          * @type int
1463          * @private
1464          * @static
1465          */
1466         startX: 0,
1467
1468         /**
1469          * The Y position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startY
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startY: 0,
1477
1478         /**
1479          * Each DragDrop instance must be registered with the DragDropMgr.
1480          * This is executed in DragDrop.init()
1481          * @method regDragDrop
1482          * @param {DragDrop} oDD the DragDrop object to register
1483          * @param {String} sGroup the name of the group this element belongs to
1484          * @static
1485          */
1486         regDragDrop: function(oDD, sGroup) {
1487             if (!this.initialized) { this.init(); }
1488
1489             if (!this.ids[sGroup]) {
1490                 this.ids[sGroup] = {};
1491             }
1492             this.ids[sGroup][oDD.id] = oDD;
1493         },
1494
1495         /**
1496          * Removes the supplied dd instance from the supplied group. Executed
1497          * by DragDrop.removeFromGroup, so don't call this function directly.
1498          * @method removeDDFromGroup
1499          * @private
1500          * @static
1501          */
1502         removeDDFromGroup: function(oDD, sGroup) {
1503             if (!this.ids[sGroup]) {
1504                 this.ids[sGroup] = {};
1505             }
1506
1507             var obj = this.ids[sGroup];
1508             if (obj && obj[oDD.id]) {
1509                 delete obj[oDD.id];
1510             }
1511         },
1512
1513         /**
1514          * Unregisters a drag and drop item.  This is executed in
1515          * DragDrop.unreg, use that method instead of calling this directly.
1516          * @method _remove
1517          * @private
1518          * @static
1519          */
1520         _remove: function(oDD) {
1521             for (var g in oDD.groups) {
1522                 if (g && this.ids[g][oDD.id]) {
1523                     delete this.ids[g][oDD.id];
1524                 }
1525             }
1526             delete this.handleIds[oDD.id];
1527         },
1528
1529         /**
1530          * Each DragDrop handle element must be registered.  This is done
1531          * automatically when executing DragDrop.setHandleElId()
1532          * @method regHandle
1533          * @param {String} sDDId the DragDrop id this element is a handle for
1534          * @param {String} sHandleId the id of the element that is the drag
1535          * handle
1536          * @static
1537          */
1538         regHandle: function(sDDId, sHandleId) {
1539             if (!this.handleIds[sDDId]) {
1540                 this.handleIds[sDDId] = {};
1541             }
1542             this.handleIds[sDDId][sHandleId] = sHandleId;
1543         },
1544
1545         /**
1546          * Utility function to determine if a given element has been
1547          * registered as a drag drop item.
1548          * @method isDragDrop
1549          * @param {String} id the element id to check
1550          * @return {boolean} true if this element is a DragDrop item,
1551          * false otherwise
1552          * @static
1553          */
1554         isDragDrop: function(id) {
1555             return ( this.getDDById(id) ) ? true : false;
1556         },
1557
1558         /**
1559          * Returns the drag and drop instances that are in all groups the
1560          * passed in instance belongs to.
1561          * @method getRelated
1562          * @param {DragDrop} p_oDD the obj to get related data for
1563          * @param {boolean} bTargetsOnly if true, only return targetable objs
1564          * @return {DragDrop[]} the related instances
1565          * @static
1566          */
1567         getRelated: function(p_oDD, bTargetsOnly) {
1568             var oDDs = [];
1569             for (var i in p_oDD.groups) {
1570                 for (j in this.ids[i]) {
1571                     var dd = this.ids[i][j];
1572                     if (! this.isTypeOfDD(dd)) {
1573                         continue;
1574                     }
1575                     if (!bTargetsOnly || dd.isTarget) {
1576                         oDDs[oDDs.length] = dd;
1577                     }
1578                 }
1579             }
1580
1581             return oDDs;
1582         },
1583
1584         /**
1585          * Returns true if the specified dd target is a legal target for
1586          * the specifice drag obj
1587          * @method isLegalTarget
1588          * @param {DragDrop} the drag obj
1589          * @param {DragDrop} the target
1590          * @return {boolean} true if the target is a legal target for the
1591          * dd obj
1592          * @static
1593          */
1594         isLegalTarget: function (oDD, oTargetDD) {
1595             var targets = this.getRelated(oDD, true);
1596             for (var i=0, len=targets.length;i<len;++i) {
1597                 if (targets[i].id == oTargetDD.id) {
1598                     return true;
1599                 }
1600             }
1601
1602             return false;
1603         },
1604
1605         /**
1606          * My goal is to be able to transparently determine if an object is
1607          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1608          * returns "object", oDD.constructor.toString() always returns
1609          * "DragDrop" and not the name of the subclass.  So for now it just
1610          * evaluates a well-known variable in DragDrop.
1611          * @method isTypeOfDD
1612          * @param {Object} the object to evaluate
1613          * @return {boolean} true if typeof oDD = DragDrop
1614          * @static
1615          */
1616         isTypeOfDD: function (oDD) {
1617             return (oDD && oDD.__ygDragDrop);
1618         },
1619
1620         /**
1621          * Utility function to determine if a given element has been
1622          * registered as a drag drop handle for the given Drag Drop object.
1623          * @method isHandle
1624          * @param {String} id the element id to check
1625          * @return {boolean} true if this element is a DragDrop handle, false
1626          * otherwise
1627          * @static
1628          */
1629         isHandle: function(sDDId, sHandleId) {
1630             return ( this.handleIds[sDDId] &&
1631                             this.handleIds[sDDId][sHandleId] );
1632         },
1633
1634         /**
1635          * Returns the DragDrop instance for a given id
1636          * @method getDDById
1637          * @param {String} id the id of the DragDrop object
1638          * @return {DragDrop} the drag drop object, null if it is not found
1639          * @static
1640          */
1641         getDDById: function(id) {
1642             for (var i in this.ids) {
1643                 if (this.ids[i][id]) {
1644                     return this.ids[i][id];
1645                 }
1646             }
1647             return null;
1648         },
1649
1650         /**
1651          * Fired after a registered DragDrop object gets the mousedown event.
1652          * Sets up the events required to track the object being dragged
1653          * @method handleMouseDown
1654          * @param {Event} e the event
1655          * @param oDD the DragDrop object being dragged
1656          * @private
1657          * @static
1658          */
1659         handleMouseDown: function(e, oDD) {
1660             if(Roo.QuickTips){
1661                 Roo.QuickTips.disable();
1662             }
1663             this.currentTarget = e.getTarget();
1664
1665             this.dragCurrent = oDD;
1666
1667             var el = oDD.getEl();
1668
1669             // track start position
1670             this.startX = e.getPageX();
1671             this.startY = e.getPageY();
1672
1673             this.deltaX = this.startX - el.offsetLeft;
1674             this.deltaY = this.startY - el.offsetTop;
1675
1676             this.dragThreshMet = false;
1677
1678             this.clickTimeout = setTimeout(
1679                     function() {
1680                         var DDM = Roo.dd.DDM;
1681                         DDM.startDrag(DDM.startX, DDM.startY);
1682                     },
1683                     this.clickTimeThresh );
1684         },
1685
1686         /**
1687          * Fired when either the drag pixel threshol or the mousedown hold
1688          * time threshold has been met.
1689          * @method startDrag
1690          * @param x {int} the X position of the original mousedown
1691          * @param y {int} the Y position of the original mousedown
1692          * @static
1693          */
1694         startDrag: function(x, y) {
1695             clearTimeout(this.clickTimeout);
1696             if (this.dragCurrent) {
1697                 this.dragCurrent.b4StartDrag(x, y);
1698                 this.dragCurrent.startDrag(x, y);
1699             }
1700             this.dragThreshMet = true;
1701         },
1702
1703         /**
1704          * Internal function to handle the mouseup event.  Will be invoked
1705          * from the context of the document.
1706          * @method handleMouseUp
1707          * @param {Event} e the event
1708          * @private
1709          * @static
1710          */
1711         handleMouseUp: function(e) {
1712
1713             if(Roo.QuickTips){
1714                 Roo.QuickTips.enable();
1715             }
1716             if (! this.dragCurrent) {
1717                 return;
1718             }
1719
1720             clearTimeout(this.clickTimeout);
1721
1722             if (this.dragThreshMet) {
1723                 this.fireEvents(e, true);
1724             } else {
1725             }
1726
1727             this.stopDrag(e);
1728
1729             this.stopEvent(e);
1730         },
1731
1732         /**
1733          * Utility to stop event propagation and event default, if these
1734          * features are turned on.
1735          * @method stopEvent
1736          * @param {Event} e the event as returned by this.getEvent()
1737          * @static
1738          */
1739         stopEvent: function(e){
1740             if(this.stopPropagation) {
1741                 e.stopPropagation();
1742             }
1743
1744             if (this.preventDefault) {
1745                 e.preventDefault();
1746             }
1747         },
1748
1749         /**
1750          * Internal function to clean up event handlers after the drag
1751          * operation is complete
1752          * @method stopDrag
1753          * @param {Event} e the event
1754          * @private
1755          * @static
1756          */
1757         stopDrag: function(e) {
1758             // Fire the drag end event for the item that was dragged
1759             if (this.dragCurrent) {
1760                 if (this.dragThreshMet) {
1761                     this.dragCurrent.b4EndDrag(e);
1762                     this.dragCurrent.endDrag(e);
1763                 }
1764
1765                 this.dragCurrent.onMouseUp(e);
1766             }
1767
1768             this.dragCurrent = null;
1769             this.dragOvers = {};
1770         },
1771
1772         /**
1773          * Internal function to handle the mousemove event.  Will be invoked
1774          * from the context of the html element.
1775          *
1776          * @TODO figure out what we can do about mouse events lost when the
1777          * user drags objects beyond the window boundary.  Currently we can
1778          * detect this in internet explorer by verifying that the mouse is
1779          * down during the mousemove event.  Firefox doesn't give us the
1780          * button state on the mousemove event.
1781          * @method handleMouseMove
1782          * @param {Event} e the event
1783          * @private
1784          * @static
1785          */
1786         handleMouseMove: function(e) {
1787             if (! this.dragCurrent) {
1788                 return true;
1789             }
1790
1791             // var button = e.which || e.button;
1792
1793             // check for IE mouseup outside of page boundary
1794             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1795                 this.stopEvent(e);
1796                 return this.handleMouseUp(e);
1797             }
1798
1799             if (!this.dragThreshMet) {
1800                 var diffX = Math.abs(this.startX - e.getPageX());
1801                 var diffY = Math.abs(this.startY - e.getPageY());
1802                 if (diffX > this.clickPixelThresh ||
1803                             diffY > this.clickPixelThresh) {
1804                     this.startDrag(this.startX, this.startY);
1805                 }
1806             }
1807
1808             if (this.dragThreshMet) {
1809                 this.dragCurrent.b4Drag(e);
1810                 this.dragCurrent.onDrag(e);
1811                 if(!this.dragCurrent.moveOnly){
1812                     this.fireEvents(e, false);
1813                 }
1814             }
1815
1816             this.stopEvent(e);
1817
1818             return true;
1819         },
1820
1821         /**
1822          * Iterates over all of the DragDrop elements to find ones we are
1823          * hovering over or dropping on
1824          * @method fireEvents
1825          * @param {Event} e the event
1826          * @param {boolean} isDrop is this a drop op or a mouseover op?
1827          * @private
1828          * @static
1829          */
1830         fireEvents: function(e, isDrop) {
1831             var dc = this.dragCurrent;
1832
1833             // If the user did the mouse up outside of the window, we could
1834             // get here even though we have ended the drag.
1835             if (!dc || dc.isLocked()) {
1836                 return;
1837             }
1838
1839             var pt = e.getPoint();
1840
1841             // cache the previous dragOver array
1842             var oldOvers = [];
1843
1844             var outEvts   = [];
1845             var overEvts  = [];
1846             var dropEvts  = [];
1847             var enterEvts = [];
1848
1849             // Check to see if the object(s) we were hovering over is no longer
1850             // being hovered over so we can fire the onDragOut event
1851             for (var i in this.dragOvers) {
1852
1853                 var ddo = this.dragOvers[i];
1854
1855                 if (! this.isTypeOfDD(ddo)) {
1856                     continue;
1857                 }
1858
1859                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1860                     outEvts.push( ddo );
1861                 }
1862
1863                 oldOvers[i] = true;
1864                 delete this.dragOvers[i];
1865             }
1866
1867             for (var sGroup in dc.groups) {
1868
1869                 if ("string" != typeof sGroup) {
1870                     continue;
1871                 }
1872
1873                 for (i in this.ids[sGroup]) {
1874                     var oDD = this.ids[sGroup][i];
1875                     if (! this.isTypeOfDD(oDD)) {
1876                         continue;
1877                     }
1878
1879                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1880                         if (this.isOverTarget(pt, oDD, this.mode)) {
1881                             // look for drop interactions
1882                             if (isDrop) {
1883                                 dropEvts.push( oDD );
1884                             // look for drag enter and drag over interactions
1885                             } else {
1886
1887                                 // initial drag over: dragEnter fires
1888                                 if (!oldOvers[oDD.id]) {
1889                                     enterEvts.push( oDD );
1890                                 // subsequent drag overs: dragOver fires
1891                                 } else {
1892                                     overEvts.push( oDD );
1893                                 }
1894
1895                                 this.dragOvers[oDD.id] = oDD;
1896                             }
1897                         }
1898                     }
1899                 }
1900             }
1901
1902             if (this.mode) {
1903                 if (outEvts.length) {
1904                     dc.b4DragOut(e, outEvts);
1905                     dc.onDragOut(e, outEvts);
1906                 }
1907
1908                 if (enterEvts.length) {
1909                     dc.onDragEnter(e, enterEvts);
1910                 }
1911
1912                 if (overEvts.length) {
1913                     dc.b4DragOver(e, overEvts);
1914                     dc.onDragOver(e, overEvts);
1915                 }
1916
1917                 if (dropEvts.length) {
1918                     dc.b4DragDrop(e, dropEvts);
1919                     dc.onDragDrop(e, dropEvts);
1920                 }
1921
1922             } else {
1923                 // fire dragout events
1924                 var len = 0;
1925                 for (i=0, len=outEvts.length; i<len; ++i) {
1926                     dc.b4DragOut(e, outEvts[i].id);
1927                     dc.onDragOut(e, outEvts[i].id);
1928                 }
1929
1930                 // fire enter events
1931                 for (i=0,len=enterEvts.length; i<len; ++i) {
1932                     // dc.b4DragEnter(e, oDD.id);
1933                     dc.onDragEnter(e, enterEvts[i].id);
1934                 }
1935
1936                 // fire over events
1937                 for (i=0,len=overEvts.length; i<len; ++i) {
1938                     dc.b4DragOver(e, overEvts[i].id);
1939                     dc.onDragOver(e, overEvts[i].id);
1940                 }
1941
1942                 // fire drop events
1943                 for (i=0, len=dropEvts.length; i<len; ++i) {
1944                     dc.b4DragDrop(e, dropEvts[i].id);
1945                     dc.onDragDrop(e, dropEvts[i].id);
1946                 }
1947
1948             }
1949
1950             // notify about a drop that did not find a target
1951             if (isDrop && !dropEvts.length) {
1952                 dc.onInvalidDrop(e);
1953             }
1954
1955         },
1956
1957         /**
1958          * Helper function for getting the best match from the list of drag
1959          * and drop objects returned by the drag and drop events when we are
1960          * in INTERSECT mode.  It returns either the first object that the
1961          * cursor is over, or the object that has the greatest overlap with
1962          * the dragged element.
1963          * @method getBestMatch
1964          * @param  {DragDrop[]} dds The array of drag and drop objects
1965          * targeted
1966          * @return {DragDrop}       The best single match
1967          * @static
1968          */
1969         getBestMatch: function(dds) {
1970             var winner = null;
1971             // Return null if the input is not what we expect
1972             //if (!dds || !dds.length || dds.length == 0) {
1973                // winner = null;
1974             // If there is only one item, it wins
1975             //} else if (dds.length == 1) {
1976
1977             var len = dds.length;
1978
1979             if (len == 1) {
1980                 winner = dds[0];
1981             } else {
1982                 // Loop through the targeted items
1983                 for (var i=0; i<len; ++i) {
1984                     var dd = dds[i];
1985                     // If the cursor is over the object, it wins.  If the
1986                     // cursor is over multiple matches, the first one we come
1987                     // to wins.
1988                     if (dd.cursorIsOver) {
1989                         winner = dd;
1990                         break;
1991                     // Otherwise the object with the most overlap wins
1992                     } else {
1993                         if (!winner ||
1994                             winner.overlap.getArea() < dd.overlap.getArea()) {
1995                             winner = dd;
1996                         }
1997                     }
1998                 }
1999             }
2000
2001             return winner;
2002         },
2003
2004         /**
2005          * Refreshes the cache of the top-left and bottom-right points of the
2006          * drag and drop objects in the specified group(s).  This is in the
2007          * format that is stored in the drag and drop instance, so typical
2008          * usage is:
2009          * <code>
2010          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2011          * </code>
2012          * Alternatively:
2013          * <code>
2014          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2015          * </code>
2016          * @TODO this really should be an indexed array.  Alternatively this
2017          * method could accept both.
2018          * @method refreshCache
2019          * @param {Object} groups an associative array of groups to refresh
2020          * @static
2021          */
2022         refreshCache: function(groups) {
2023             for (var sGroup in groups) {
2024                 if ("string" != typeof sGroup) {
2025                     continue;
2026                 }
2027                 for (var i in this.ids[sGroup]) {
2028                     var oDD = this.ids[sGroup][i];
2029
2030                     if (this.isTypeOfDD(oDD)) {
2031                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2032                         var loc = this.getLocation(oDD);
2033                         if (loc) {
2034                             this.locationCache[oDD.id] = loc;
2035                         } else {
2036                             delete this.locationCache[oDD.id];
2037                             // this will unregister the drag and drop object if
2038                             // the element is not in a usable state
2039                             // oDD.unreg();
2040                         }
2041                     }
2042                 }
2043             }
2044         },
2045
2046         /**
2047          * This checks to make sure an element exists and is in the DOM.  The
2048          * main purpose is to handle cases where innerHTML is used to remove
2049          * drag and drop objects from the DOM.  IE provides an 'unspecified
2050          * error' when trying to access the offsetParent of such an element
2051          * @method verifyEl
2052          * @param {HTMLElement} el the element to check
2053          * @return {boolean} true if the element looks usable
2054          * @static
2055          */
2056         verifyEl: function(el) {
2057             if (el) {
2058                 var parent;
2059                 if(Roo.isIE){
2060                     try{
2061                         parent = el.offsetParent;
2062                     }catch(e){}
2063                 }else{
2064                     parent = el.offsetParent;
2065                 }
2066                 if (parent) {
2067                     return true;
2068                 }
2069             }
2070
2071             return false;
2072         },
2073
2074         /**
2075          * Returns a Region object containing the drag and drop element's position
2076          * and size, including the padding configured for it
2077          * @method getLocation
2078          * @param {DragDrop} oDD the drag and drop object to get the
2079          *                       location for
2080          * @return {Roo.lib.Region} a Region object representing the total area
2081          *                             the element occupies, including any padding
2082          *                             the instance is configured for.
2083          * @static
2084          */
2085         getLocation: function(oDD) {
2086             if (! this.isTypeOfDD(oDD)) {
2087                 return null;
2088             }
2089
2090             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2091
2092             try {
2093                 pos= Roo.lib.Dom.getXY(el);
2094             } catch (e) { }
2095
2096             if (!pos) {
2097                 return null;
2098             }
2099
2100             x1 = pos[0];
2101             x2 = x1 + el.offsetWidth;
2102             y1 = pos[1];
2103             y2 = y1 + el.offsetHeight;
2104
2105             t = y1 - oDD.padding[0];
2106             r = x2 + oDD.padding[1];
2107             b = y2 + oDD.padding[2];
2108             l = x1 - oDD.padding[3];
2109
2110             return new Roo.lib.Region( t, r, b, l );
2111         },
2112
2113         /**
2114          * Checks the cursor location to see if it over the target
2115          * @method isOverTarget
2116          * @param {Roo.lib.Point} pt The point to evaluate
2117          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2118          * @return {boolean} true if the mouse is over the target
2119          * @private
2120          * @static
2121          */
2122         isOverTarget: function(pt, oTarget, intersect) {
2123             // use cache if available
2124             var loc = this.locationCache[oTarget.id];
2125             if (!loc || !this.useCache) {
2126                 loc = this.getLocation(oTarget);
2127                 this.locationCache[oTarget.id] = loc;
2128
2129             }
2130
2131             if (!loc) {
2132                 return false;
2133             }
2134
2135             oTarget.cursorIsOver = loc.contains( pt );
2136
2137             // DragDrop is using this as a sanity check for the initial mousedown
2138             // in this case we are done.  In POINT mode, if the drag obj has no
2139             // contraints, we are also done. Otherwise we need to evaluate the
2140             // location of the target as related to the actual location of the
2141             // dragged element.
2142             var dc = this.dragCurrent;
2143             if (!dc || !dc.getTargetCoord ||
2144                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2145                 return oTarget.cursorIsOver;
2146             }
2147
2148             oTarget.overlap = null;
2149
2150             // Get the current location of the drag element, this is the
2151             // location of the mouse event less the delta that represents
2152             // where the original mousedown happened on the element.  We
2153             // need to consider constraints and ticks as well.
2154             var pos = dc.getTargetCoord(pt.x, pt.y);
2155
2156             var el = dc.getDragEl();
2157             var curRegion = new Roo.lib.Region( pos.y,
2158                                                    pos.x + el.offsetWidth,
2159                                                    pos.y + el.offsetHeight,
2160                                                    pos.x );
2161
2162             var overlap = curRegion.intersect(loc);
2163
2164             if (overlap) {
2165                 oTarget.overlap = overlap;
2166                 return (intersect) ? true : oTarget.cursorIsOver;
2167             } else {
2168                 return false;
2169             }
2170         },
2171
2172         /**
2173          * unload event handler
2174          * @method _onUnload
2175          * @private
2176          * @static
2177          */
2178         _onUnload: function(e, me) {
2179             Roo.dd.DragDropMgr.unregAll();
2180         },
2181
2182         /**
2183          * Cleans up the drag and drop events and objects.
2184          * @method unregAll
2185          * @private
2186          * @static
2187          */
2188         unregAll: function() {
2189
2190             if (this.dragCurrent) {
2191                 this.stopDrag();
2192                 this.dragCurrent = null;
2193             }
2194
2195             this._execOnAll("unreg", []);
2196
2197             for (i in this.elementCache) {
2198                 delete this.elementCache[i];
2199             }
2200
2201             this.elementCache = {};
2202             this.ids = {};
2203         },
2204
2205         /**
2206          * A cache of DOM elements
2207          * @property elementCache
2208          * @private
2209          * @static
2210          */
2211         elementCache: {},
2212
2213         /**
2214          * Get the wrapper for the DOM element specified
2215          * @method getElWrapper
2216          * @param {String} id the id of the element to get
2217          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2218          * @private
2219          * @deprecated This wrapper isn't that useful
2220          * @static
2221          */
2222         getElWrapper: function(id) {
2223             var oWrapper = this.elementCache[id];
2224             if (!oWrapper || !oWrapper.el) {
2225                 oWrapper = this.elementCache[id] =
2226                     new this.ElementWrapper(Roo.getDom(id));
2227             }
2228             return oWrapper;
2229         },
2230
2231         /**
2232          * Returns the actual DOM element
2233          * @method getElement
2234          * @param {String} id the id of the elment to get
2235          * @return {Object} The element
2236          * @deprecated use Roo.getDom instead
2237          * @static
2238          */
2239         getElement: function(id) {
2240             return Roo.getDom(id);
2241         },
2242
2243         /**
2244          * Returns the style property for the DOM element (i.e.,
2245          * document.getElById(id).style)
2246          * @method getCss
2247          * @param {String} id the id of the elment to get
2248          * @return {Object} The style property of the element
2249          * @deprecated use Roo.getDom instead
2250          * @static
2251          */
2252         getCss: function(id) {
2253             var el = Roo.getDom(id);
2254             return (el) ? el.style : null;
2255         },
2256
2257         /**
2258          * Inner class for cached elements
2259          * @class DragDropMgr.ElementWrapper
2260          * @for DragDropMgr
2261          * @private
2262          * @deprecated
2263          */
2264         ElementWrapper: function(el) {
2265                 /**
2266                  * The element
2267                  * @property el
2268                  */
2269                 this.el = el || null;
2270                 /**
2271                  * The element id
2272                  * @property id
2273                  */
2274                 this.id = this.el && el.id;
2275                 /**
2276                  * A reference to the style property
2277                  * @property css
2278                  */
2279                 this.css = this.el && el.style;
2280             },
2281
2282         /**
2283          * Returns the X position of an html element
2284          * @method getPosX
2285          * @param el the element for which to get the position
2286          * @return {int} the X coordinate
2287          * @for DragDropMgr
2288          * @deprecated use Roo.lib.Dom.getX instead
2289          * @static
2290          */
2291         getPosX: function(el) {
2292             return Roo.lib.Dom.getX(el);
2293         },
2294
2295         /**
2296          * Returns the Y position of an html element
2297          * @method getPosY
2298          * @param el the element for which to get the position
2299          * @return {int} the Y coordinate
2300          * @deprecated use Roo.lib.Dom.getY instead
2301          * @static
2302          */
2303         getPosY: function(el) {
2304             return Roo.lib.Dom.getY(el);
2305         },
2306
2307         /**
2308          * Swap two nodes.  In IE, we use the native method, for others we
2309          * emulate the IE behavior
2310          * @method swapNode
2311          * @param n1 the first node to swap
2312          * @param n2 the other node to swap
2313          * @static
2314          */
2315         swapNode: function(n1, n2) {
2316             if (n1.swapNode) {
2317                 n1.swapNode(n2);
2318             } else {
2319                 var p = n2.parentNode;
2320                 var s = n2.nextSibling;
2321
2322                 if (s == n1) {
2323                     p.insertBefore(n1, n2);
2324                 } else if (n2 == n1.nextSibling) {
2325                     p.insertBefore(n2, n1);
2326                 } else {
2327                     n1.parentNode.replaceChild(n2, n1);
2328                     p.insertBefore(n1, s);
2329                 }
2330             }
2331         },
2332
2333         /**
2334          * Returns the current scroll position
2335          * @method getScroll
2336          * @private
2337          * @static
2338          */
2339         getScroll: function () {
2340             var t, l, dde=document.documentElement, db=document.body;
2341             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2342                 t = dde.scrollTop;
2343                 l = dde.scrollLeft;
2344             } else if (db) {
2345                 t = db.scrollTop;
2346                 l = db.scrollLeft;
2347             } else {
2348
2349             }
2350             return { top: t, left: l };
2351         },
2352
2353         /**
2354          * Returns the specified element style property
2355          * @method getStyle
2356          * @param {HTMLElement} el          the element
2357          * @param {string}      styleProp   the style property
2358          * @return {string} The value of the style property
2359          * @deprecated use Roo.lib.Dom.getStyle
2360          * @static
2361          */
2362         getStyle: function(el, styleProp) {
2363             return Roo.fly(el).getStyle(styleProp);
2364         },
2365
2366         /**
2367          * Gets the scrollTop
2368          * @method getScrollTop
2369          * @return {int} the document's scrollTop
2370          * @static
2371          */
2372         getScrollTop: function () { return this.getScroll().top; },
2373
2374         /**
2375          * Gets the scrollLeft
2376          * @method getScrollLeft
2377          * @return {int} the document's scrollTop
2378          * @static
2379          */
2380         getScrollLeft: function () { return this.getScroll().left; },
2381
2382         /**
2383          * Sets the x/y position of an element to the location of the
2384          * target element.
2385          * @method moveToEl
2386          * @param {HTMLElement} moveEl      The element to move
2387          * @param {HTMLElement} targetEl    The position reference element
2388          * @static
2389          */
2390         moveToEl: function (moveEl, targetEl) {
2391             var aCoord = Roo.lib.Dom.getXY(targetEl);
2392             Roo.lib.Dom.setXY(moveEl, aCoord);
2393         },
2394
2395         /**
2396          * Numeric array sort function
2397          * @method numericSort
2398          * @static
2399          */
2400         numericSort: function(a, b) { return (a - b); },
2401
2402         /**
2403          * Internal counter
2404          * @property _timeoutCount
2405          * @private
2406          * @static
2407          */
2408         _timeoutCount: 0,
2409
2410         /**
2411          * Trying to make the load order less important.  Without this we get
2412          * an error if this file is loaded before the Event Utility.
2413          * @method _addListeners
2414          * @private
2415          * @static
2416          */
2417         _addListeners: function() {
2418             var DDM = Roo.dd.DDM;
2419             if ( Roo.lib.Event && document ) {
2420                 DDM._onLoad();
2421             } else {
2422                 if (DDM._timeoutCount > 2000) {
2423                 } else {
2424                     setTimeout(DDM._addListeners, 10);
2425                     if (document && document.body) {
2426                         DDM._timeoutCount += 1;
2427                     }
2428                 }
2429             }
2430         },
2431
2432         /**
2433          * Recursively searches the immediate parent and all child nodes for
2434          * the handle element in order to determine wheter or not it was
2435          * clicked.
2436          * @method handleWasClicked
2437          * @param node the html element to inspect
2438          * @static
2439          */
2440         handleWasClicked: function(node, id) {
2441             if (this.isHandle(id, node.id)) {
2442                 return true;
2443             } else {
2444                 // check to see if this is a text node child of the one we want
2445                 var p = node.parentNode;
2446
2447                 while (p) {
2448                     if (this.isHandle(id, p.id)) {
2449                         return true;
2450                     } else {
2451                         p = p.parentNode;
2452                     }
2453                 }
2454             }
2455
2456             return false;
2457         }
2458
2459     };
2460
2461 }();
2462
2463 // shorter alias, save a few bytes
2464 Roo.dd.DDM = Roo.dd.DragDropMgr;
2465 Roo.dd.DDM._addListeners();
2466
2467 }/*
2468  * Based on:
2469  * Ext JS Library 1.1.1
2470  * Copyright(c) 2006-2007, Ext JS, LLC.
2471  *
2472  * Originally Released Under LGPL - original licence link has changed is not relivant.
2473  *
2474  * Fork - LGPL
2475  * <script type="text/javascript">
2476  */
2477
2478 /**
2479  * @class Roo.dd.DD
2480  * A DragDrop implementation where the linked element follows the
2481  * mouse cursor during a drag.
2482  * @extends Roo.dd.DragDrop
2483  * @constructor
2484  * @param {String} id the id of the linked element
2485  * @param {String} sGroup the group of related DragDrop items
2486  * @param {object} config an object containing configurable attributes
2487  *                Valid properties for DD:
2488  *                    scroll
2489  */
2490 Roo.dd.DD = function(id, sGroup, config) {
2491     if (id) {
2492         this.init(id, sGroup, config);
2493     }
2494 };
2495
2496 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2497
2498     /**
2499      * When set to true, the utility automatically tries to scroll the browser
2500      * window wehn a drag and drop element is dragged near the viewport boundary.
2501      * Defaults to true.
2502      * @property scroll
2503      * @type boolean
2504      */
2505     scroll: true,
2506
2507     /**
2508      * Sets the pointer offset to the distance between the linked element's top
2509      * left corner and the location the element was clicked
2510      * @method autoOffset
2511      * @param {int} iPageX the X coordinate of the click
2512      * @param {int} iPageY the Y coordinate of the click
2513      */
2514     autoOffset: function(iPageX, iPageY) {
2515         var x = iPageX - this.startPageX;
2516         var y = iPageY - this.startPageY;
2517         this.setDelta(x, y);
2518     },
2519
2520     /**
2521      * Sets the pointer offset.  You can call this directly to force the
2522      * offset to be in a particular location (e.g., pass in 0,0 to set it
2523      * to the center of the object)
2524      * @method setDelta
2525      * @param {int} iDeltaX the distance from the left
2526      * @param {int} iDeltaY the distance from the top
2527      */
2528     setDelta: function(iDeltaX, iDeltaY) {
2529         this.deltaX = iDeltaX;
2530         this.deltaY = iDeltaY;
2531     },
2532
2533     /**
2534      * Sets the drag element to the location of the mousedown or click event,
2535      * maintaining the cursor location relative to the location on the element
2536      * that was clicked.  Override this if you want to place the element in a
2537      * location other than where the cursor is.
2538      * @method setDragElPos
2539      * @param {int} iPageX the X coordinate of the mousedown or drag event
2540      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2541      */
2542     setDragElPos: function(iPageX, iPageY) {
2543         // the first time we do this, we are going to check to make sure
2544         // the element has css positioning
2545
2546         var el = this.getDragEl();
2547         this.alignElWithMouse(el, iPageX, iPageY);
2548     },
2549
2550     /**
2551      * Sets the element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method alignElWithMouse
2556      * @param {HTMLElement} el the element to move
2557      * @param {int} iPageX the X coordinate of the mousedown or drag event
2558      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2559      */
2560     alignElWithMouse: function(el, iPageX, iPageY) {
2561         var oCoord = this.getTargetCoord(iPageX, iPageY);
2562         var fly = el.dom ? el : Roo.fly(el);
2563         if (!this.deltaSetXY) {
2564             var aCoord = [oCoord.x, oCoord.y];
2565             fly.setXY(aCoord);
2566             var newLeft = fly.getLeft(true);
2567             var newTop  = fly.getTop(true);
2568             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2569         } else {
2570             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2571         }
2572
2573         this.cachePosition(oCoord.x, oCoord.y);
2574         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2575         return oCoord;
2576     },
2577
2578     /**
2579      * Saves the most recent position so that we can reset the constraints and
2580      * tick marks on-demand.  We need to know this so that we can calculate the
2581      * number of pixels the element is offset from its original position.
2582      * @method cachePosition
2583      * @param iPageX the current x position (optional, this just makes it so we
2584      * don't have to look it up again)
2585      * @param iPageY the current y position (optional, this just makes it so we
2586      * don't have to look it up again)
2587      */
2588     cachePosition: function(iPageX, iPageY) {
2589         if (iPageX) {
2590             this.lastPageX = iPageX;
2591             this.lastPageY = iPageY;
2592         } else {
2593             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2594             this.lastPageX = aCoord[0];
2595             this.lastPageY = aCoord[1];
2596         }
2597     },
2598
2599     /**
2600      * Auto-scroll the window if the dragged object has been moved beyond the
2601      * visible window boundary.
2602      * @method autoScroll
2603      * @param {int} x the drag element's x position
2604      * @param {int} y the drag element's y position
2605      * @param {int} h the height of the drag element
2606      * @param {int} w the width of the drag element
2607      * @private
2608      */
2609     autoScroll: function(x, y, h, w) {
2610
2611         if (this.scroll) {
2612             // The client height
2613             var clientH = Roo.lib.Dom.getViewWidth();
2614
2615             // The client width
2616             var clientW = Roo.lib.Dom.getViewHeight();
2617
2618             // The amt scrolled down
2619             var st = this.DDM.getScrollTop();
2620
2621             // The amt scrolled right
2622             var sl = this.DDM.getScrollLeft();
2623
2624             // Location of the bottom of the element
2625             var bot = h + y;
2626
2627             // Location of the right of the element
2628             var right = w + x;
2629
2630             // The distance from the cursor to the bottom of the visible area,
2631             // adjusted so that we don't scroll if the cursor is beyond the
2632             // element drag constraints
2633             var toBot = (clientH + st - y - this.deltaY);
2634
2635             // The distance from the cursor to the right of the visible area
2636             var toRight = (clientW + sl - x - this.deltaX);
2637
2638
2639             // How close to the edge the cursor must be before we scroll
2640             // var thresh = (document.all) ? 100 : 40;
2641             var thresh = 40;
2642
2643             // How many pixels to scroll per autoscroll op.  This helps to reduce
2644             // clunky scrolling. IE is more sensitive about this ... it needs this
2645             // value to be higher.
2646             var scrAmt = (document.all) ? 80 : 30;
2647
2648             // Scroll down if we are near the bottom of the visible page and the
2649             // obj extends below the crease
2650             if ( bot > clientH && toBot < thresh ) {
2651                 window.scrollTo(sl, st + scrAmt);
2652             }
2653
2654             // Scroll up if the window is scrolled down and the top of the object
2655             // goes above the top border
2656             if ( y < st && st > 0 && y - st < thresh ) {
2657                 window.scrollTo(sl, st - scrAmt);
2658             }
2659
2660             // Scroll right if the obj is beyond the right border and the cursor is
2661             // near the border.
2662             if ( right > clientW && toRight < thresh ) {
2663                 window.scrollTo(sl + scrAmt, st);
2664             }
2665
2666             // Scroll left if the window has been scrolled to the right and the obj
2667             // extends past the left border
2668             if ( x < sl && sl > 0 && x - sl < thresh ) {
2669                 window.scrollTo(sl - scrAmt, st);
2670             }
2671         }
2672     },
2673
2674     /**
2675      * Finds the location the element should be placed if we want to move
2676      * it to where the mouse location less the click offset would place us.
2677      * @method getTargetCoord
2678      * @param {int} iPageX the X coordinate of the click
2679      * @param {int} iPageY the Y coordinate of the click
2680      * @return an object that contains the coordinates (Object.x and Object.y)
2681      * @private
2682      */
2683     getTargetCoord: function(iPageX, iPageY) {
2684
2685
2686         var x = iPageX - this.deltaX;
2687         var y = iPageY - this.deltaY;
2688
2689         if (this.constrainX) {
2690             if (x < this.minX) { x = this.minX; }
2691             if (x > this.maxX) { x = this.maxX; }
2692         }
2693
2694         if (this.constrainY) {
2695             if (y < this.minY) { y = this.minY; }
2696             if (y > this.maxY) { y = this.maxY; }
2697         }
2698
2699         x = this.getTick(x, this.xTicks);
2700         y = this.getTick(y, this.yTicks);
2701
2702
2703         return {x:x, y:y};
2704     },
2705
2706     /*
2707      * Sets up config options specific to this class. Overrides
2708      * Roo.dd.DragDrop, but all versions of this method through the
2709      * inheritance chain are called
2710      */
2711     applyConfig: function() {
2712         Roo.dd.DD.superclass.applyConfig.call(this);
2713         this.scroll = (this.config.scroll !== false);
2714     },
2715
2716     /*
2717      * Event that fires prior to the onMouseDown event.  Overrides
2718      * Roo.dd.DragDrop.
2719      */
2720     b4MouseDown: function(e) {
2721         // this.resetConstraints();
2722         this.autoOffset(e.getPageX(),
2723                             e.getPageY());
2724     },
2725
2726     /*
2727      * Event that fires prior to the onDrag event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4Drag: function(e) {
2731         this.setDragElPos(e.getPageX(),
2732                             e.getPageY());
2733     },
2734
2735     toString: function() {
2736         return ("DD " + this.id);
2737     }
2738
2739     //////////////////////////////////////////////////////////////////////////
2740     // Debugging ygDragDrop events that can be overridden
2741     //////////////////////////////////////////////////////////////////////////
2742     /*
2743     startDrag: function(x, y) {
2744     },
2745
2746     onDrag: function(e) {
2747     },
2748
2749     onDragEnter: function(e, id) {
2750     },
2751
2752     onDragOver: function(e, id) {
2753     },
2754
2755     onDragOut: function(e, id) {
2756     },
2757
2758     onDragDrop: function(e, id) {
2759     },
2760
2761     endDrag: function(e) {
2762     }
2763
2764     */
2765
2766 });/*
2767  * Based on:
2768  * Ext JS Library 1.1.1
2769  * Copyright(c) 2006-2007, Ext JS, LLC.
2770  *
2771  * Originally Released Under LGPL - original licence link has changed is not relivant.
2772  *
2773  * Fork - LGPL
2774  * <script type="text/javascript">
2775  */
2776
2777 /**
2778  * @class Roo.dd.DDProxy
2779  * A DragDrop implementation that inserts an empty, bordered div into
2780  * the document that follows the cursor during drag operations.  At the time of
2781  * the click, the frame div is resized to the dimensions of the linked html
2782  * element, and moved to the exact location of the linked element.
2783  *
2784  * References to the "frame" element refer to the single proxy element that
2785  * was created to be dragged in place of all DDProxy elements on the
2786  * page.
2787  *
2788  * @extends Roo.dd.DD
2789  * @constructor
2790  * @param {String} id the id of the linked html element
2791  * @param {String} sGroup the group of related DragDrop objects
2792  * @param {object} config an object containing configurable attributes
2793  *                Valid properties for DDProxy in addition to those in DragDrop:
2794  *                   resizeFrame, centerFrame, dragElId
2795  */
2796 Roo.dd.DDProxy = function(id, sGroup, config) {
2797     if (id) {
2798         this.init(id, sGroup, config);
2799         this.initFrame();
2800     }
2801 };
2802
2803 /**
2804  * The default drag frame div id
2805  * @property Roo.dd.DDProxy.dragElId
2806  * @type String
2807  * @static
2808  */
2809 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2810
2811 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2812
2813     /**
2814      * By default we resize the drag frame to be the same size as the element
2815      * we want to drag (this is to get the frame effect).  We can turn it off
2816      * if we want a different behavior.
2817      * @property resizeFrame
2818      * @type boolean
2819      */
2820     resizeFrame: true,
2821
2822     /**
2823      * By default the frame is positioned exactly where the drag element is, so
2824      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2825      * you do not have constraints on the obj is to have the drag frame centered
2826      * around the cursor.  Set centerFrame to true for this effect.
2827      * @property centerFrame
2828      * @type boolean
2829      */
2830     centerFrame: false,
2831
2832     /**
2833      * Creates the proxy element if it does not yet exist
2834      * @method createFrame
2835      */
2836     createFrame: function() {
2837         var self = this;
2838         var body = document.body;
2839
2840         if (!body || !body.firstChild) {
2841             setTimeout( function() { self.createFrame(); }, 50 );
2842             return;
2843         }
2844
2845         var div = this.getDragEl();
2846
2847         if (!div) {
2848             div    = document.createElement("div");
2849             div.id = this.dragElId;
2850             var s  = div.style;
2851
2852             s.position   = "absolute";
2853             s.visibility = "hidden";
2854             s.cursor     = "move";
2855             s.border     = "2px solid #aaa";
2856             s.zIndex     = 999;
2857
2858             // appendChild can blow up IE if invoked prior to the window load event
2859             // while rendering a table.  It is possible there are other scenarios
2860             // that would cause this to happen as well.
2861             body.insertBefore(div, body.firstChild);
2862         }
2863     },
2864
2865     /**
2866      * Initialization for the drag frame element.  Must be called in the
2867      * constructor of all subclasses
2868      * @method initFrame
2869      */
2870     initFrame: function() {
2871         this.createFrame();
2872     },
2873
2874     applyConfig: function() {
2875         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2876
2877         this.resizeFrame = (this.config.resizeFrame !== false);
2878         this.centerFrame = (this.config.centerFrame);
2879         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2880     },
2881
2882     /**
2883      * Resizes the drag frame to the dimensions of the clicked object, positions
2884      * it over the object, and finally displays it
2885      * @method showFrame
2886      * @param {int} iPageX X click position
2887      * @param {int} iPageY Y click position
2888      * @private
2889      */
2890     showFrame: function(iPageX, iPageY) {
2891         var el = this.getEl();
2892         var dragEl = this.getDragEl();
2893         var s = dragEl.style;
2894
2895         this._resizeProxy();
2896
2897         if (this.centerFrame) {
2898             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2899                            Math.round(parseInt(s.height, 10)/2) );
2900         }
2901
2902         this.setDragElPos(iPageX, iPageY);
2903
2904         Roo.fly(dragEl).show();
2905     },
2906
2907     /**
2908      * The proxy is automatically resized to the dimensions of the linked
2909      * element when a drag is initiated, unless resizeFrame is set to false
2910      * @method _resizeProxy
2911      * @private
2912      */
2913     _resizeProxy: function() {
2914         if (this.resizeFrame) {
2915             var el = this.getEl();
2916             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2917         }
2918     },
2919
2920     // overrides Roo.dd.DragDrop
2921     b4MouseDown: function(e) {
2922         var x = e.getPageX();
2923         var y = e.getPageY();
2924         this.autoOffset(x, y);
2925         this.setDragElPos(x, y);
2926     },
2927
2928     // overrides Roo.dd.DragDrop
2929     b4StartDrag: function(x, y) {
2930         // show the drag frame
2931         this.showFrame(x, y);
2932     },
2933
2934     // overrides Roo.dd.DragDrop
2935     b4EndDrag: function(e) {
2936         Roo.fly(this.getDragEl()).hide();
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     // By default we try to move the element to the last location of the frame.
2941     // This is so that the default behavior mirrors that of Roo.dd.DD.
2942     endDrag: function(e) {
2943
2944         var lel = this.getEl();
2945         var del = this.getDragEl();
2946
2947         // Show the drag frame briefly so we can get its position
2948         del.style.visibility = "";
2949
2950         this.beforeMove();
2951         // Hide the linked element before the move to get around a Safari
2952         // rendering bug.
2953         lel.style.visibility = "hidden";
2954         Roo.dd.DDM.moveToEl(lel, del);
2955         del.style.visibility = "hidden";
2956         lel.style.visibility = "";
2957
2958         this.afterDrag();
2959     },
2960
2961     beforeMove : function(){
2962
2963     },
2964
2965     afterDrag : function(){
2966
2967     },
2968
2969     toString: function() {
2970         return ("DDProxy " + this.id);
2971     }
2972
2973 });
2974 /*
2975  * Based on:
2976  * Ext JS Library 1.1.1
2977  * Copyright(c) 2006-2007, Ext JS, LLC.
2978  *
2979  * Originally Released Under LGPL - original licence link has changed is not relivant.
2980  *
2981  * Fork - LGPL
2982  * <script type="text/javascript">
2983  */
2984
2985  /**
2986  * @class Roo.dd.DDTarget
2987  * A DragDrop implementation that does not move, but can be a drop
2988  * target.  You would get the same result by simply omitting implementation
2989  * for the event callbacks, but this way we reduce the processing cost of the
2990  * event listener and the callbacks.
2991  * @extends Roo.dd.DragDrop
2992  * @constructor
2993  * @param {String} id the id of the element that is a drop target
2994  * @param {String} sGroup the group of related DragDrop objects
2995  * @param {object} config an object containing configurable attributes
2996  *                 Valid properties for DDTarget in addition to those in
2997  *                 DragDrop:
2998  *                    none
2999  */
3000 Roo.dd.DDTarget = function(id, sGroup, config) {
3001     if (id) {
3002         this.initTarget(id, sGroup, config);
3003     }
3004     if (config.listeners || config.events) { 
3005        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3006             listeners : config.listeners || {}, 
3007             events : config.events || {} 
3008         });    
3009     }
3010 };
3011
3012 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3013 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3014     toString: function() {
3015         return ("DDTarget " + this.id);
3016     }
3017 });
3018 /*
3019  * Based on:
3020  * Ext JS Library 1.1.1
3021  * Copyright(c) 2006-2007, Ext JS, LLC.
3022  *
3023  * Originally Released Under LGPL - original licence link has changed is not relivant.
3024  *
3025  * Fork - LGPL
3026  * <script type="text/javascript">
3027  */
3028  
3029
3030 /**
3031  * @class Roo.dd.ScrollManager
3032  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3033  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3034  * @singleton
3035  */
3036 Roo.dd.ScrollManager = function(){
3037     var ddm = Roo.dd.DragDropMgr;
3038     var els = {};
3039     var dragEl = null;
3040     var proc = {};
3041     
3042     var onStop = function(e){
3043         dragEl = null;
3044         clearProc();
3045     };
3046     
3047     var triggerRefresh = function(){
3048         if(ddm.dragCurrent){
3049              ddm.refreshCache(ddm.dragCurrent.groups);
3050         }
3051     };
3052     
3053     var doScroll = function(){
3054         if(ddm.dragCurrent){
3055             var dds = Roo.dd.ScrollManager;
3056             if(!dds.animate){
3057                 if(proc.el.scroll(proc.dir, dds.increment)){
3058                     triggerRefresh();
3059                 }
3060             }else{
3061                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3062             }
3063         }
3064     };
3065     
3066     var clearProc = function(){
3067         if(proc.id){
3068             clearInterval(proc.id);
3069         }
3070         proc.id = 0;
3071         proc.el = null;
3072         proc.dir = "";
3073     };
3074     
3075     var startProc = function(el, dir){
3076         clearProc();
3077         proc.el = el;
3078         proc.dir = dir;
3079         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3080     };
3081     
3082     var onFire = function(e, isDrop){
3083         if(isDrop || !ddm.dragCurrent){ return; }
3084         var dds = Roo.dd.ScrollManager;
3085         if(!dragEl || dragEl != ddm.dragCurrent){
3086             dragEl = ddm.dragCurrent;
3087             // refresh regions on drag start
3088             dds.refreshCache();
3089         }
3090         
3091         var xy = Roo.lib.Event.getXY(e);
3092         var pt = new Roo.lib.Point(xy[0], xy[1]);
3093         for(var id in els){
3094             var el = els[id], r = el._region;
3095             if(r && r.contains(pt) && el.isScrollable()){
3096                 if(r.bottom - pt.y <= dds.thresh){
3097                     if(proc.el != el){
3098                         startProc(el, "down");
3099                     }
3100                     return;
3101                 }else if(r.right - pt.x <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "left");
3104                     }
3105                     return;
3106                 }else if(pt.y - r.top <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "up");
3109                     }
3110                     return;
3111                 }else if(pt.x - r.left <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "right");
3114                     }
3115                     return;
3116                 }
3117             }
3118         }
3119         clearProc();
3120     };
3121     
3122     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3123     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3124     
3125     return {
3126         /**
3127          * Registers new overflow element(s) to auto scroll
3128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3129          */
3130         register : function(el){
3131             if(el instanceof Array){
3132                 for(var i = 0, len = el.length; i < len; i++) {
3133                         this.register(el[i]);
3134                 }
3135             }else{
3136                 el = Roo.get(el);
3137                 els[el.id] = el;
3138             }
3139         },
3140         
3141         /**
3142          * Unregisters overflow element(s) so they are no longer scrolled
3143          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3144          */
3145         unregister : function(el){
3146             if(el instanceof Array){
3147                 for(var i = 0, len = el.length; i < len; i++) {
3148                         this.unregister(el[i]);
3149                 }
3150             }else{
3151                 el = Roo.get(el);
3152                 delete els[el.id];
3153             }
3154         },
3155         
3156         /**
3157          * The number of pixels from the edge of a container the pointer needs to be to 
3158          * trigger scrolling (defaults to 25)
3159          * @type Number
3160          */
3161         thresh : 25,
3162         
3163         /**
3164          * The number of pixels to scroll in each scroll increment (defaults to 50)
3165          * @type Number
3166          */
3167         increment : 100,
3168         
3169         /**
3170          * The frequency of scrolls in milliseconds (defaults to 500)
3171          * @type Number
3172          */
3173         frequency : 500,
3174         
3175         /**
3176          * True to animate the scroll (defaults to true)
3177          * @type Boolean
3178          */
3179         animate: true,
3180         
3181         /**
3182          * The animation duration in seconds - 
3183          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3184          * @type Number
3185          */
3186         animDuration: .4,
3187         
3188         /**
3189          * Manually trigger a cache refresh.
3190          */
3191         refreshCache : function(){
3192             for(var id in els){
3193                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3194                     els[id]._region = els[id].getRegion();
3195                 }
3196             }
3197         }
3198     };
3199 }();/*
3200  * Based on:
3201  * Ext JS Library 1.1.1
3202  * Copyright(c) 2006-2007, Ext JS, LLC.
3203  *
3204  * Originally Released Under LGPL - original licence link has changed is not relivant.
3205  *
3206  * Fork - LGPL
3207  * <script type="text/javascript">
3208  */
3209  
3210
3211 /**
3212  * @class Roo.dd.Registry
3213  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3214  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3215  * @singleton
3216  */
3217 Roo.dd.Registry = function(){
3218     var elements = {}; 
3219     var handles = {}; 
3220     var autoIdSeed = 0;
3221
3222     var getId = function(el, autogen){
3223         if(typeof el == "string"){
3224             return el;
3225         }
3226         var id = el.id;
3227         if(!id && autogen !== false){
3228             id = "roodd-" + (++autoIdSeed);
3229             el.id = id;
3230         }
3231         return id;
3232     };
3233     
3234     return {
3235     /**
3236      * Register a drag drop element
3237      * @param {String|HTMLElement} element The id or DOM node to register
3238      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3239      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3240      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3241      * populated in the data object (if applicable):
3242      * <pre>
3243 Value      Description<br />
3244 ---------  ------------------------------------------<br />
3245 handles    Array of DOM nodes that trigger dragging<br />
3246            for the element being registered<br />
3247 isHandle   True if the element passed in triggers<br />
3248            dragging itself, else false
3249 </pre>
3250      */
3251         register : function(el, data){
3252             data = data || {};
3253             if(typeof el == "string"){
3254                 el = document.getElementById(el);
3255             }
3256             data.ddel = el;
3257             elements[getId(el)] = data;
3258             if(data.isHandle !== false){
3259                 handles[data.ddel.id] = data;
3260             }
3261             if(data.handles){
3262                 var hs = data.handles;
3263                 for(var i = 0, len = hs.length; i < len; i++){
3264                         handles[getId(hs[i])] = data;
3265                 }
3266             }
3267         },
3268
3269     /**
3270      * Unregister a drag drop element
3271      * @param {String|HTMLElement}  element The id or DOM node to unregister
3272      */
3273         unregister : function(el){
3274             var id = getId(el, false);
3275             var data = elements[id];
3276             if(data){
3277                 delete elements[id];
3278                 if(data.handles){
3279                     var hs = data.handles;
3280                     for(var i = 0, len = hs.length; i < len; i++){
3281                         delete handles[getId(hs[i], false)];
3282                     }
3283                 }
3284             }
3285         },
3286
3287     /**
3288      * Returns the handle registered for a DOM Node by id
3289      * @param {String|HTMLElement} id The DOM node or id to look up
3290      * @return {Object} handle The custom handle data
3291      */
3292         getHandle : function(id){
3293             if(typeof id != "string"){ // must be element?
3294                 id = id.id;
3295             }
3296             return handles[id];
3297         },
3298
3299     /**
3300      * Returns the handle that is registered for the DOM node that is the target of the event
3301      * @param {Event} e The event
3302      * @return {Object} handle The custom handle data
3303      */
3304         getHandleFromEvent : function(e){
3305             var t = Roo.lib.Event.getTarget(e);
3306             return t ? handles[t.id] : null;
3307         },
3308
3309     /**
3310      * Returns a custom data object that is registered for a DOM node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} data The custom data
3313      */
3314         getTarget : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return elements[id];
3319         },
3320
3321     /**
3322      * Returns a custom data object that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} data The custom data
3325      */
3326         getTargetFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? elements[t.id] || handles[t.id] : null;
3329         }
3330     };
3331 }();/*
3332  * Based on:
3333  * Ext JS Library 1.1.1
3334  * Copyright(c) 2006-2007, Ext JS, LLC.
3335  *
3336  * Originally Released Under LGPL - original licence link has changed is not relivant.
3337  *
3338  * Fork - LGPL
3339  * <script type="text/javascript">
3340  */
3341  
3342
3343 /**
3344  * @class Roo.dd.StatusProxy
3345  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3346  * default drag proxy used by all Roo.dd components.
3347  * @constructor
3348  * @param {Object} config
3349  */
3350 Roo.dd.StatusProxy = function(config){
3351     Roo.apply(this, config);
3352     this.id = this.id || Roo.id();
3353     this.el = new Roo.Layer({
3354         dh: {
3355             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3356                 {tag: "div", cls: "x-dd-drop-icon"},
3357                 {tag: "div", cls: "x-dd-drag-ghost"}
3358             ]
3359         }, 
3360         shadow: !config || config.shadow !== false
3361     });
3362     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3363     this.dropStatus = this.dropNotAllowed;
3364 };
3365
3366 Roo.dd.StatusProxy.prototype = {
3367     /**
3368      * @cfg {String} dropAllowed
3369      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3370      */
3371     dropAllowed : "x-dd-drop-ok",
3372     /**
3373      * @cfg {String} dropNotAllowed
3374      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3375      */
3376     dropNotAllowed : "x-dd-drop-nodrop",
3377
3378     /**
3379      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3380      * over the current target element.
3381      * @param {String} cssClass The css class for the new drop status indicator image
3382      */
3383     setStatus : function(cssClass){
3384         cssClass = cssClass || this.dropNotAllowed;
3385         if(this.dropStatus != cssClass){
3386             this.el.replaceClass(this.dropStatus, cssClass);
3387             this.dropStatus = cssClass;
3388         }
3389     },
3390
3391     /**
3392      * Resets the status indicator to the default dropNotAllowed value
3393      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3394      */
3395     reset : function(clearGhost){
3396         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3397         this.dropStatus = this.dropNotAllowed;
3398         if(clearGhost){
3399             this.ghost.update("");
3400         }
3401     },
3402
3403     /**
3404      * Updates the contents of the ghost element
3405      * @param {String} html The html that will replace the current innerHTML of the ghost element
3406      */
3407     update : function(html){
3408         if(typeof html == "string"){
3409             this.ghost.update(html);
3410         }else{
3411             this.ghost.update("");
3412             html.style.margin = "0";
3413             this.ghost.dom.appendChild(html);
3414         }
3415         // ensure float = none set?? cant remember why though.
3416         var el = this.ghost.dom.firstChild;
3417                 if(el){
3418                         Roo.fly(el).setStyle('float', 'none');
3419                 }
3420     },
3421     
3422     /**
3423      * Returns the underlying proxy {@link Roo.Layer}
3424      * @return {Roo.Layer} el
3425     */
3426     getEl : function(){
3427         return this.el;
3428     },
3429
3430     /**
3431      * Returns the ghost element
3432      * @return {Roo.Element} el
3433      */
3434     getGhost : function(){
3435         return this.ghost;
3436     },
3437
3438     /**
3439      * Hides the proxy
3440      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3441      */
3442     hide : function(clear){
3443         this.el.hide();
3444         if(clear){
3445             this.reset(true);
3446         }
3447     },
3448
3449     /**
3450      * Stops the repair animation if it's currently running
3451      */
3452     stop : function(){
3453         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3454             this.anim.stop();
3455         }
3456     },
3457
3458     /**
3459      * Displays this proxy
3460      */
3461     show : function(){
3462         this.el.show();
3463     },
3464
3465     /**
3466      * Force the Layer to sync its shadow and shim positions to the element
3467      */
3468     sync : function(){
3469         this.el.sync();
3470     },
3471
3472     /**
3473      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3474      * invalid drop operation by the item being dragged.
3475      * @param {Array} xy The XY position of the element ([x, y])
3476      * @param {Function} callback The function to call after the repair is complete
3477      * @param {Object} scope The scope in which to execute the callback
3478      */
3479     repair : function(xy, callback, scope){
3480         this.callback = callback;
3481         this.scope = scope;
3482         if(xy && this.animRepair !== false){
3483             this.el.addClass("x-dd-drag-repair");
3484             this.el.hideUnders(true);
3485             this.anim = this.el.shift({
3486                 duration: this.repairDuration || .5,
3487                 easing: 'easeOut',
3488                 xy: xy,
3489                 stopFx: true,
3490                 callback: this.afterRepair,
3491                 scope: this
3492             });
3493         }else{
3494             this.afterRepair();
3495         }
3496     },
3497
3498     // private
3499     afterRepair : function(){
3500         this.hide(true);
3501         if(typeof this.callback == "function"){
3502             this.callback.call(this.scope || this);
3503         }
3504         this.callback = null;
3505         this.scope = null;
3506     }
3507 };/*
3508  * Based on:
3509  * Ext JS Library 1.1.1
3510  * Copyright(c) 2006-2007, Ext JS, LLC.
3511  *
3512  * Originally Released Under LGPL - original licence link has changed is not relivant.
3513  *
3514  * Fork - LGPL
3515  * <script type="text/javascript">
3516  */
3517
3518 /**
3519  * @class Roo.dd.DragSource
3520  * @extends Roo.dd.DDProxy
3521  * A simple class that provides the basic implementation needed to make any element draggable.
3522  * @constructor
3523  * @param {String/HTMLElement/Element} el The container element
3524  * @param {Object} config
3525  */
3526 Roo.dd.DragSource = function(el, config){
3527     this.el = Roo.get(el);
3528     this.dragData = {};
3529     
3530     Roo.apply(this, config);
3531     
3532     if(!this.proxy){
3533         this.proxy = new Roo.dd.StatusProxy();
3534     }
3535
3536     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3537           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3538     
3539     this.dragging = false;
3540 };
3541
3542 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3543     /**
3544      * @cfg {String} dropAllowed
3545      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3546      */
3547     dropAllowed : "x-dd-drop-ok",
3548     /**
3549      * @cfg {String} dropNotAllowed
3550      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3551      */
3552     dropNotAllowed : "x-dd-drop-nodrop",
3553
3554     /**
3555      * Returns the data object associated with this drag source
3556      * @return {Object} data An object containing arbitrary data
3557      */
3558     getDragData : function(e){
3559         return this.dragData;
3560     },
3561
3562     // private
3563     onDragEnter : function(e, id){
3564         var target = Roo.dd.DragDropMgr.getDDById(id);
3565         this.cachedTarget = target;
3566         if(this.beforeDragEnter(target, e, id) !== false){
3567             if(target.isNotifyTarget){
3568                 var status = target.notifyEnter(this, e, this.dragData);
3569                 this.proxy.setStatus(status);
3570             }else{
3571                 this.proxy.setStatus(this.dropAllowed);
3572             }
3573             
3574             if(this.afterDragEnter){
3575                 /**
3576                  * An empty function by default, but provided so that you can perform a custom action
3577                  * when the dragged item enters the drop target by providing an implementation.
3578                  * @param {Roo.dd.DragDrop} target The drop target
3579                  * @param {Event} e The event object
3580                  * @param {String} id The id of the dragged element
3581                  * @method afterDragEnter
3582                  */
3583                 this.afterDragEnter(target, e, id);
3584             }
3585         }
3586     },
3587
3588     /**
3589      * An empty function by default, but provided so that you can perform a custom action
3590      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3591      * @param {Roo.dd.DragDrop} target The drop target
3592      * @param {Event} e The event object
3593      * @param {String} id The id of the dragged element
3594      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3595      */
3596     beforeDragEnter : function(target, e, id){
3597         return true;
3598     },
3599
3600     // private
3601     alignElWithMouse: function() {
3602         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3603         this.proxy.sync();
3604     },
3605
3606     // private
3607     onDragOver : function(e, id){
3608         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3609         if(this.beforeDragOver(target, e, id) !== false){
3610             if(target.isNotifyTarget){
3611                 var status = target.notifyOver(this, e, this.dragData);
3612                 this.proxy.setStatus(status);
3613             }
3614
3615             if(this.afterDragOver){
3616                 /**
3617                  * An empty function by default, but provided so that you can perform a custom action
3618                  * while the dragged item is over the drop target by providing an implementation.
3619                  * @param {Roo.dd.DragDrop} target The drop target
3620                  * @param {Event} e The event object
3621                  * @param {String} id The id of the dragged element
3622                  * @method afterDragOver
3623                  */
3624                 this.afterDragOver(target, e, id);
3625             }
3626         }
3627     },
3628
3629     /**
3630      * An empty function by default, but provided so that you can perform a custom action
3631      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3632      * @param {Roo.dd.DragDrop} target The drop target
3633      * @param {Event} e The event object
3634      * @param {String} id The id of the dragged element
3635      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3636      */
3637     beforeDragOver : function(target, e, id){
3638         return true;
3639     },
3640
3641     // private
3642     onDragOut : function(e, id){
3643         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3644         if(this.beforeDragOut(target, e, id) !== false){
3645             if(target.isNotifyTarget){
3646                 target.notifyOut(this, e, this.dragData);
3647             }
3648             this.proxy.reset();
3649             if(this.afterDragOut){
3650                 /**
3651                  * An empty function by default, but provided so that you can perform a custom action
3652                  * after the dragged item is dragged out of the target without dropping.
3653                  * @param {Roo.dd.DragDrop} target The drop target
3654                  * @param {Event} e The event object
3655                  * @param {String} id The id of the dragged element
3656                  * @method afterDragOut
3657                  */
3658                 this.afterDragOut(target, e, id);
3659             }
3660         }
3661         this.cachedTarget = null;
3662     },
3663
3664     /**
3665      * An empty function by default, but provided so that you can perform a custom action before the dragged
3666      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3667      * @param {Roo.dd.DragDrop} target The drop target
3668      * @param {Event} e The event object
3669      * @param {String} id The id of the dragged element
3670      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3671      */
3672     beforeDragOut : function(target, e, id){
3673         return true;
3674     },
3675     
3676     // private
3677     onDragDrop : function(e, id){
3678         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3679         if(this.beforeDragDrop(target, e, id) !== false){
3680             if(target.isNotifyTarget){
3681                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3682                     this.onValidDrop(target, e, id);
3683                 }else{
3684                     this.onInvalidDrop(target, e, id);
3685                 }
3686             }else{
3687                 this.onValidDrop(target, e, id);
3688             }
3689             
3690             if(this.afterDragDrop){
3691                 /**
3692                  * An empty function by default, but provided so that you can perform a custom action
3693                  * after a valid drag drop has occurred by providing an implementation.
3694                  * @param {Roo.dd.DragDrop} target The drop target
3695                  * @param {Event} e The event object
3696                  * @param {String} id The id of the dropped element
3697                  * @method afterDragDrop
3698                  */
3699                 this.afterDragDrop(target, e, id);
3700             }
3701         }
3702         delete this.cachedTarget;
3703     },
3704
3705     /**
3706      * An empty function by default, but provided so that you can perform a custom action before the dragged
3707      * item is dropped onto the target and optionally cancel the onDragDrop.
3708      * @param {Roo.dd.DragDrop} target The drop target
3709      * @param {Event} e The event object
3710      * @param {String} id The id of the dragged element
3711      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3712      */
3713     beforeDragDrop : function(target, e, id){
3714         return true;
3715     },
3716
3717     // private
3718     onValidDrop : function(target, e, id){
3719         this.hideProxy();
3720         if(this.afterValidDrop){
3721             /**
3722              * An empty function by default, but provided so that you can perform a custom action
3723              * after a valid drop has occurred by providing an implementation.
3724              * @param {Object} target The target DD 
3725              * @param {Event} e The event object
3726              * @param {String} id The id of the dropped element
3727              * @method afterInvalidDrop
3728              */
3729             this.afterValidDrop(target, e, id);
3730         }
3731     },
3732
3733     // private
3734     getRepairXY : function(e, data){
3735         return this.el.getXY();  
3736     },
3737
3738     // private
3739     onInvalidDrop : function(target, e, id){
3740         this.beforeInvalidDrop(target, e, id);
3741         if(this.cachedTarget){
3742             if(this.cachedTarget.isNotifyTarget){
3743                 this.cachedTarget.notifyOut(this, e, this.dragData);
3744             }
3745             this.cacheTarget = null;
3746         }
3747         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3748
3749         if(this.afterInvalidDrop){
3750             /**
3751              * An empty function by default, but provided so that you can perform a custom action
3752              * after an invalid drop has occurred by providing an implementation.
3753              * @param {Event} e The event object
3754              * @param {String} id The id of the dropped element
3755              * @method afterInvalidDrop
3756              */
3757             this.afterInvalidDrop(e, id);
3758         }
3759     },
3760
3761     // private
3762     afterRepair : function(){
3763         if(Roo.enableFx){
3764             this.el.highlight(this.hlColor || "c3daf9");
3765         }
3766         this.dragging = false;
3767     },
3768
3769     /**
3770      * An empty function by default, but provided so that you can perform a custom action after an invalid
3771      * drop has occurred.
3772      * @param {Roo.dd.DragDrop} target The drop target
3773      * @param {Event} e The event object
3774      * @param {String} id The id of the dragged element
3775      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3776      */
3777     beforeInvalidDrop : function(target, e, id){
3778         return true;
3779     },
3780
3781     // private
3782     handleMouseDown : function(e){
3783         if(this.dragging) {
3784             return;
3785         }
3786         var data = this.getDragData(e);
3787         if(data && this.onBeforeDrag(data, e) !== false){
3788             this.dragData = data;
3789             this.proxy.stop();
3790             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3791         } 
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action before the initial
3796      * drag event begins and optionally cancel it.
3797      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3798      * @param {Event} e The event object
3799      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3800      */
3801     onBeforeDrag : function(data, e){
3802         return true;
3803     },
3804
3805     /**
3806      * An empty function by default, but provided so that you can perform a custom action once the initial
3807      * drag event has begun.  The drag cannot be canceled from this function.
3808      * @param {Number} x The x position of the click on the dragged object
3809      * @param {Number} y The y position of the click on the dragged object
3810      */
3811     onStartDrag : Roo.emptyFn,
3812
3813     // private - YUI override
3814     startDrag : function(x, y){
3815         this.proxy.reset();
3816         this.dragging = true;
3817         this.proxy.update("");
3818         this.onInitDrag(x, y);
3819         this.proxy.show();
3820     },
3821
3822     // private
3823     onInitDrag : function(x, y){
3824         var clone = this.el.dom.cloneNode(true);
3825         clone.id = Roo.id(); // prevent duplicate ids
3826         this.proxy.update(clone);
3827         this.onStartDrag(x, y);
3828         return true;
3829     },
3830
3831     /**
3832      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3833      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3834      */
3835     getProxy : function(){
3836         return this.proxy;  
3837     },
3838
3839     /**
3840      * Hides the drag source's {@link Roo.dd.StatusProxy}
3841      */
3842     hideProxy : function(){
3843         this.proxy.hide();  
3844         this.proxy.reset(true);
3845         this.dragging = false;
3846     },
3847
3848     // private
3849     triggerCacheRefresh : function(){
3850         Roo.dd.DDM.refreshCache(this.groups);
3851     },
3852
3853     // private - override to prevent hiding
3854     b4EndDrag: function(e) {
3855     },
3856
3857     // private - override to prevent moving
3858     endDrag : function(e){
3859         this.onEndDrag(this.dragData, e);
3860     },
3861
3862     // private
3863     onEndDrag : function(data, e){
3864     },
3865     
3866     // private - pin to cursor
3867     autoOffset : function(x, y) {
3868         this.setDelta(-12, -20);
3869     }    
3870 });/*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880
3881
3882 /**
3883  * @class Roo.dd.DropTarget
3884  * @extends Roo.dd.DDTarget
3885  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3886  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3887  * @constructor
3888  * @param {String/HTMLElement/Element} el The container element
3889  * @param {Object} config
3890  */
3891 Roo.dd.DropTarget = function(el, config){
3892     this.el = Roo.get(el);
3893     
3894     var listeners = false; ;
3895     if (config & config.listeners) {
3896         listeners= config.listeners;
3897         delete config.listeners;
3898     }
3899     Roo.apply(this, config);
3900     
3901     if(this.containerScroll){
3902         Roo.dd.ScrollManager.register(this.el);
3903     }
3904     this.addEvents( {
3905          /**
3906          * @scope Roo.dd.DropTarget
3907          */
3908          
3909          /**
3910          * @event enter
3911          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3912          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3913          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3914          * 
3915          * IMPORTANT : it should set this.overClass and this.dropAllowed
3916          * 
3917          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3918          * @param {Event} e The event
3919          * @param {Object} data An object containing arbitrary data supplied by the drag source
3920          */
3921         "enter" : true,
3922         
3923          /**
3924          * @event over
3925          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3926          * This method will be called on every mouse movement while the drag source is over the drop target.
3927          * This default implementation simply returns the dropAllowed config value.
3928          * 
3929          * IMPORTANT : it should set this.dropAllowed
3930          * 
3931          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3932          * @param {Event} e The event
3933          * @param {Object} data An object containing arbitrary data supplied by the drag source
3934          
3935          */
3936         "over" : true,
3937         /**
3938          * @event out
3939          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3940          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3941          * overClass (if any) from the drop element.
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946          "out" : true,
3947          
3948         /**
3949          * @event drop
3950          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3951          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3952          * implementation that does something to process the drop event and returns true so that the drag source's
3953          * repair action does not run.
3954          * 
3955          * IMPORTANT : it should set this.success
3956          * 
3957          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3958          * @param {Event} e The event
3959          * @param {Object} data An object containing arbitrary data supplied by the drag source
3960         */
3961          "drop" : true
3962     });
3963             
3964      
3965     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3966         this.el.dom, 
3967         this.ddGroup || this.group,
3968         {
3969             isTarget: true,
3970             listeners : listeners || {} 
3971            
3972         
3973         }
3974     );
3975
3976 };
3977
3978 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3979     /**
3980      * @cfg {String} overClass
3981      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3982      */
3983      /**
3984      * @cfg {String} ddGroup
3985      * The drag drop group to handle drop events for
3986      */
3987      
3988     /**
3989      * @cfg {String} dropAllowed
3990      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3991      */
3992     dropAllowed : "x-dd-drop-ok",
3993     /**
3994      * @cfg {String} dropNotAllowed
3995      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3996      */
3997     dropNotAllowed : "x-dd-drop-nodrop",
3998     /**
3999      * @cfg {boolean} success
4000      * set this after drop listener.. 
4001      */
4002     success : false,
4003     /**
4004      * @cfg {boolean} valid
4005      * if the drop point is valid for over/enter..
4006      */
4007     valid : false,
4008     // private
4009     isTarget : true,
4010
4011     // private
4012     isNotifyTarget : true,
4013     
4014     /**
4015      * @hide
4016      */
4017     notifyEnter : function(dd, e, data){
4018         this.valid = true;
4019         this.fireEvent('enter', this, dd, e, data);
4020         if(this.overClass){
4021             this.el.addClass(this.overClass);
4022         }
4023         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4024     },
4025
4026     /**
4027      * @hide
4028      */
4029     notifyOver : function(dd, e, data){
4030         this.valid = true;
4031         this.fireEvent('over', this, dd, e, data);
4032         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4033     },
4034
4035     /**
4036      * @hide
4037      */
4038     notifyOut : function(dd, e, data){
4039         this.fireEvent('out', this, dd, e, data);
4040         if(this.overClass){
4041             this.el.removeClass(this.overClass);
4042         }
4043     },
4044
4045     /**
4046      * @hide
4047      */
4048     notifyDrop : function(dd, e, data){
4049         this.success = false;
4050         this.fireEvent('drop', this, dd, e, data);
4051         return this.success;
4052     }
4053 });/*
4054  * Based on:
4055  * Ext JS Library 1.1.1
4056  * Copyright(c) 2006-2007, Ext JS, LLC.
4057  *
4058  * Originally Released Under LGPL - original licence link has changed is not relivant.
4059  *
4060  * Fork - LGPL
4061  * <script type="text/javascript">
4062  */
4063
4064
4065 /**
4066  * @class Roo.dd.DragZone
4067  * @extends Roo.dd.DragSource
4068  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4069  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4070  * @constructor
4071  * @param {String/HTMLElement/Element} el The container element
4072  * @param {Object} config
4073  */
4074 Roo.dd.DragZone = function(el, config){
4075     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4076     if(this.containerScroll){
4077         Roo.dd.ScrollManager.register(this.el);
4078     }
4079 };
4080
4081 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4082     /**
4083      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4084      * for auto scrolling during drag operations.
4085      */
4086     /**
4087      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4088      * method after a failed drop (defaults to "c3daf9" - light blue)
4089      */
4090
4091     /**
4092      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4093      * for a valid target to drag based on the mouse down. Override this method
4094      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4095      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4096      * @param {EventObject} e The mouse down event
4097      * @return {Object} The dragData
4098      */
4099     getDragData : function(e){
4100         return Roo.dd.Registry.getHandleFromEvent(e);
4101     },
4102     
4103     /**
4104      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4105      * this.dragData.ddel
4106      * @param {Number} x The x position of the click on the dragged object
4107      * @param {Number} y The y position of the click on the dragged object
4108      * @return {Boolean} true to continue the drag, false to cancel
4109      */
4110     onInitDrag : function(x, y){
4111         this.proxy.update(this.dragData.ddel.cloneNode(true));
4112         this.onStartDrag(x, y);
4113         return true;
4114     },
4115     
4116     /**
4117      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4118      */
4119     afterRepair : function(){
4120         if(Roo.enableFx){
4121             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4122         }
4123         this.dragging = false;
4124     },
4125
4126     /**
4127      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4128      * the XY of this.dragData.ddel
4129      * @param {EventObject} e The mouse up event
4130      * @return {Array} The xy location (e.g. [100, 200])
4131      */
4132     getRepairXY : function(e){
4133         return Roo.Element.fly(this.dragData.ddel).getXY();  
4134     }
4135 });/*
4136  * Based on:
4137  * Ext JS Library 1.1.1
4138  * Copyright(c) 2006-2007, Ext JS, LLC.
4139  *
4140  * Originally Released Under LGPL - original licence link has changed is not relivant.
4141  *
4142  * Fork - LGPL
4143  * <script type="text/javascript">
4144  */
4145 /**
4146  * @class Roo.dd.DropZone
4147  * @extends Roo.dd.DropTarget
4148  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4149  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4150  * @constructor
4151  * @param {String/HTMLElement/Element} el The container element
4152  * @param {Object} config
4153  */
4154 Roo.dd.DropZone = function(el, config){
4155     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4156 };
4157
4158 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4159     /**
4160      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4161      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4162      * provide your own custom lookup.
4163      * @param {Event} e The event
4164      * @return {Object} data The custom data
4165      */
4166     getTargetFromEvent : function(e){
4167         return Roo.dd.Registry.getTargetFromEvent(e);
4168     },
4169
4170     /**
4171      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4172      * that it has registered.  This method has no default implementation and should be overridden to provide
4173      * node-specific processing if necessary.
4174      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4175      * {@link #getTargetFromEvent} for this node)
4176      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4177      * @param {Event} e The event
4178      * @param {Object} data An object containing arbitrary data supplied by the drag source
4179      */
4180     onNodeEnter : function(n, dd, e, data){
4181         
4182     },
4183
4184     /**
4185      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4186      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4187      * overridden to provide the proper feedback.
4188      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4189      * {@link #getTargetFromEvent} for this node)
4190      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4191      * @param {Event} e The event
4192      * @param {Object} data An object containing arbitrary data supplied by the drag source
4193      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4194      * underlying {@link Roo.dd.StatusProxy} can be updated
4195      */
4196     onNodeOver : function(n, dd, e, data){
4197         return this.dropAllowed;
4198     },
4199
4200     /**
4201      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4202      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4203      * node-specific processing if necessary.
4204      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4205      * {@link #getTargetFromEvent} for this node)
4206      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4207      * @param {Event} e The event
4208      * @param {Object} data An object containing arbitrary data supplied by the drag source
4209      */
4210     onNodeOut : function(n, dd, e, data){
4211         
4212     },
4213
4214     /**
4215      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4216      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4217      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4218      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4219      * {@link #getTargetFromEvent} for this node)
4220      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4221      * @param {Event} e The event
4222      * @param {Object} data An object containing arbitrary data supplied by the drag source
4223      * @return {Boolean} True if the drop was valid, else false
4224      */
4225     onNodeDrop : function(n, dd, e, data){
4226         return false;
4227     },
4228
4229     /**
4230      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4231      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4232      * it should be overridden to provide the proper feedback if necessary.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag source
4236      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4237      * underlying {@link Roo.dd.StatusProxy} can be updated
4238      */
4239     onContainerOver : function(dd, e, data){
4240         return this.dropNotAllowed;
4241     },
4242
4243     /**
4244      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4245      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4246      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4247      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {Boolean} True if the drop was valid, else false
4252      */
4253     onContainerDrop : function(dd, e, data){
4254         return false;
4255     },
4256
4257     /**
4258      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4259      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4260      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4261      * you should override this method and provide a custom implementation.
4262      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4263      * @param {Event} e The event
4264      * @param {Object} data An object containing arbitrary data supplied by the drag source
4265      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4266      * underlying {@link Roo.dd.StatusProxy} can be updated
4267      */
4268     notifyEnter : function(dd, e, data){
4269         return this.dropNotAllowed;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4274      * This method will be called on every mouse movement while the drag source is over the drop zone.
4275      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4276      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4277      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4278      * registered node, it will call {@link #onContainerOver}.
4279      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4280      * @param {Event} e The event
4281      * @param {Object} data An object containing arbitrary data supplied by the drag source
4282      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4283      * underlying {@link Roo.dd.StatusProxy} can be updated
4284      */
4285     notifyOver : function(dd, e, data){
4286         var n = this.getTargetFromEvent(e);
4287         if(!n){ // not over valid drop target
4288             if(this.lastOverNode){
4289                 this.onNodeOut(this.lastOverNode, dd, e, data);
4290                 this.lastOverNode = null;
4291             }
4292             return this.onContainerOver(dd, e, data);
4293         }
4294         if(this.lastOverNode != n){
4295             if(this.lastOverNode){
4296                 this.onNodeOut(this.lastOverNode, dd, e, data);
4297             }
4298             this.onNodeEnter(n, dd, e, data);
4299             this.lastOverNode = n;
4300         }
4301         return this.onNodeOver(n, dd, e, data);
4302     },
4303
4304     /**
4305      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4306      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4307      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4308      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4309      * @param {Event} e The event
4310      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4311      */
4312     notifyOut : function(dd, e, data){
4313         if(this.lastOverNode){
4314             this.onNodeOut(this.lastOverNode, dd, e, data);
4315             this.lastOverNode = null;
4316         }
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4321      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4322      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4323      * otherwise it will call {@link #onContainerDrop}.
4324      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4325      * @param {Event} e The event
4326      * @param {Object} data An object containing arbitrary data supplied by the drag source
4327      * @return {Boolean} True if the drop was valid, else false
4328      */
4329     notifyDrop : function(dd, e, data){
4330         if(this.lastOverNode){
4331             this.onNodeOut(this.lastOverNode, dd, e, data);
4332             this.lastOverNode = null;
4333         }
4334         var n = this.getTargetFromEvent(e);
4335         return n ?
4336             this.onNodeDrop(n, dd, e, data) :
4337             this.onContainerDrop(dd, e, data);
4338     },
4339
4340     // private
4341     triggerCacheRefresh : function(){
4342         Roo.dd.DDM.refreshCache(this.groups);
4343     }  
4344 });/*
4345  * Based on:
4346  * Ext JS Library 1.1.1
4347  * Copyright(c) 2006-2007, Ext JS, LLC.
4348  *
4349  * Originally Released Under LGPL - original licence link has changed is not relivant.
4350  *
4351  * Fork - LGPL
4352  * <script type="text/javascript">
4353  */
4354
4355
4356 /**
4357  * @class Roo.data.SortTypes
4358  * @singleton
4359  * Defines the default sorting (casting?) comparison functions used when sorting data.
4360  */
4361 Roo.data.SortTypes = {
4362     /**
4363      * Default sort that does nothing
4364      * @param {Mixed} s The value being converted
4365      * @return {Mixed} The comparison value
4366      */
4367     none : function(s){
4368         return s;
4369     },
4370     
4371     /**
4372      * The regular expression used to strip tags
4373      * @type {RegExp}
4374      * @property
4375      */
4376     stripTagsRE : /<\/?[^>]+>/gi,
4377     
4378     /**
4379      * Strips all HTML tags to sort on text only
4380      * @param {Mixed} s The value being converted
4381      * @return {String} The comparison value
4382      */
4383     asText : function(s){
4384         return String(s).replace(this.stripTagsRE, "");
4385     },
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only - Case insensitive
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asUCText : function(s){
4393         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Case insensitive string
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCString : function(s) {
4402         return String(s).toUpperCase();
4403     },
4404     
4405     /**
4406      * Date sorting
4407      * @param {Mixed} s The value being converted
4408      * @return {Number} The comparison value
4409      */
4410     asDate : function(s) {
4411         if(!s){
4412             return 0;
4413         }
4414         if(s instanceof Date){
4415             return s.getTime();
4416         }
4417         return Date.parse(String(s));
4418     },
4419     
4420     /**
4421      * Float sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Float} The comparison value
4424      */
4425     asFloat : function(s) {
4426         var val = parseFloat(String(s).replace(/,/g, ""));
4427         if(isNaN(val)) val = 0;
4428         return val;
4429     },
4430     
4431     /**
4432      * Integer sorting
4433      * @param {Mixed} s The value being converted
4434      * @return {Number} The comparison value
4435      */
4436     asInt : function(s) {
4437         var val = parseInt(String(s).replace(/,/g, ""));
4438         if(isNaN(val)) val = 0;
4439         return val;
4440     }
4441 };/*
4442  * Based on:
4443  * Ext JS Library 1.1.1
4444  * Copyright(c) 2006-2007, Ext JS, LLC.
4445  *
4446  * Originally Released Under LGPL - original licence link has changed is not relivant.
4447  *
4448  * Fork - LGPL
4449  * <script type="text/javascript">
4450  */
4451
4452 /**
4453 * @class Roo.data.Record
4454  * Instances of this class encapsulate both record <em>definition</em> information, and record
4455  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4456  * to access Records cached in an {@link Roo.data.Store} object.<br>
4457  * <p>
4458  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4459  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4460  * objects.<br>
4461  * <p>
4462  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4463  * @constructor
4464  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4465  * {@link #create}. The parameters are the same.
4466  * @param {Array} data An associative Array of data values keyed by the field name.
4467  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4468  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4469  * not specified an integer id is generated.
4470  */
4471 Roo.data.Record = function(data, id){
4472     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4473     this.data = data;
4474 };
4475
4476 /**
4477  * Generate a constructor for a specific record layout.
4478  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4479  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4480  * Each field definition object may contain the following properties: <ul>
4481  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4482  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4483  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4484  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4485  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4486  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4487  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4488  * this may be omitted.</p></li>
4489  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4490  * <ul><li>auto (Default, implies no conversion)</li>
4491  * <li>string</li>
4492  * <li>int</li>
4493  * <li>float</li>
4494  * <li>boolean</li>
4495  * <li>date</li></ul></p></li>
4496  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4497  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4498  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4499  * by the Reader into an object that will be stored in the Record. It is passed the
4500  * following parameters:<ul>
4501  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4502  * </ul></p></li>
4503  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4504  * </ul>
4505  * <br>usage:<br><pre><code>
4506 var TopicRecord = Roo.data.Record.create(
4507     {name: 'title', mapping: 'topic_title'},
4508     {name: 'author', mapping: 'username'},
4509     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4510     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4511     {name: 'lastPoster', mapping: 'user2'},
4512     {name: 'excerpt', mapping: 'post_text'}
4513 );
4514
4515 var myNewRecord = new TopicRecord({
4516     title: 'Do my job please',
4517     author: 'noobie',
4518     totalPosts: 1,
4519     lastPost: new Date(),
4520     lastPoster: 'Animal',
4521     excerpt: 'No way dude!'
4522 });
4523 myStore.add(myNewRecord);
4524 </code></pre>
4525  * @method create
4526  * @static
4527  */
4528 Roo.data.Record.create = function(o){
4529     var f = function(){
4530         f.superclass.constructor.apply(this, arguments);
4531     };
4532     Roo.extend(f, Roo.data.Record);
4533     var p = f.prototype;
4534     p.fields = new Roo.util.MixedCollection(false, function(field){
4535         return field.name;
4536     });
4537     for(var i = 0, len = o.length; i < len; i++){
4538         p.fields.add(new Roo.data.Field(o[i]));
4539     }
4540     f.getField = function(name){
4541         return p.fields.get(name);  
4542     };
4543     return f;
4544 };
4545
4546 Roo.data.Record.AUTO_ID = 1000;
4547 Roo.data.Record.EDIT = 'edit';
4548 Roo.data.Record.REJECT = 'reject';
4549 Roo.data.Record.COMMIT = 'commit';
4550
4551 Roo.data.Record.prototype = {
4552     /**
4553      * Readonly flag - true if this record has been modified.
4554      * @type Boolean
4555      */
4556     dirty : false,
4557     editing : false,
4558     error: null,
4559     modified: null,
4560
4561     // private
4562     join : function(store){
4563         this.store = store;
4564     },
4565
4566     /**
4567      * Set the named field to the specified value.
4568      * @param {String} name The name of the field to set.
4569      * @param {Object} value The value to set the field to.
4570      */
4571     set : function(name, value){
4572         if(this.data[name] == value){
4573             return;
4574         }
4575         this.dirty = true;
4576         if(!this.modified){
4577             this.modified = {};
4578         }
4579         if(typeof this.modified[name] == 'undefined'){
4580             this.modified[name] = this.data[name];
4581         }
4582         this.data[name] = value;
4583         if(!this.editing){
4584             this.store.afterEdit(this);
4585         }       
4586     },
4587
4588     /**
4589      * Get the value of the named field.
4590      * @param {String} name The name of the field to get the value of.
4591      * @return {Object} The value of the field.
4592      */
4593     get : function(name){
4594         return this.data[name]; 
4595     },
4596
4597     // private
4598     beginEdit : function(){
4599         this.editing = true;
4600         this.modified = {}; 
4601     },
4602
4603     // private
4604     cancelEdit : function(){
4605         this.editing = false;
4606         delete this.modified;
4607     },
4608
4609     // private
4610     endEdit : function(){
4611         this.editing = false;
4612         if(this.dirty && this.store){
4613             this.store.afterEdit(this);
4614         }
4615     },
4616
4617     /**
4618      * Usually called by the {@link Roo.data.Store} which owns the Record.
4619      * Rejects all changes made to the Record since either creation, or the last commit operation.
4620      * Modified fields are reverted to their original values.
4621      * <p>
4622      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4623      * of reject operations.
4624      */
4625     reject : function(){
4626         var m = this.modified;
4627         for(var n in m){
4628             if(typeof m[n] != "function"){
4629                 this.data[n] = m[n];
4630             }
4631         }
4632         this.dirty = false;
4633         delete this.modified;
4634         this.editing = false;
4635         if(this.store){
4636             this.store.afterReject(this);
4637         }
4638     },
4639
4640     /**
4641      * Usually called by the {@link Roo.data.Store} which owns the Record.
4642      * Commits all changes made to the Record since either creation, or the last commit operation.
4643      * <p>
4644      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4645      * of commit operations.
4646      */
4647     commit : function(){
4648         this.dirty = false;
4649         delete this.modified;
4650         this.editing = false;
4651         if(this.store){
4652             this.store.afterCommit(this);
4653         }
4654     },
4655
4656     // private
4657     hasError : function(){
4658         return this.error != null;
4659     },
4660
4661     // private
4662     clearError : function(){
4663         this.error = null;
4664     },
4665
4666     /**
4667      * Creates a copy of this record.
4668      * @param {String} id (optional) A new record id if you don't want to use this record's id
4669      * @return {Record}
4670      */
4671     copy : function(newId) {
4672         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4673     }
4674 };/*
4675  * Based on:
4676  * Ext JS Library 1.1.1
4677  * Copyright(c) 2006-2007, Ext JS, LLC.
4678  *
4679  * Originally Released Under LGPL - original licence link has changed is not relivant.
4680  *
4681  * Fork - LGPL
4682  * <script type="text/javascript">
4683  */
4684
4685
4686
4687 /**
4688  * @class Roo.data.Store
4689  * @extends Roo.util.Observable
4690  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4691  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4692  * <p>
4693  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4694  * has no knowledge of the format of the data returned by the Proxy.<br>
4695  * <p>
4696  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4697  * instances from the data object. These records are cached and made available through accessor functions.
4698  * @constructor
4699  * Creates a new Store.
4700  * @param {Object} config A config object containing the objects needed for the Store to access data,
4701  * and read the data into Records.
4702  */
4703 Roo.data.Store = function(config){
4704     this.data = new Roo.util.MixedCollection(false);
4705     this.data.getKey = function(o){
4706         return o.id;
4707     };
4708     this.baseParams = {};
4709     // private
4710     this.paramNames = {
4711         "start" : "start",
4712         "limit" : "limit",
4713         "sort" : "sort",
4714         "dir" : "dir"
4715     };
4716
4717     if(config && config.data){
4718         this.inlineData = config.data;
4719         delete config.data;
4720     }
4721
4722     Roo.apply(this, config);
4723     
4724     if(this.reader){ // reader passed
4725         this.reader = Roo.factory(this.reader, Roo.data);
4726         this.reader.xmodule = this.xmodule || false;
4727         if(!this.recordType){
4728             this.recordType = this.reader.recordType;
4729         }
4730         if(this.reader.onMetaChange){
4731             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4732         }
4733     }
4734
4735     if(this.recordType){
4736         this.fields = this.recordType.prototype.fields;
4737     }
4738     this.modified = [];
4739
4740     this.addEvents({
4741         /**
4742          * @event datachanged
4743          * Fires when the data cache has changed, and a widget which is using this Store
4744          * as a Record cache should refresh its view.
4745          * @param {Store} this
4746          */
4747         datachanged : true,
4748         /**
4749          * @event metachange
4750          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4751          * @param {Store} this
4752          * @param {Object} meta The JSON metadata
4753          */
4754         metachange : true,
4755         /**
4756          * @event add
4757          * Fires when Records have been added to the Store
4758          * @param {Store} this
4759          * @param {Roo.data.Record[]} records The array of Records added
4760          * @param {Number} index The index at which the record(s) were added
4761          */
4762         add : true,
4763         /**
4764          * @event remove
4765          * Fires when a Record has been removed from the Store
4766          * @param {Store} this
4767          * @param {Roo.data.Record} record The Record that was removed
4768          * @param {Number} index The index at which the record was removed
4769          */
4770         remove : true,
4771         /**
4772          * @event update
4773          * Fires when a Record has been updated
4774          * @param {Store} this
4775          * @param {Roo.data.Record} record The Record that was updated
4776          * @param {String} operation The update operation being performed.  Value may be one of:
4777          * <pre><code>
4778  Roo.data.Record.EDIT
4779  Roo.data.Record.REJECT
4780  Roo.data.Record.COMMIT
4781          * </code></pre>
4782          */
4783         update : true,
4784         /**
4785          * @event clear
4786          * Fires when the data cache has been cleared.
4787          * @param {Store} this
4788          */
4789         clear : true,
4790         /**
4791          * @event beforeload
4792          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4793          * the load action will be canceled.
4794          * @param {Store} this
4795          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4796          */
4797         beforeload : true,
4798         /**
4799          * @event load
4800          * Fires after a new set of Records has been loaded.
4801          * @param {Store} this
4802          * @param {Roo.data.Record[]} records The Records that were loaded
4803          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4804          */
4805         load : true,
4806         /**
4807          * @event loadexception
4808          * Fires if an exception occurs in the Proxy during loading.
4809          * Called with the signature of the Proxy's "loadexception" event.
4810          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4811          * 
4812          * @param {Proxy} 
4813          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4814          * @param {Object} load options 
4815          * @param {Object} jsonData from your request (normally this contains the Exception)
4816          */
4817         loadexception : true
4818     });
4819     
4820     if(this.proxy){
4821         this.proxy = Roo.factory(this.proxy, Roo.data);
4822         this.proxy.xmodule = this.xmodule || false;
4823         this.relayEvents(this.proxy,  ["loadexception"]);
4824     }
4825     this.sortToggle = {};
4826
4827     Roo.data.Store.superclass.constructor.call(this);
4828
4829     if(this.inlineData){
4830         this.loadData(this.inlineData);
4831         delete this.inlineData;
4832     }
4833 };
4834 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4835      /**
4836     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4837     * without a remote query - used by combo/forms at present.
4838     */
4839     
4840     /**
4841     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4842     */
4843     /**
4844     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4845     */
4846     /**
4847     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4848     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4849     */
4850     /**
4851     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4852     * on any HTTP request
4853     */
4854     /**
4855     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4856     */
4857     /**
4858     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4859     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4860     */
4861     remoteSort : false,
4862
4863     /**
4864     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4865      * loaded or when a record is removed. (defaults to false).
4866     */
4867     pruneModifiedRecords : false,
4868
4869     // private
4870     lastOptions : null,
4871
4872     /**
4873      * Add Records to the Store and fires the add event.
4874      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4875      */
4876     add : function(records){
4877         records = [].concat(records);
4878         for(var i = 0, len = records.length; i < len; i++){
4879             records[i].join(this);
4880         }
4881         var index = this.data.length;
4882         this.data.addAll(records);
4883         this.fireEvent("add", this, records, index);
4884     },
4885
4886     /**
4887      * Remove a Record from the Store and fires the remove event.
4888      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4889      */
4890     remove : function(record){
4891         var index = this.data.indexOf(record);
4892         this.data.removeAt(index);
4893         if(this.pruneModifiedRecords){
4894             this.modified.remove(record);
4895         }
4896         this.fireEvent("remove", this, record, index);
4897     },
4898
4899     /**
4900      * Remove all Records from the Store and fires the clear event.
4901      */
4902     removeAll : function(){
4903         this.data.clear();
4904         if(this.pruneModifiedRecords){
4905             this.modified = [];
4906         }
4907         this.fireEvent("clear", this);
4908     },
4909
4910     /**
4911      * Inserts Records to the Store at the given index and fires the add event.
4912      * @param {Number} index The start index at which to insert the passed Records.
4913      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4914      */
4915     insert : function(index, records){
4916         records = [].concat(records);
4917         for(var i = 0, len = records.length; i < len; i++){
4918             this.data.insert(index, records[i]);
4919             records[i].join(this);
4920         }
4921         this.fireEvent("add", this, records, index);
4922     },
4923
4924     /**
4925      * Get the index within the cache of the passed Record.
4926      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4927      * @return {Number} The index of the passed Record. Returns -1 if not found.
4928      */
4929     indexOf : function(record){
4930         return this.data.indexOf(record);
4931     },
4932
4933     /**
4934      * Get the index within the cache of the Record with the passed id.
4935      * @param {String} id The id of the Record to find.
4936      * @return {Number} The index of the Record. Returns -1 if not found.
4937      */
4938     indexOfId : function(id){
4939         return this.data.indexOfKey(id);
4940     },
4941
4942     /**
4943      * Get the Record with the specified id.
4944      * @param {String} id The id of the Record to find.
4945      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4946      */
4947     getById : function(id){
4948         return this.data.key(id);
4949     },
4950
4951     /**
4952      * Get the Record at the specified index.
4953      * @param {Number} index The index of the Record to find.
4954      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4955      */
4956     getAt : function(index){
4957         return this.data.itemAt(index);
4958     },
4959
4960     /**
4961      * Returns a range of Records between specified indices.
4962      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4963      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4964      * @return {Roo.data.Record[]} An array of Records
4965      */
4966     getRange : function(start, end){
4967         return this.data.getRange(start, end);
4968     },
4969
4970     // private
4971     storeOptions : function(o){
4972         o = Roo.apply({}, o);
4973         delete o.callback;
4974         delete o.scope;
4975         this.lastOptions = o;
4976     },
4977
4978     /**
4979      * Loads the Record cache from the configured Proxy using the configured Reader.
4980      * <p>
4981      * If using remote paging, then the first load call must specify the <em>start</em>
4982      * and <em>limit</em> properties in the options.params property to establish the initial
4983      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4984      * <p>
4985      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4986      * and this call will return before the new data has been loaded. Perform any post-processing
4987      * in a callback function, or in a "load" event handler.</strong>
4988      * <p>
4989      * @param {Object} options An object containing properties which control loading options:<ul>
4990      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4991      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4992      * passed the following arguments:<ul>
4993      * <li>r : Roo.data.Record[]</li>
4994      * <li>options: Options object from the load call</li>
4995      * <li>success: Boolean success indicator</li></ul></li>
4996      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4997      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4998      * </ul>
4999      */
5000     load : function(options){
5001         options = options || {};
5002         if(this.fireEvent("beforeload", this, options) !== false){
5003             this.storeOptions(options);
5004             var p = Roo.apply(options.params || {}, this.baseParams);
5005             // if meta was not loaded from remote source.. try requesting it.
5006             if (!this.reader.metaFromRemote) {
5007                 p._requestMeta = 1;
5008             }
5009             if(this.sortInfo && this.remoteSort){
5010                 var pn = this.paramNames;
5011                 p[pn["sort"]] = this.sortInfo.field;
5012                 p[pn["dir"]] = this.sortInfo.direction;
5013             }
5014             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5015         }
5016     },
5017
5018     /**
5019      * Reloads the Record cache from the configured Proxy using the configured Reader and
5020      * the options from the last load operation performed.
5021      * @param {Object} options (optional) An object containing properties which may override the options
5022      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5023      * the most recently used options are reused).
5024      */
5025     reload : function(options){
5026         this.load(Roo.applyIf(options||{}, this.lastOptions));
5027     },
5028
5029     // private
5030     // Called as a callback by the Reader during a load operation.
5031     loadRecords : function(o, options, success){
5032         if(!o || success === false){
5033             if(success !== false){
5034                 this.fireEvent("load", this, [], options);
5035             }
5036             if(options.callback){
5037                 options.callback.call(options.scope || this, [], options, false);
5038             }
5039             return;
5040         }
5041         // if data returned failure - throw an exception.
5042         if (o.success === false) {
5043             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5044             return;
5045         }
5046         var r = o.records, t = o.totalRecords || r.length;
5047         if(!options || options.add !== true){
5048             if(this.pruneModifiedRecords){
5049                 this.modified = [];
5050             }
5051             for(var i = 0, len = r.length; i < len; i++){
5052                 r[i].join(this);
5053             }
5054             if(this.snapshot){
5055                 this.data = this.snapshot;
5056                 delete this.snapshot;
5057             }
5058             this.data.clear();
5059             this.data.addAll(r);
5060             this.totalLength = t;
5061             this.applySort();
5062             this.fireEvent("datachanged", this);
5063         }else{
5064             this.totalLength = Math.max(t, this.data.length+r.length);
5065             this.add(r);
5066         }
5067         this.fireEvent("load", this, r, options);
5068         if(options.callback){
5069             options.callback.call(options.scope || this, r, options, true);
5070         }
5071     },
5072
5073     /**
5074      * Loads data from a passed data block. A Reader which understands the format of the data
5075      * must have been configured in the constructor.
5076      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5077      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5078      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5079      */
5080     loadData : function(o, append){
5081         var r = this.reader.readRecords(o);
5082         this.loadRecords(r, {add: append}, true);
5083     },
5084
5085     /**
5086      * Gets the number of cached records.
5087      * <p>
5088      * <em>If using paging, this may not be the total size of the dataset. If the data object
5089      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5090      * the data set size</em>
5091      */
5092     getCount : function(){
5093         return this.data.length || 0;
5094     },
5095
5096     /**
5097      * Gets the total number of records in the dataset as returned by the server.
5098      * <p>
5099      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5100      * the dataset size</em>
5101      */
5102     getTotalCount : function(){
5103         return this.totalLength || 0;
5104     },
5105
5106     /**
5107      * Returns the sort state of the Store as an object with two properties:
5108      * <pre><code>
5109  field {String} The name of the field by which the Records are sorted
5110  direction {String} The sort order, "ASC" or "DESC"
5111      * </code></pre>
5112      */
5113     getSortState : function(){
5114         return this.sortInfo;
5115     },
5116
5117     // private
5118     applySort : function(){
5119         if(this.sortInfo && !this.remoteSort){
5120             var s = this.sortInfo, f = s.field;
5121             var st = this.fields.get(f).sortType;
5122             var fn = function(r1, r2){
5123                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5124                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5125             };
5126             this.data.sort(s.direction, fn);
5127             if(this.snapshot && this.snapshot != this.data){
5128                 this.snapshot.sort(s.direction, fn);
5129             }
5130         }
5131     },
5132
5133     /**
5134      * Sets the default sort column and order to be used by the next load operation.
5135      * @param {String} fieldName The name of the field to sort by.
5136      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5137      */
5138     setDefaultSort : function(field, dir){
5139         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5140     },
5141
5142     /**
5143      * Sort the Records.
5144      * If remote sorting is used, the sort is performed on the server, and the cache is
5145      * reloaded. If local sorting is used, the cache is sorted internally.
5146      * @param {String} fieldName The name of the field to sort by.
5147      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5148      */
5149     sort : function(fieldName, dir){
5150         var f = this.fields.get(fieldName);
5151         if(!dir){
5152             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5153                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5154             }else{
5155                 dir = f.sortDir;
5156             }
5157         }
5158         this.sortToggle[f.name] = dir;
5159         this.sortInfo = {field: f.name, direction: dir};
5160         if(!this.remoteSort){
5161             this.applySort();
5162             this.fireEvent("datachanged", this);
5163         }else{
5164             this.load(this.lastOptions);
5165         }
5166     },
5167
5168     /**
5169      * Calls the specified function for each of the Records in the cache.
5170      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5171      * Returning <em>false</em> aborts and exits the iteration.
5172      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5173      */
5174     each : function(fn, scope){
5175         this.data.each(fn, scope);
5176     },
5177
5178     /**
5179      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5180      * (e.g., during paging).
5181      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5182      */
5183     getModifiedRecords : function(){
5184         return this.modified;
5185     },
5186
5187     // private
5188     createFilterFn : function(property, value, anyMatch){
5189         if(!value.exec){ // not a regex
5190             value = String(value);
5191             if(value.length == 0){
5192                 return false;
5193             }
5194             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5195         }
5196         return function(r){
5197             return value.test(r.data[property]);
5198         };
5199     },
5200
5201     /**
5202      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5203      * @param {String} property A field on your records
5204      * @param {Number} start The record index to start at (defaults to 0)
5205      * @param {Number} end The last record index to include (defaults to length - 1)
5206      * @return {Number} The sum
5207      */
5208     sum : function(property, start, end){
5209         var rs = this.data.items, v = 0;
5210         start = start || 0;
5211         end = (end || end === 0) ? end : rs.length-1;
5212
5213         for(var i = start; i <= end; i++){
5214             v += (rs[i].data[property] || 0);
5215         }
5216         return v;
5217     },
5218
5219     /**
5220      * Filter the records by a specified property.
5221      * @param {String} field A field on your records
5222      * @param {String/RegExp} value Either a string that the field
5223      * should start with or a RegExp to test against the field
5224      * @param {Boolean} anyMatch True to match any part not just the beginning
5225      */
5226     filter : function(property, value, anyMatch){
5227         var fn = this.createFilterFn(property, value, anyMatch);
5228         return fn ? this.filterBy(fn) : this.clearFilter();
5229     },
5230
5231     /**
5232      * Filter by a function. The specified function will be called with each
5233      * record in this data source. If the function returns true the record is included,
5234      * otherwise it is filtered.
5235      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5236      * @param {Object} scope (optional) The scope of the function (defaults to this)
5237      */
5238     filterBy : function(fn, scope){
5239         this.snapshot = this.snapshot || this.data;
5240         this.data = this.queryBy(fn, scope||this);
5241         this.fireEvent("datachanged", this);
5242     },
5243
5244     /**
5245      * Query the records by a specified property.
5246      * @param {String} field A field on your records
5247      * @param {String/RegExp} value Either a string that the field
5248      * should start with or a RegExp to test against the field
5249      * @param {Boolean} anyMatch True to match any part not just the beginning
5250      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5251      */
5252     query : function(property, value, anyMatch){
5253         var fn = this.createFilterFn(property, value, anyMatch);
5254         return fn ? this.queryBy(fn) : this.data.clone();
5255     },
5256
5257     /**
5258      * Query by a function. The specified function will be called with each
5259      * record in this data source. If the function returns true the record is included
5260      * in the results.
5261      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5262      * @param {Object} scope (optional) The scope of the function (defaults to this)
5263       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5264      **/
5265     queryBy : function(fn, scope){
5266         var data = this.snapshot || this.data;
5267         return data.filterBy(fn, scope||this);
5268     },
5269
5270     /**
5271      * Collects unique values for a particular dataIndex from this store.
5272      * @param {String} dataIndex The property to collect
5273      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5274      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5275      * @return {Array} An array of the unique values
5276      **/
5277     collect : function(dataIndex, allowNull, bypassFilter){
5278         var d = (bypassFilter === true && this.snapshot) ?
5279                 this.snapshot.items : this.data.items;
5280         var v, sv, r = [], l = {};
5281         for(var i = 0, len = d.length; i < len; i++){
5282             v = d[i].data[dataIndex];
5283             sv = String(v);
5284             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5285                 l[sv] = true;
5286                 r[r.length] = v;
5287             }
5288         }
5289         return r;
5290     },
5291
5292     /**
5293      * Revert to a view of the Record cache with no filtering applied.
5294      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5295      */
5296     clearFilter : function(suppressEvent){
5297         if(this.snapshot && this.snapshot != this.data){
5298             this.data = this.snapshot;
5299             delete this.snapshot;
5300             if(suppressEvent !== true){
5301                 this.fireEvent("datachanged", this);
5302             }
5303         }
5304     },
5305
5306     // private
5307     afterEdit : function(record){
5308         if(this.modified.indexOf(record) == -1){
5309             this.modified.push(record);
5310         }
5311         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5312     },
5313
5314     // private
5315     afterReject : function(record){
5316         this.modified.remove(record);
5317         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5318     },
5319
5320     // private
5321     afterCommit : function(record){
5322         this.modified.remove(record);
5323         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5324     },
5325
5326     /**
5327      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5328      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5329      */
5330     commitChanges : function(){
5331         var m = this.modified.slice(0);
5332         this.modified = [];
5333         for(var i = 0, len = m.length; i < len; i++){
5334             m[i].commit();
5335         }
5336     },
5337
5338     /**
5339      * Cancel outstanding changes on all changed records.
5340      */
5341     rejectChanges : function(){
5342         var m = this.modified.slice(0);
5343         this.modified = [];
5344         for(var i = 0, len = m.length; i < len; i++){
5345             m[i].reject();
5346         }
5347     },
5348
5349     onMetaChange : function(meta, rtype, o){
5350         this.recordType = rtype;
5351         this.fields = rtype.prototype.fields;
5352         delete this.snapshot;
5353         this.sortInfo = meta.sortInfo || this.sortInfo;
5354         this.modified = [];
5355         this.fireEvent('metachange', this, this.reader.meta);
5356     }
5357 });/*
5358  * Based on:
5359  * Ext JS Library 1.1.1
5360  * Copyright(c) 2006-2007, Ext JS, LLC.
5361  *
5362  * Originally Released Under LGPL - original licence link has changed is not relivant.
5363  *
5364  * Fork - LGPL
5365  * <script type="text/javascript">
5366  */
5367
5368 /**
5369  * @class Roo.data.SimpleStore
5370  * @extends Roo.data.Store
5371  * Small helper class to make creating Stores from Array data easier.
5372  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5373  * @cfg {Array} fields An array of field definition objects, or field name strings.
5374  * @cfg {Array} data The multi-dimensional array of data
5375  * @constructor
5376  * @param {Object} config
5377  */
5378 Roo.data.SimpleStore = function(config){
5379     Roo.data.SimpleStore.superclass.constructor.call(this, {
5380         isLocal : true,
5381         reader: new Roo.data.ArrayReader({
5382                 id: config.id
5383             },
5384             Roo.data.Record.create(config.fields)
5385         ),
5386         proxy : new Roo.data.MemoryProxy(config.data)
5387     });
5388     this.load();
5389 };
5390 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5391  * Based on:
5392  * Ext JS Library 1.1.1
5393  * Copyright(c) 2006-2007, Ext JS, LLC.
5394  *
5395  * Originally Released Under LGPL - original licence link has changed is not relivant.
5396  *
5397  * Fork - LGPL
5398  * <script type="text/javascript">
5399  */
5400
5401 /**
5402 /**
5403  * @extends Roo.data.Store
5404  * @class Roo.data.JsonStore
5405  * Small helper class to make creating Stores for JSON data easier. <br/>
5406 <pre><code>
5407 var store = new Roo.data.JsonStore({
5408     url: 'get-images.php',
5409     root: 'images',
5410     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5411 });
5412 </code></pre>
5413  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5414  * JsonReader and HttpProxy (unless inline data is provided).</b>
5415  * @cfg {Array} fields An array of field definition objects, or field name strings.
5416  * @constructor
5417  * @param {Object} config
5418  */
5419 Roo.data.JsonStore = function(c){
5420     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5421         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5422         reader: new Roo.data.JsonReader(c, c.fields)
5423     }));
5424 };
5425 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5426  * Based on:
5427  * Ext JS Library 1.1.1
5428  * Copyright(c) 2006-2007, Ext JS, LLC.
5429  *
5430  * Originally Released Under LGPL - original licence link has changed is not relivant.
5431  *
5432  * Fork - LGPL
5433  * <script type="text/javascript">
5434  */
5435
5436  
5437 Roo.data.Field = function(config){
5438     if(typeof config == "string"){
5439         config = {name: config};
5440     }
5441     Roo.apply(this, config);
5442     
5443     if(!this.type){
5444         this.type = "auto";
5445     }
5446     
5447     var st = Roo.data.SortTypes;
5448     // named sortTypes are supported, here we look them up
5449     if(typeof this.sortType == "string"){
5450         this.sortType = st[this.sortType];
5451     }
5452     
5453     // set default sortType for strings and dates
5454     if(!this.sortType){
5455         switch(this.type){
5456             case "string":
5457                 this.sortType = st.asUCString;
5458                 break;
5459             case "date":
5460                 this.sortType = st.asDate;
5461                 break;
5462             default:
5463                 this.sortType = st.none;
5464         }
5465     }
5466
5467     // define once
5468     var stripRe = /[\$,%]/g;
5469
5470     // prebuilt conversion function for this field, instead of
5471     // switching every time we're reading a value
5472     if(!this.convert){
5473         var cv, dateFormat = this.dateFormat;
5474         switch(this.type){
5475             case "":
5476             case "auto":
5477             case undefined:
5478                 cv = function(v){ return v; };
5479                 break;
5480             case "string":
5481                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5482                 break;
5483             case "int":
5484                 cv = function(v){
5485                     return v !== undefined && v !== null && v !== '' ?
5486                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5487                     };
5488                 break;
5489             case "float":
5490                 cv = function(v){
5491                     return v !== undefined && v !== null && v !== '' ?
5492                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5493                     };
5494                 break;
5495             case "bool":
5496             case "boolean":
5497                 cv = function(v){ return v === true || v === "true" || v == 1; };
5498                 break;
5499             case "date":
5500                 cv = function(v){
5501                     if(!v){
5502                         return '';
5503                     }
5504                     if(v instanceof Date){
5505                         return v;
5506                     }
5507                     if(dateFormat){
5508                         if(dateFormat == "timestamp"){
5509                             return new Date(v*1000);
5510                         }
5511                         return Date.parseDate(v, dateFormat);
5512                     }
5513                     var parsed = Date.parse(v);
5514                     return parsed ? new Date(parsed) : null;
5515                 };
5516              break;
5517             
5518         }
5519         this.convert = cv;
5520     }
5521 };
5522
5523 Roo.data.Field.prototype = {
5524     dateFormat: null,
5525     defaultValue: "",
5526     mapping: null,
5527     sortType : null,
5528     sortDir : "ASC"
5529 };/*
5530  * Based on:
5531  * Ext JS Library 1.1.1
5532  * Copyright(c) 2006-2007, Ext JS, LLC.
5533  *
5534  * Originally Released Under LGPL - original licence link has changed is not relivant.
5535  *
5536  * Fork - LGPL
5537  * <script type="text/javascript">
5538  */
5539  
5540 // Base class for reading structured data from a data source.  This class is intended to be
5541 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5542
5543 /**
5544  * @class Roo.data.DataReader
5545  * Base class for reading structured data from a data source.  This class is intended to be
5546  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5547  */
5548
5549 Roo.data.DataReader = function(meta, recordType){
5550     
5551     this.meta = meta;
5552     
5553     this.recordType = recordType instanceof Array ? 
5554         Roo.data.Record.create(recordType) : recordType;
5555 };
5556
5557 Roo.data.DataReader.prototype = {
5558      /**
5559      * Create an empty record
5560      * @param {Object} data (optional) - overlay some values
5561      * @return {Roo.data.Record} record created.
5562      */
5563     newRow :  function(d) {
5564         var da =  {};
5565         this.recordType.prototype.fields.each(function(c) {
5566             switch( c.type) {
5567                 case 'int' : da[c.name] = 0; break;
5568                 case 'date' : da[c.name] = new Date(); break;
5569                 case 'float' : da[c.name] = 0.0; break;
5570                 case 'boolean' : da[c.name] = false; break;
5571                 default : da[c.name] = ""; break;
5572             }
5573             
5574         });
5575         return new this.recordType(Roo.apply(da, d));
5576     }
5577     
5578 };/*
5579  * Based on:
5580  * Ext JS Library 1.1.1
5581  * Copyright(c) 2006-2007, Ext JS, LLC.
5582  *
5583  * Originally Released Under LGPL - original licence link has changed is not relivant.
5584  *
5585  * Fork - LGPL
5586  * <script type="text/javascript">
5587  */
5588
5589 /**
5590  * @class Roo.data.DataProxy
5591  * @extends Roo.data.Observable
5592  * This class is an abstract base class for implementations which provide retrieval of
5593  * unformatted data objects.<br>
5594  * <p>
5595  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5596  * (of the appropriate type which knows how to parse the data object) to provide a block of
5597  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5598  * <p>
5599  * Custom implementations must implement the load method as described in
5600  * {@link Roo.data.HttpProxy#load}.
5601  */
5602 Roo.data.DataProxy = function(){
5603     this.addEvents({
5604         /**
5605          * @event beforeload
5606          * Fires before a network request is made to retrieve a data object.
5607          * @param {Object} This DataProxy object.
5608          * @param {Object} params The params parameter to the load function.
5609          */
5610         beforeload : true,
5611         /**
5612          * @event load
5613          * Fires before the load method's callback is called.
5614          * @param {Object} This DataProxy object.
5615          * @param {Object} o The data object.
5616          * @param {Object} arg The callback argument object passed to the load function.
5617          */
5618         load : true,
5619         /**
5620          * @event loadexception
5621          * Fires if an Exception occurs during data retrieval.
5622          * @param {Object} This DataProxy object.
5623          * @param {Object} o The data object.
5624          * @param {Object} arg The callback argument object passed to the load function.
5625          * @param {Object} e The Exception.
5626          */
5627         loadexception : true
5628     });
5629     Roo.data.DataProxy.superclass.constructor.call(this);
5630 };
5631
5632 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5633
5634     /**
5635      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5636      */
5637 /*
5638  * Based on:
5639  * Ext JS Library 1.1.1
5640  * Copyright(c) 2006-2007, Ext JS, LLC.
5641  *
5642  * Originally Released Under LGPL - original licence link has changed is not relivant.
5643  *
5644  * Fork - LGPL
5645  * <script type="text/javascript">
5646  */
5647 /**
5648  * @class Roo.data.MemoryProxy
5649  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5650  * to the Reader when its load method is called.
5651  * @constructor
5652  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5653  */
5654 Roo.data.MemoryProxy = function(data){
5655     if (data.data) {
5656         data = data.data;
5657     }
5658     Roo.data.MemoryProxy.superclass.constructor.call(this);
5659     this.data = data;
5660 };
5661
5662 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5663     /**
5664      * Load data from the requested source (in this case an in-memory
5665      * data object passed to the constructor), read the data object into
5666      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5667      * process that block using the passed callback.
5668      * @param {Object} params This parameter is not used by the MemoryProxy class.
5669      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5670      * object into a block of Roo.data.Records.
5671      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5672      * The function must be passed <ul>
5673      * <li>The Record block object</li>
5674      * <li>The "arg" argument from the load function</li>
5675      * <li>A boolean success indicator</li>
5676      * </ul>
5677      * @param {Object} scope The scope in which to call the callback
5678      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5679      */
5680     load : function(params, reader, callback, scope, arg){
5681         params = params || {};
5682         var result;
5683         try {
5684             result = reader.readRecords(this.data);
5685         }catch(e){
5686             this.fireEvent("loadexception", this, arg, null, e);
5687             callback.call(scope, null, arg, false);
5688             return;
5689         }
5690         callback.call(scope, result, arg, true);
5691     },
5692     
5693     // private
5694     update : function(params, records){
5695         
5696     }
5697 });/*
5698  * Based on:
5699  * Ext JS Library 1.1.1
5700  * Copyright(c) 2006-2007, Ext JS, LLC.
5701  *
5702  * Originally Released Under LGPL - original licence link has changed is not relivant.
5703  *
5704  * Fork - LGPL
5705  * <script type="text/javascript">
5706  */
5707 /**
5708  * @class Roo.data.HttpProxy
5709  * @extends Roo.data.DataProxy
5710  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5711  * configured to reference a certain URL.<br><br>
5712  * <p>
5713  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5714  * from which the running page was served.<br><br>
5715  * <p>
5716  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5717  * <p>
5718  * Be aware that to enable the browser to parse an XML document, the server must set
5719  * the Content-Type header in the HTTP response to "text/xml".
5720  * @constructor
5721  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5722  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5723  * will be used to make the request.
5724  */
5725 Roo.data.HttpProxy = function(conn){
5726     Roo.data.HttpProxy.superclass.constructor.call(this);
5727     // is conn a conn config or a real conn?
5728     this.conn = conn;
5729     this.useAjax = !conn || !conn.events;
5730   
5731 };
5732
5733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5734     // thse are take from connection...
5735     
5736     /**
5737      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5738      */
5739     /**
5740      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5741      * extra parameters to each request made by this object. (defaults to undefined)
5742      */
5743     /**
5744      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5745      *  to each request made by this object. (defaults to undefined)
5746      */
5747     /**
5748      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5749      */
5750     /**
5751      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5752      */
5753      /**
5754      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5755      * @type Boolean
5756      */
5757   
5758
5759     /**
5760      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5761      * @type Boolean
5762      */
5763     /**
5764      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5765      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5766      * a finer-grained basis than the DataProxy events.
5767      */
5768     getConnection : function(){
5769         return this.useAjax ? Roo.Ajax : this.conn;
5770     },
5771
5772     /**
5773      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5774      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5775      * process that block using the passed callback.
5776      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5777      * for the request to the remote server.
5778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5779      * object into a block of Roo.data.Records.
5780      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5781      * The function must be passed <ul>
5782      * <li>The Record block object</li>
5783      * <li>The "arg" argument from the load function</li>
5784      * <li>A boolean success indicator</li>
5785      * </ul>
5786      * @param {Object} scope The scope in which to call the callback
5787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5788      */
5789     load : function(params, reader, callback, scope, arg){
5790         if(this.fireEvent("beforeload", this, params) !== false){
5791             var  o = {
5792                 params : params || {},
5793                 request: {
5794                     callback : callback,
5795                     scope : scope,
5796                     arg : arg
5797                 },
5798                 reader: reader,
5799                 callback : this.loadResponse,
5800                 scope: this
5801             };
5802             if(this.useAjax){
5803                 Roo.applyIf(o, this.conn);
5804                 if(this.activeRequest){
5805                     Roo.Ajax.abort(this.activeRequest);
5806                 }
5807                 this.activeRequest = Roo.Ajax.request(o);
5808             }else{
5809                 this.conn.request(o);
5810             }
5811         }else{
5812             callback.call(scope||this, null, arg, false);
5813         }
5814     },
5815
5816     // private
5817     loadResponse : function(o, success, response){
5818         delete this.activeRequest;
5819         if(!success){
5820             this.fireEvent("loadexception", this, o, response);
5821             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5822             return;
5823         }
5824         var result;
5825         try {
5826             result = o.reader.read(response);
5827         }catch(e){
5828             this.fireEvent("loadexception", this, o, response, e);
5829             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5830             return;
5831         }
5832         
5833         this.fireEvent("load", this, o, o.request.arg);
5834         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5835     },
5836
5837     // private
5838     update : function(dataSet){
5839
5840     },
5841
5842     // private
5843     updateResponse : function(dataSet){
5844
5845     }
5846 });/*
5847  * Based on:
5848  * Ext JS Library 1.1.1
5849  * Copyright(c) 2006-2007, Ext JS, LLC.
5850  *
5851  * Originally Released Under LGPL - original licence link has changed is not relivant.
5852  *
5853  * Fork - LGPL
5854  * <script type="text/javascript">
5855  */
5856
5857 /**
5858  * @class Roo.data.ScriptTagProxy
5859  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5860  * other than the originating domain of the running page.<br><br>
5861  * <p>
5862  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5863  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5864  * <p>
5865  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5866  * source code that is used as the source inside a &lt;script> tag.<br><br>
5867  * <p>
5868  * In order for the browser to process the returned data, the server must wrap the data object
5869  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5870  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5871  * depending on whether the callback name was passed:
5872  * <p>
5873  * <pre><code>
5874 boolean scriptTag = false;
5875 String cb = request.getParameter("callback");
5876 if (cb != null) {
5877     scriptTag = true;
5878     response.setContentType("text/javascript");
5879 } else {
5880     response.setContentType("application/x-json");
5881 }
5882 Writer out = response.getWriter();
5883 if (scriptTag) {
5884     out.write(cb + "(");
5885 }
5886 out.print(dataBlock.toJsonString());
5887 if (scriptTag) {
5888     out.write(");");
5889 }
5890 </pre></code>
5891  *
5892  * @constructor
5893  * @param {Object} config A configuration object.
5894  */
5895 Roo.data.ScriptTagProxy = function(config){
5896     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5897     Roo.apply(this, config);
5898     this.head = document.getElementsByTagName("head")[0];
5899 };
5900
5901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5902
5903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5904     /**
5905      * @cfg {String} url The URL from which to request the data object.
5906      */
5907     /**
5908      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5909      */
5910     timeout : 30000,
5911     /**
5912      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5913      * the server the name of the callback function set up by the load call to process the returned data object.
5914      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5915      * javascript output which calls this named function passing the data object as its only parameter.
5916      */
5917     callbackParam : "callback",
5918     /**
5919      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5920      * name to the request.
5921      */
5922     nocache : true,
5923
5924     /**
5925      * Load data from the configured URL, read the data object into
5926      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5927      * process that block using the passed callback.
5928      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5929      * for the request to the remote server.
5930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5931      * object into a block of Roo.data.Records.
5932      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5933      * The function must be passed <ul>
5934      * <li>The Record block object</li>
5935      * <li>The "arg" argument from the load function</li>
5936      * <li>A boolean success indicator</li>
5937      * </ul>
5938      * @param {Object} scope The scope in which to call the callback
5939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5940      */
5941     load : function(params, reader, callback, scope, arg){
5942         if(this.fireEvent("beforeload", this, params) !== false){
5943
5944             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5945
5946             var url = this.url;
5947             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5948             if(this.nocache){
5949                 url += "&_dc=" + (new Date().getTime());
5950             }
5951             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5952             var trans = {
5953                 id : transId,
5954                 cb : "stcCallback"+transId,
5955                 scriptId : "stcScript"+transId,
5956                 params : params,
5957                 arg : arg,
5958                 url : url,
5959                 callback : callback,
5960                 scope : scope,
5961                 reader : reader
5962             };
5963             var conn = this;
5964
5965             window[trans.cb] = function(o){
5966                 conn.handleResponse(o, trans);
5967             };
5968
5969             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5970
5971             if(this.autoAbort !== false){
5972                 this.abort();
5973             }
5974
5975             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5976
5977             var script = document.createElement("script");
5978             script.setAttribute("src", url);
5979             script.setAttribute("type", "text/javascript");
5980             script.setAttribute("id", trans.scriptId);
5981             this.head.appendChild(script);
5982
5983             this.trans = trans;
5984         }else{
5985             callback.call(scope||this, null, arg, false);
5986         }
5987     },
5988
5989     // private
5990     isLoading : function(){
5991         return this.trans ? true : false;
5992     },
5993
5994     /**
5995      * Abort the current server request.
5996      */
5997     abort : function(){
5998         if(this.isLoading()){
5999             this.destroyTrans(this.trans);
6000         }
6001     },
6002
6003     // private
6004     destroyTrans : function(trans, isLoaded){
6005         this.head.removeChild(document.getElementById(trans.scriptId));
6006         clearTimeout(trans.timeoutId);
6007         if(isLoaded){
6008             window[trans.cb] = undefined;
6009             try{
6010                 delete window[trans.cb];
6011             }catch(e){}
6012         }else{
6013             // if hasn't been loaded, wait for load to remove it to prevent script error
6014             window[trans.cb] = function(){
6015                 window[trans.cb] = undefined;
6016                 try{
6017                     delete window[trans.cb];
6018                 }catch(e){}
6019             };
6020         }
6021     },
6022
6023     // private
6024     handleResponse : function(o, trans){
6025         this.trans = false;
6026         this.destroyTrans(trans, true);
6027         var result;
6028         try {
6029             result = trans.reader.readRecords(o);
6030         }catch(e){
6031             this.fireEvent("loadexception", this, o, trans.arg, e);
6032             trans.callback.call(trans.scope||window, null, trans.arg, false);
6033             return;
6034         }
6035         this.fireEvent("load", this, o, trans.arg);
6036         trans.callback.call(trans.scope||window, result, trans.arg, true);
6037     },
6038
6039     // private
6040     handleFailure : function(trans){
6041         this.trans = false;
6042         this.destroyTrans(trans, false);
6043         this.fireEvent("loadexception", this, null, trans.arg);
6044         trans.callback.call(trans.scope||window, null, trans.arg, false);
6045     }
6046 });/*
6047  * Based on:
6048  * Ext JS Library 1.1.1
6049  * Copyright(c) 2006-2007, Ext JS, LLC.
6050  *
6051  * Originally Released Under LGPL - original licence link has changed is not relivant.
6052  *
6053  * Fork - LGPL
6054  * <script type="text/javascript">
6055  */
6056
6057 /**
6058  * @class Roo.data.JsonReader
6059  * @extends Roo.data.DataReader
6060  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6061  * based on mappings in a provided Roo.data.Record constructor.
6062  * 
6063  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6064  * in the reply previously. 
6065  * 
6066  * <p>
6067  * Example code:
6068  * <pre><code>
6069 var RecordDef = Roo.data.Record.create([
6070     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6071     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6072 ]);
6073 var myReader = new Roo.data.JsonReader({
6074     totalProperty: "results",    // The property which contains the total dataset size (optional)
6075     root: "rows",                // The property which contains an Array of row objects
6076     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6077 }, RecordDef);
6078 </code></pre>
6079  * <p>
6080  * This would consume a JSON file like this:
6081  * <pre><code>
6082 { 'results': 2, 'rows': [
6083     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6084     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6085 }
6086 </code></pre>
6087  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6088  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6089  * paged from the remote server.
6090  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6091  * @cfg {String} root name of the property which contains the Array of row objects.
6092  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6093  * @constructor
6094  * Create a new JsonReader
6095  * @param {Object} meta Metadata configuration options
6096  * @param {Object} recordType Either an Array of field definition objects,
6097  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6098  */
6099 Roo.data.JsonReader = function(meta, recordType){
6100     
6101     meta = meta || {};
6102     // set some defaults:
6103     Roo.applyIf(meta, {
6104         totalProperty: 'total',
6105         successProperty : 'success',
6106         root : 'data',
6107         id : 'id'
6108     });
6109     
6110     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6111 };
6112 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6113     
6114     /**
6115      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6116      * Used by Store query builder to append _requestMeta to params.
6117      * 
6118      */
6119     metaFromRemote : false,
6120     /**
6121      * This method is only used by a DataProxy which has retrieved data from a remote server.
6122      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6123      * @return {Object} data A data block which is used by an Roo.data.Store object as
6124      * a cache of Roo.data.Records.
6125      */
6126     read : function(response){
6127         var json = response.responseText;
6128        
6129         var o = /* eval:var:o */ eval("("+json+")");
6130         if(!o) {
6131             throw {message: "JsonReader.read: Json object not found"};
6132         }
6133         
6134         if(o.metaData){
6135             
6136             delete this.ef;
6137             this.metaFromRemote = true;
6138             this.meta = o.metaData;
6139             this.recordType = Roo.data.Record.create(o.metaData.fields);
6140             this.onMetaChange(this.meta, this.recordType, o);
6141         }
6142         return this.readRecords(o);
6143     },
6144
6145     // private function a store will implement
6146     onMetaChange : function(meta, recordType, o){
6147
6148     },
6149
6150     /**
6151          * @ignore
6152          */
6153     simpleAccess: function(obj, subsc) {
6154         return obj[subsc];
6155     },
6156
6157         /**
6158          * @ignore
6159          */
6160     getJsonAccessor: function(){
6161         var re = /[\[\.]/;
6162         return function(expr) {
6163             try {
6164                 return(re.test(expr))
6165                     ? new Function("obj", "return obj." + expr)
6166                     : function(obj){
6167                         return obj[expr];
6168                     };
6169             } catch(e){}
6170             return Roo.emptyFn;
6171         };
6172     }(),
6173
6174     /**
6175      * Create a data block containing Roo.data.Records from an XML document.
6176      * @param {Object} o An object which contains an Array of row objects in the property specified
6177      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6178      * which contains the total size of the dataset.
6179      * @return {Object} data A data block which is used by an Roo.data.Store object as
6180      * a cache of Roo.data.Records.
6181      */
6182     readRecords : function(o){
6183         /**
6184          * After any data loads, the raw JSON data is available for further custom processing.
6185          * @type Object
6186          */
6187         this.jsonData = o;
6188         var s = this.meta, Record = this.recordType,
6189             f = Record.prototype.fields, fi = f.items, fl = f.length;
6190
6191 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6192         if (!this.ef) {
6193             if(s.totalProperty) {
6194                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6195                 }
6196                 if(s.successProperty) {
6197                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6198                 }
6199                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6200                 if (s.id) {
6201                         var g = this.getJsonAccessor(s.id);
6202                         this.getId = function(rec) {
6203                                 var r = g(rec);
6204                                 return (r === undefined || r === "") ? null : r;
6205                         };
6206                 } else {
6207                         this.getId = function(){return null;};
6208                 }
6209             this.ef = [];
6210             for(var jj = 0; jj < fl; jj++){
6211                 f = fi[jj];
6212                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6213                 this.ef[jj] = this.getJsonAccessor(map);
6214             }
6215         }
6216
6217         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6218         if(s.totalProperty){
6219             var vt = parseInt(this.getTotal(o), 10);
6220             if(!isNaN(vt)){
6221                 totalRecords = vt;
6222             }
6223         }
6224         if(s.successProperty){
6225             var vs = this.getSuccess(o);
6226             if(vs === false || vs === 'false'){
6227                 success = false;
6228             }
6229         }
6230         var records = [];
6231             for(var i = 0; i < c; i++){
6232                     var n = root[i];
6233                 var values = {};
6234                 var id = this.getId(n);
6235                 for(var j = 0; j < fl; j++){
6236                     f = fi[j];
6237                 var v = this.ef[j](n);
6238                 if (!f.convert) {
6239                     Roo.log('missing convert for ' + f.name);
6240                     Roo.log(f);
6241                     continue;
6242                 }
6243                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6244                 }
6245                 var record = new Record(values, id);
6246                 record.json = n;
6247                 records[i] = record;
6248             }
6249             return {
6250                 success : success,
6251                 records : records,
6252                 totalRecords : totalRecords
6253             };
6254     }
6255 });/*
6256  * Based on:
6257  * Ext JS Library 1.1.1
6258  * Copyright(c) 2006-2007, Ext JS, LLC.
6259  *
6260  * Originally Released Under LGPL - original licence link has changed is not relivant.
6261  *
6262  * Fork - LGPL
6263  * <script type="text/javascript">
6264  */
6265
6266 /**
6267  * @class Roo.data.XmlReader
6268  * @extends Roo.data.DataReader
6269  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6270  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6271  * <p>
6272  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6273  * header in the HTTP response must be set to "text/xml".</em>
6274  * <p>
6275  * Example code:
6276  * <pre><code>
6277 var RecordDef = Roo.data.Record.create([
6278    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6279    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6280 ]);
6281 var myReader = new Roo.data.XmlReader({
6282    totalRecords: "results", // The element which contains the total dataset size (optional)
6283    record: "row",           // The repeated element which contains row information
6284    id: "id"                 // The element within the row that provides an ID for the record (optional)
6285 }, RecordDef);
6286 </code></pre>
6287  * <p>
6288  * This would consume an XML file like this:
6289  * <pre><code>
6290 &lt;?xml?>
6291 &lt;dataset>
6292  &lt;results>2&lt;/results>
6293  &lt;row>
6294    &lt;id>1&lt;/id>
6295    &lt;name>Bill&lt;/name>
6296    &lt;occupation>Gardener&lt;/occupation>
6297  &lt;/row>
6298  &lt;row>
6299    &lt;id>2&lt;/id>
6300    &lt;name>Ben&lt;/name>
6301    &lt;occupation>Horticulturalist&lt;/occupation>
6302  &lt;/row>
6303 &lt;/dataset>
6304 </code></pre>
6305  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6306  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6307  * paged from the remote server.
6308  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6309  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6310  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6311  * a record identifier value.
6312  * @constructor
6313  * Create a new XmlReader
6314  * @param {Object} meta Metadata configuration options
6315  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6316  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6317  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6318  */
6319 Roo.data.XmlReader = function(meta, recordType){
6320     meta = meta || {};
6321     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6322 };
6323 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6324     /**
6325      * This method is only used by a DataProxy which has retrieved data from a remote server.
6326          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6327          * to contain a method called 'responseXML' that returns an XML document object.
6328      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6329      * a cache of Roo.data.Records.
6330      */
6331     read : function(response){
6332         var doc = response.responseXML;
6333         if(!doc) {
6334             throw {message: "XmlReader.read: XML Document not available"};
6335         }
6336         return this.readRecords(doc);
6337     },
6338
6339     /**
6340      * Create a data block containing Roo.data.Records from an XML document.
6341          * @param {Object} doc A parsed XML document.
6342      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6343      * a cache of Roo.data.Records.
6344      */
6345     readRecords : function(doc){
6346         /**
6347          * After any data loads/reads, the raw XML Document is available for further custom processing.
6348          * @type XMLDocument
6349          */
6350         this.xmlData = doc;
6351         var root = doc.documentElement || doc;
6352         var q = Roo.DomQuery;
6353         var recordType = this.recordType, fields = recordType.prototype.fields;
6354         var sid = this.meta.id;
6355         var totalRecords = 0, success = true;
6356         if(this.meta.totalRecords){
6357             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6358         }
6359         
6360         if(this.meta.success){
6361             var sv = q.selectValue(this.meta.success, root, true);
6362             success = sv !== false && sv !== 'false';
6363         }
6364         var records = [];
6365         var ns = q.select(this.meta.record, root);
6366         for(var i = 0, len = ns.length; i < len; i++) {
6367                 var n = ns[i];
6368                 var values = {};
6369                 var id = sid ? q.selectValue(sid, n) : undefined;
6370                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6371                     var f = fields.items[j];
6372                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6373                     v = f.convert(v);
6374                     values[f.name] = v;
6375                 }
6376                 var record = new recordType(values, id);
6377                 record.node = n;
6378                 records[records.length] = record;
6379             }
6380
6381             return {
6382                 success : success,
6383                 records : records,
6384                 totalRecords : totalRecords || records.length
6385             };
6386     }
6387 });/*
6388  * Based on:
6389  * Ext JS Library 1.1.1
6390  * Copyright(c) 2006-2007, Ext JS, LLC.
6391  *
6392  * Originally Released Under LGPL - original licence link has changed is not relivant.
6393  *
6394  * Fork - LGPL
6395  * <script type="text/javascript">
6396  */
6397
6398 /**
6399  * @class Roo.data.ArrayReader
6400  * @extends Roo.data.DataReader
6401  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6402  * Each element of that Array represents a row of data fields. The
6403  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6404  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6405  * <p>
6406  * Example code:.
6407  * <pre><code>
6408 var RecordDef = Roo.data.Record.create([
6409     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6410     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6411 ]);
6412 var myReader = new Roo.data.ArrayReader({
6413     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6414 }, RecordDef);
6415 </code></pre>
6416  * <p>
6417  * This would consume an Array like this:
6418  * <pre><code>
6419 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6420   </code></pre>
6421  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6422  * @constructor
6423  * Create a new JsonReader
6424  * @param {Object} meta Metadata configuration options.
6425  * @param {Object} recordType Either an Array of field definition objects
6426  * as specified to {@link Roo.data.Record#create},
6427  * or an {@link Roo.data.Record} object
6428  * created using {@link Roo.data.Record#create}.
6429  */
6430 Roo.data.ArrayReader = function(meta, recordType){
6431     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6432 };
6433
6434 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6435     /**
6436      * Create a data block containing Roo.data.Records from an XML document.
6437      * @param {Object} o An Array of row objects which represents the dataset.
6438      * @return {Object} data A data block which is used by an Roo.data.Store object as
6439      * a cache of Roo.data.Records.
6440      */
6441     readRecords : function(o){
6442         var sid = this.meta ? this.meta.id : null;
6443         var recordType = this.recordType, fields = recordType.prototype.fields;
6444         var records = [];
6445         var root = o;
6446             for(var i = 0; i < root.length; i++){
6447                     var n = root[i];
6448                 var values = {};
6449                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6450                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6451                 var f = fields.items[j];
6452                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6453                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6454                 v = f.convert(v);
6455                 values[f.name] = v;
6456             }
6457                 var record = new recordType(values, id);
6458                 record.json = n;
6459                 records[records.length] = record;
6460             }
6461             return {
6462                 records : records,
6463                 totalRecords : records.length
6464             };
6465     }
6466 });/*
6467  * Based on:
6468  * Ext JS Library 1.1.1
6469  * Copyright(c) 2006-2007, Ext JS, LLC.
6470  *
6471  * Originally Released Under LGPL - original licence link has changed is not relivant.
6472  *
6473  * Fork - LGPL
6474  * <script type="text/javascript">
6475  */
6476
6477
6478 /**
6479  * @class Roo.data.Tree
6480  * @extends Roo.util.Observable
6481  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6482  * in the tree have most standard DOM functionality.
6483  * @constructor
6484  * @param {Node} root (optional) The root node
6485  */
6486 Roo.data.Tree = function(root){
6487    this.nodeHash = {};
6488    /**
6489     * The root node for this tree
6490     * @type Node
6491     */
6492    this.root = null;
6493    if(root){
6494        this.setRootNode(root);
6495    }
6496    this.addEvents({
6497        /**
6498         * @event append
6499         * Fires when a new child node is appended to a node in this tree.
6500         * @param {Tree} tree The owner tree
6501         * @param {Node} parent The parent node
6502         * @param {Node} node The newly appended node
6503         * @param {Number} index The index of the newly appended node
6504         */
6505        "append" : true,
6506        /**
6507         * @event remove
6508         * Fires when a child node is removed from a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The child node removed
6512         */
6513        "remove" : true,
6514        /**
6515         * @event move
6516         * Fires when a node is moved to a new location in the tree
6517         * @param {Tree} tree The owner tree
6518         * @param {Node} node The node moved
6519         * @param {Node} oldParent The old parent of this node
6520         * @param {Node} newParent The new parent of this node
6521         * @param {Number} index The index it was moved to
6522         */
6523        "move" : true,
6524        /**
6525         * @event insert
6526         * Fires when a new child node is inserted in a node in this tree.
6527         * @param {Tree} tree The owner tree
6528         * @param {Node} parent The parent node
6529         * @param {Node} node The child node inserted
6530         * @param {Node} refNode The child node the node was inserted before
6531         */
6532        "insert" : true,
6533        /**
6534         * @event beforeappend
6535         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6536         * @param {Tree} tree The owner tree
6537         * @param {Node} parent The parent node
6538         * @param {Node} node The child node to be appended
6539         */
6540        "beforeappend" : true,
6541        /**
6542         * @event beforeremove
6543         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6544         * @param {Tree} tree The owner tree
6545         * @param {Node} parent The parent node
6546         * @param {Node} node The child node to be removed
6547         */
6548        "beforeremove" : true,
6549        /**
6550         * @event beforemove
6551         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6552         * @param {Tree} tree The owner tree
6553         * @param {Node} node The node being moved
6554         * @param {Node} oldParent The parent of the node
6555         * @param {Node} newParent The new parent the node is moving to
6556         * @param {Number} index The index it is being moved to
6557         */
6558        "beforemove" : true,
6559        /**
6560         * @event beforeinsert
6561         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6562         * @param {Tree} tree The owner tree
6563         * @param {Node} parent The parent node
6564         * @param {Node} node The child node to be inserted
6565         * @param {Node} refNode The child node the node is being inserted before
6566         */
6567        "beforeinsert" : true
6568    });
6569
6570     Roo.data.Tree.superclass.constructor.call(this);
6571 };
6572
6573 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6574     pathSeparator: "/",
6575
6576     proxyNodeEvent : function(){
6577         return this.fireEvent.apply(this, arguments);
6578     },
6579
6580     /**
6581      * Returns the root node for this tree.
6582      * @return {Node}
6583      */
6584     getRootNode : function(){
6585         return this.root;
6586     },
6587
6588     /**
6589      * Sets the root node for this tree.
6590      * @param {Node} node
6591      * @return {Node}
6592      */
6593     setRootNode : function(node){
6594         this.root = node;
6595         node.ownerTree = this;
6596         node.isRoot = true;
6597         this.registerNode(node);
6598         return node;
6599     },
6600
6601     /**
6602      * Gets a node in this tree by its id.
6603      * @param {String} id
6604      * @return {Node}
6605      */
6606     getNodeById : function(id){
6607         return this.nodeHash[id];
6608     },
6609
6610     registerNode : function(node){
6611         this.nodeHash[node.id] = node;
6612     },
6613
6614     unregisterNode : function(node){
6615         delete this.nodeHash[node.id];
6616     },
6617
6618     toString : function(){
6619         return "[Tree"+(this.id?" "+this.id:"")+"]";
6620     }
6621 });
6622
6623 /**
6624  * @class Roo.data.Node
6625  * @extends Roo.util.Observable
6626  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6627  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6628  * @constructor
6629  * @param {Object} attributes The attributes/config for the node
6630  */
6631 Roo.data.Node = function(attributes){
6632     /**
6633      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6634      * @type {Object}
6635      */
6636     this.attributes = attributes || {};
6637     this.leaf = this.attributes.leaf;
6638     /**
6639      * The node id. @type String
6640      */
6641     this.id = this.attributes.id;
6642     if(!this.id){
6643         this.id = Roo.id(null, "ynode-");
6644         this.attributes.id = this.id;
6645     }
6646     /**
6647      * All child nodes of this node. @type Array
6648      */
6649     this.childNodes = [];
6650     if(!this.childNodes.indexOf){ // indexOf is a must
6651         this.childNodes.indexOf = function(o){
6652             for(var i = 0, len = this.length; i < len; i++){
6653                 if(this[i] == o) {
6654                     return i;
6655                 }
6656             }
6657             return -1;
6658         };
6659     }
6660     /**
6661      * The parent node for this node. @type Node
6662      */
6663     this.parentNode = null;
6664     /**
6665      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6666      */
6667     this.firstChild = null;
6668     /**
6669      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6670      */
6671     this.lastChild = null;
6672     /**
6673      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6674      */
6675     this.previousSibling = null;
6676     /**
6677      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6678      */
6679     this.nextSibling = null;
6680
6681     this.addEvents({
6682        /**
6683         * @event append
6684         * Fires when a new child node is appended
6685         * @param {Tree} tree The owner tree
6686         * @param {Node} this This node
6687         * @param {Node} node The newly appended node
6688         * @param {Number} index The index of the newly appended node
6689         */
6690        "append" : true,
6691        /**
6692         * @event remove
6693         * Fires when a child node is removed
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The removed node
6697         */
6698        "remove" : true,
6699        /**
6700         * @event move
6701         * Fires when this node is moved to a new location in the tree
6702         * @param {Tree} tree The owner tree
6703         * @param {Node} this This node
6704         * @param {Node} oldParent The old parent of this node
6705         * @param {Node} newParent The new parent of this node
6706         * @param {Number} index The index it was moved to
6707         */
6708        "move" : true,
6709        /**
6710         * @event insert
6711         * Fires when a new child node is inserted.
6712         * @param {Tree} tree The owner tree
6713         * @param {Node} this This node
6714         * @param {Node} node The child node inserted
6715         * @param {Node} refNode The child node the node was inserted before
6716         */
6717        "insert" : true,
6718        /**
6719         * @event beforeappend
6720         * Fires before a new child is appended, return false to cancel the append.
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The child node to be appended
6724         */
6725        "beforeappend" : true,
6726        /**
6727         * @event beforeremove
6728         * Fires before a child is removed, return false to cancel the remove.
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The child node to be removed
6732         */
6733        "beforeremove" : true,
6734        /**
6735         * @event beforemove
6736         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6737         * @param {Tree} tree The owner tree
6738         * @param {Node} this This node
6739         * @param {Node} oldParent The parent of this node
6740         * @param {Node} newParent The new parent this node is moving to
6741         * @param {Number} index The index it is being moved to
6742         */
6743        "beforemove" : true,
6744        /**
6745         * @event beforeinsert
6746         * Fires before a new child is inserted, return false to cancel the insert.
6747         * @param {Tree} tree The owner tree
6748         * @param {Node} this This node
6749         * @param {Node} node The child node to be inserted
6750         * @param {Node} refNode The child node the node is being inserted before
6751         */
6752        "beforeinsert" : true
6753    });
6754     this.listeners = this.attributes.listeners;
6755     Roo.data.Node.superclass.constructor.call(this);
6756 };
6757
6758 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6759     fireEvent : function(evtName){
6760         // first do standard event for this node
6761         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6762             return false;
6763         }
6764         // then bubble it up to the tree if the event wasn't cancelled
6765         var ot = this.getOwnerTree();
6766         if(ot){
6767             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6768                 return false;
6769             }
6770         }
6771         return true;
6772     },
6773
6774     /**
6775      * Returns true if this node is a leaf
6776      * @return {Boolean}
6777      */
6778     isLeaf : function(){
6779         return this.leaf === true;
6780     },
6781
6782     // private
6783     setFirstChild : function(node){
6784         this.firstChild = node;
6785     },
6786
6787     //private
6788     setLastChild : function(node){
6789         this.lastChild = node;
6790     },
6791
6792
6793     /**
6794      * Returns true if this node is the last child of its parent
6795      * @return {Boolean}
6796      */
6797     isLast : function(){
6798        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6799     },
6800
6801     /**
6802      * Returns true if this node is the first child of its parent
6803      * @return {Boolean}
6804      */
6805     isFirst : function(){
6806        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6807     },
6808
6809     hasChildNodes : function(){
6810         return !this.isLeaf() && this.childNodes.length > 0;
6811     },
6812
6813     /**
6814      * Insert node(s) as the last child node of this node.
6815      * @param {Node/Array} node The node or Array of nodes to append
6816      * @return {Node} The appended node if single append, or null if an array was passed
6817      */
6818     appendChild : function(node){
6819         var multi = false;
6820         if(node instanceof Array){
6821             multi = node;
6822         }else if(arguments.length > 1){
6823             multi = arguments;
6824         }
6825         // if passed an array or multiple args do them one by one
6826         if(multi){
6827             for(var i = 0, len = multi.length; i < len; i++) {
6828                 this.appendChild(multi[i]);
6829             }
6830         }else{
6831             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6832                 return false;
6833             }
6834             var index = this.childNodes.length;
6835             var oldParent = node.parentNode;
6836             // it's a move, make sure we move it cleanly
6837             if(oldParent){
6838                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6839                     return false;
6840                 }
6841                 oldParent.removeChild(node);
6842             }
6843             index = this.childNodes.length;
6844             if(index == 0){
6845                 this.setFirstChild(node);
6846             }
6847             this.childNodes.push(node);
6848             node.parentNode = this;
6849             var ps = this.childNodes[index-1];
6850             if(ps){
6851                 node.previousSibling = ps;
6852                 ps.nextSibling = node;
6853             }else{
6854                 node.previousSibling = null;
6855             }
6856             node.nextSibling = null;
6857             this.setLastChild(node);
6858             node.setOwnerTree(this.getOwnerTree());
6859             this.fireEvent("append", this.ownerTree, this, node, index);
6860             if(oldParent){
6861                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6862             }
6863             return node;
6864         }
6865     },
6866
6867     /**
6868      * Removes a child node from this node.
6869      * @param {Node} node The node to remove
6870      * @return {Node} The removed node
6871      */
6872     removeChild : function(node){
6873         var index = this.childNodes.indexOf(node);
6874         if(index == -1){
6875             return false;
6876         }
6877         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6878             return false;
6879         }
6880
6881         // remove it from childNodes collection
6882         this.childNodes.splice(index, 1);
6883
6884         // update siblings
6885         if(node.previousSibling){
6886             node.previousSibling.nextSibling = node.nextSibling;
6887         }
6888         if(node.nextSibling){
6889             node.nextSibling.previousSibling = node.previousSibling;
6890         }
6891
6892         // update child refs
6893         if(this.firstChild == node){
6894             this.setFirstChild(node.nextSibling);
6895         }
6896         if(this.lastChild == node){
6897             this.setLastChild(node.previousSibling);
6898         }
6899
6900         node.setOwnerTree(null);
6901         // clear any references from the node
6902         node.parentNode = null;
6903         node.previousSibling = null;
6904         node.nextSibling = null;
6905         this.fireEvent("remove", this.ownerTree, this, node);
6906         return node;
6907     },
6908
6909     /**
6910      * Inserts the first node before the second node in this nodes childNodes collection.
6911      * @param {Node} node The node to insert
6912      * @param {Node} refNode The node to insert before (if null the node is appended)
6913      * @return {Node} The inserted node
6914      */
6915     insertBefore : function(node, refNode){
6916         if(!refNode){ // like standard Dom, refNode can be null for append
6917             return this.appendChild(node);
6918         }
6919         // nothing to do
6920         if(node == refNode){
6921             return false;
6922         }
6923
6924         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6925             return false;
6926         }
6927         var index = this.childNodes.indexOf(refNode);
6928         var oldParent = node.parentNode;
6929         var refIndex = index;
6930
6931         // when moving internally, indexes will change after remove
6932         if(oldParent == this && this.childNodes.indexOf(node) < index){
6933             refIndex--;
6934         }
6935
6936         // it's a move, make sure we move it cleanly
6937         if(oldParent){
6938             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6939                 return false;
6940             }
6941             oldParent.removeChild(node);
6942         }
6943         if(refIndex == 0){
6944             this.setFirstChild(node);
6945         }
6946         this.childNodes.splice(refIndex, 0, node);
6947         node.parentNode = this;
6948         var ps = this.childNodes[refIndex-1];
6949         if(ps){
6950             node.previousSibling = ps;
6951             ps.nextSibling = node;
6952         }else{
6953             node.previousSibling = null;
6954         }
6955         node.nextSibling = refNode;
6956         refNode.previousSibling = node;
6957         node.setOwnerTree(this.getOwnerTree());
6958         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6959         if(oldParent){
6960             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6961         }
6962         return node;
6963     },
6964
6965     /**
6966      * Returns the child node at the specified index.
6967      * @param {Number} index
6968      * @return {Node}
6969      */
6970     item : function(index){
6971         return this.childNodes[index];
6972     },
6973
6974     /**
6975      * Replaces one child node in this node with another.
6976      * @param {Node} newChild The replacement node
6977      * @param {Node} oldChild The node to replace
6978      * @return {Node} The replaced node
6979      */
6980     replaceChild : function(newChild, oldChild){
6981         this.insertBefore(newChild, oldChild);
6982         this.removeChild(oldChild);
6983         return oldChild;
6984     },
6985
6986     /**
6987      * Returns the index of a child node
6988      * @param {Node} node
6989      * @return {Number} The index of the node or -1 if it was not found
6990      */
6991     indexOf : function(child){
6992         return this.childNodes.indexOf(child);
6993     },
6994
6995     /**
6996      * Returns the tree this node is in.
6997      * @return {Tree}
6998      */
6999     getOwnerTree : function(){
7000         // if it doesn't have one, look for one
7001         if(!this.ownerTree){
7002             var p = this;
7003             while(p){
7004                 if(p.ownerTree){
7005                     this.ownerTree = p.ownerTree;
7006                     break;
7007                 }
7008                 p = p.parentNode;
7009             }
7010         }
7011         return this.ownerTree;
7012     },
7013
7014     /**
7015      * Returns depth of this node (the root node has a depth of 0)
7016      * @return {Number}
7017      */
7018     getDepth : function(){
7019         var depth = 0;
7020         var p = this;
7021         while(p.parentNode){
7022             ++depth;
7023             p = p.parentNode;
7024         }
7025         return depth;
7026     },
7027
7028     // private
7029     setOwnerTree : function(tree){
7030         // if it's move, we need to update everyone
7031         if(tree != this.ownerTree){
7032             if(this.ownerTree){
7033                 this.ownerTree.unregisterNode(this);
7034             }
7035             this.ownerTree = tree;
7036             var cs = this.childNodes;
7037             for(var i = 0, len = cs.length; i < len; i++) {
7038                 cs[i].setOwnerTree(tree);
7039             }
7040             if(tree){
7041                 tree.registerNode(this);
7042             }
7043         }
7044     },
7045
7046     /**
7047      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7048      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7049      * @return {String} The path
7050      */
7051     getPath : function(attr){
7052         attr = attr || "id";
7053         var p = this.parentNode;
7054         var b = [this.attributes[attr]];
7055         while(p){
7056             b.unshift(p.attributes[attr]);
7057             p = p.parentNode;
7058         }
7059         var sep = this.getOwnerTree().pathSeparator;
7060         return sep + b.join(sep);
7061     },
7062
7063     /**
7064      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7065      * function call will be the scope provided or the current node. The arguments to the function
7066      * will be the args provided or the current node. If the function returns false at any point,
7067      * the bubble is stopped.
7068      * @param {Function} fn The function to call
7069      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7070      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7071      */
7072     bubble : function(fn, scope, args){
7073         var p = this;
7074         while(p){
7075             if(fn.call(scope || p, args || p) === false){
7076                 break;
7077             }
7078             p = p.parentNode;
7079         }
7080     },
7081
7082     /**
7083      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7084      * function call will be the scope provided or the current node. The arguments to the function
7085      * will be the args provided or the current node. If the function returns false at any point,
7086      * the cascade is stopped on that branch.
7087      * @param {Function} fn The function to call
7088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7090      */
7091     cascade : function(fn, scope, args){
7092         if(fn.call(scope || this, args || this) !== false){
7093             var cs = this.childNodes;
7094             for(var i = 0, len = cs.length; i < len; i++) {
7095                 cs[i].cascade(fn, scope, args);
7096             }
7097         }
7098     },
7099
7100     /**
7101      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7102      * function call will be the scope provided or the current node. The arguments to the function
7103      * will be the args provided or the current node. If the function returns false at any point,
7104      * the iteration stops.
7105      * @param {Function} fn The function to call
7106      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7107      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7108      */
7109     eachChild : function(fn, scope, args){
7110         var cs = this.childNodes;
7111         for(var i = 0, len = cs.length; i < len; i++) {
7112                 if(fn.call(scope || this, args || cs[i]) === false){
7113                     break;
7114                 }
7115         }
7116     },
7117
7118     /**
7119      * Finds the first child that has the attribute with the specified value.
7120      * @param {String} attribute The attribute name
7121      * @param {Mixed} value The value to search for
7122      * @return {Node} The found child or null if none was found
7123      */
7124     findChild : function(attribute, value){
7125         var cs = this.childNodes;
7126         for(var i = 0, len = cs.length; i < len; i++) {
7127                 if(cs[i].attributes[attribute] == value){
7128                     return cs[i];
7129                 }
7130         }
7131         return null;
7132     },
7133
7134     /**
7135      * Finds the first child by a custom function. The child matches if the function passed
7136      * returns true.
7137      * @param {Function} fn
7138      * @param {Object} scope (optional)
7139      * @return {Node} The found child or null if none was found
7140      */
7141     findChildBy : function(fn, scope){
7142         var cs = this.childNodes;
7143         for(var i = 0, len = cs.length; i < len; i++) {
7144                 if(fn.call(scope||cs[i], cs[i]) === true){
7145                     return cs[i];
7146                 }
7147         }
7148         return null;
7149     },
7150
7151     /**
7152      * Sorts this nodes children using the supplied sort function
7153      * @param {Function} fn
7154      * @param {Object} scope (optional)
7155      */
7156     sort : function(fn, scope){
7157         var cs = this.childNodes;
7158         var len = cs.length;
7159         if(len > 0){
7160             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7161             cs.sort(sortFn);
7162             for(var i = 0; i < len; i++){
7163                 var n = cs[i];
7164                 n.previousSibling = cs[i-1];
7165                 n.nextSibling = cs[i+1];
7166                 if(i == 0){
7167                     this.setFirstChild(n);
7168                 }
7169                 if(i == len-1){
7170                     this.setLastChild(n);
7171                 }
7172             }
7173         }
7174     },
7175
7176     /**
7177      * Returns true if this node is an ancestor (at any point) of the passed node.
7178      * @param {Node} node
7179      * @return {Boolean}
7180      */
7181     contains : function(node){
7182         return node.isAncestor(this);
7183     },
7184
7185     /**
7186      * Returns true if the passed node is an ancestor (at any point) of this node.
7187      * @param {Node} node
7188      * @return {Boolean}
7189      */
7190     isAncestor : function(node){
7191         var p = this.parentNode;
7192         while(p){
7193             if(p == node){
7194                 return true;
7195             }
7196             p = p.parentNode;
7197         }
7198         return false;
7199     },
7200
7201     toString : function(){
7202         return "[Node"+(this.id?" "+this.id:"")+"]";
7203     }
7204 });/*
7205  * Based on:
7206  * Ext JS Library 1.1.1
7207  * Copyright(c) 2006-2007, Ext JS, LLC.
7208  *
7209  * Originally Released Under LGPL - original licence link has changed is not relivant.
7210  *
7211  * Fork - LGPL
7212  * <script type="text/javascript">
7213  */
7214  
7215
7216 /**
7217  * @class Roo.ComponentMgr
7218  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7219  * @singleton
7220  */
7221 Roo.ComponentMgr = function(){
7222     var all = new Roo.util.MixedCollection();
7223
7224     return {
7225         /**
7226          * Registers a component.
7227          * @param {Roo.Component} c The component
7228          */
7229         register : function(c){
7230             all.add(c);
7231         },
7232
7233         /**
7234          * Unregisters a component.
7235          * @param {Roo.Component} c The component
7236          */
7237         unregister : function(c){
7238             all.remove(c);
7239         },
7240
7241         /**
7242          * Returns a component by id
7243          * @param {String} id The component id
7244          */
7245         get : function(id){
7246             return all.get(id);
7247         },
7248
7249         /**
7250          * Registers a function that will be called when a specified component is added to ComponentMgr
7251          * @param {String} id The component id
7252          * @param {Funtction} fn The callback function
7253          * @param {Object} scope The scope of the callback
7254          */
7255         onAvailable : function(id, fn, scope){
7256             all.on("add", function(index, o){
7257                 if(o.id == id){
7258                     fn.call(scope || o, o);
7259                     all.un("add", fn, scope);
7260                 }
7261             });
7262         }
7263     };
7264 }();/*
7265  * Based on:
7266  * Ext JS Library 1.1.1
7267  * Copyright(c) 2006-2007, Ext JS, LLC.
7268  *
7269  * Originally Released Under LGPL - original licence link has changed is not relivant.
7270  *
7271  * Fork - LGPL
7272  * <script type="text/javascript">
7273  */
7274  
7275 /**
7276  * @class Roo.Component
7277  * @extends Roo.util.Observable
7278  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7279  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7280  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7281  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7282  * All visual components (widgets) that require rendering into a layout should subclass Component.
7283  * @constructor
7284  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7285  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7286  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7287  */
7288 Roo.Component = function(config){
7289     config = config || {};
7290     if(config.tagName || config.dom || typeof config == "string"){ // element object
7291         config = {el: config, id: config.id || config};
7292     }
7293     this.initialConfig = config;
7294
7295     Roo.apply(this, config);
7296     this.addEvents({
7297         /**
7298          * @event disable
7299          * Fires after the component is disabled.
7300              * @param {Roo.Component} this
7301              */
7302         disable : true,
7303         /**
7304          * @event enable
7305          * Fires after the component is enabled.
7306              * @param {Roo.Component} this
7307              */
7308         enable : true,
7309         /**
7310          * @event beforeshow
7311          * Fires before the component is shown.  Return false to stop the show.
7312              * @param {Roo.Component} this
7313              */
7314         beforeshow : true,
7315         /**
7316          * @event show
7317          * Fires after the component is shown.
7318              * @param {Roo.Component} this
7319              */
7320         show : true,
7321         /**
7322          * @event beforehide
7323          * Fires before the component is hidden. Return false to stop the hide.
7324              * @param {Roo.Component} this
7325              */
7326         beforehide : true,
7327         /**
7328          * @event hide
7329          * Fires after the component is hidden.
7330              * @param {Roo.Component} this
7331              */
7332         hide : true,
7333         /**
7334          * @event beforerender
7335          * Fires before the component is rendered. Return false to stop the render.
7336              * @param {Roo.Component} this
7337              */
7338         beforerender : true,
7339         /**
7340          * @event render
7341          * Fires after the component is rendered.
7342              * @param {Roo.Component} this
7343              */
7344         render : true,
7345         /**
7346          * @event beforedestroy
7347          * Fires before the component is destroyed. Return false to stop the destroy.
7348              * @param {Roo.Component} this
7349              */
7350         beforedestroy : true,
7351         /**
7352          * @event destroy
7353          * Fires after the component is destroyed.
7354              * @param {Roo.Component} this
7355              */
7356         destroy : true
7357     });
7358     if(!this.id){
7359         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7360     }
7361     Roo.ComponentMgr.register(this);
7362     Roo.Component.superclass.constructor.call(this);
7363     this.initComponent();
7364     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7365         this.render(this.renderTo);
7366         delete this.renderTo;
7367     }
7368 };
7369
7370 // private
7371 Roo.Component.AUTO_ID = 1000;
7372
7373 Roo.extend(Roo.Component, Roo.util.Observable, {
7374     /**
7375      * @property {Boolean} hidden
7376      * true if this component is hidden. Read-only.
7377      */
7378     hidden : false,
7379     /**
7380      * true if this component is disabled. Read-only.
7381      */
7382     disabled : false,
7383     /**
7384      * true if this component has been rendered. Read-only.
7385      */
7386     rendered : false,
7387     
7388     /** @cfg {String} disableClass
7389      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7390      */
7391     disabledClass : "x-item-disabled",
7392         /** @cfg {Boolean} allowDomMove
7393          * Whether the component can move the Dom node when rendering (defaults to true).
7394          */
7395     allowDomMove : true,
7396     /** @cfg {String} hideMode
7397      * How this component should hidden. Supported values are
7398      * "visibility" (css visibility), "offsets" (negative offset position) and
7399      * "display" (css display) - defaults to "display".
7400      */
7401     hideMode: 'display',
7402
7403     // private
7404     ctype : "Roo.Component",
7405
7406     /** @cfg {String} actionMode 
7407      * which property holds the element that used for  hide() / show() / disable() / enable()
7408      * default is 'el' 
7409      */
7410     actionMode : "el",
7411
7412     // private
7413     getActionEl : function(){
7414         return this[this.actionMode];
7415     },
7416
7417     initComponent : Roo.emptyFn,
7418     /**
7419      * If this is a lazy rendering component, render it to its container element.
7420      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7421      */
7422     render : function(container, position){
7423         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7424             if(!container && this.el){
7425                 this.el = Roo.get(this.el);
7426                 container = this.el.dom.parentNode;
7427                 this.allowDomMove = false;
7428             }
7429             this.container = Roo.get(container);
7430             this.rendered = true;
7431             if(position !== undefined){
7432                 if(typeof position == 'number'){
7433                     position = this.container.dom.childNodes[position];
7434                 }else{
7435                     position = Roo.getDom(position);
7436                 }
7437             }
7438             this.onRender(this.container, position || null);
7439             if(this.cls){
7440                 this.el.addClass(this.cls);
7441                 delete this.cls;
7442             }
7443             if(this.style){
7444                 this.el.applyStyles(this.style);
7445                 delete this.style;
7446             }
7447             this.fireEvent("render", this);
7448             this.afterRender(this.container);
7449             if(this.hidden){
7450                 this.hide();
7451             }
7452             if(this.disabled){
7453                 this.disable();
7454             }
7455         }
7456         return this;
7457     },
7458
7459     // private
7460     // default function is not really useful
7461     onRender : function(ct, position){
7462         if(this.el){
7463             this.el = Roo.get(this.el);
7464             if(this.allowDomMove !== false){
7465                 ct.dom.insertBefore(this.el.dom, position);
7466             }
7467         }
7468     },
7469
7470     // private
7471     getAutoCreate : function(){
7472         var cfg = typeof this.autoCreate == "object" ?
7473                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7474         if(this.id && !cfg.id){
7475             cfg.id = this.id;
7476         }
7477         return cfg;
7478     },
7479
7480     // private
7481     afterRender : Roo.emptyFn,
7482
7483     /**
7484      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7485      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7486      */
7487     destroy : function(){
7488         if(this.fireEvent("beforedestroy", this) !== false){
7489             this.purgeListeners();
7490             this.beforeDestroy();
7491             if(this.rendered){
7492                 this.el.removeAllListeners();
7493                 this.el.remove();
7494                 if(this.actionMode == "container"){
7495                     this.container.remove();
7496                 }
7497             }
7498             this.onDestroy();
7499             Roo.ComponentMgr.unregister(this);
7500             this.fireEvent("destroy", this);
7501         }
7502     },
7503
7504         // private
7505     beforeDestroy : function(){
7506
7507     },
7508
7509         // private
7510         onDestroy : function(){
7511
7512     },
7513
7514     /**
7515      * Returns the underlying {@link Roo.Element}.
7516      * @return {Roo.Element} The element
7517      */
7518     getEl : function(){
7519         return this.el;
7520     },
7521
7522     /**
7523      * Returns the id of this component.
7524      * @return {String}
7525      */
7526     getId : function(){
7527         return this.id;
7528     },
7529
7530     /**
7531      * Try to focus this component.
7532      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7533      * @return {Roo.Component} this
7534      */
7535     focus : function(selectText){
7536         if(this.rendered){
7537             this.el.focus();
7538             if(selectText === true){
7539                 this.el.dom.select();
7540             }
7541         }
7542         return this;
7543     },
7544
7545     // private
7546     blur : function(){
7547         if(this.rendered){
7548             this.el.blur();
7549         }
7550         return this;
7551     },
7552
7553     /**
7554      * Disable this component.
7555      * @return {Roo.Component} this
7556      */
7557     disable : function(){
7558         if(this.rendered){
7559             this.onDisable();
7560         }
7561         this.disabled = true;
7562         this.fireEvent("disable", this);
7563         return this;
7564     },
7565
7566         // private
7567     onDisable : function(){
7568         this.getActionEl().addClass(this.disabledClass);
7569         this.el.dom.disabled = true;
7570     },
7571
7572     /**
7573      * Enable this component.
7574      * @return {Roo.Component} this
7575      */
7576     enable : function(){
7577         if(this.rendered){
7578             this.onEnable();
7579         }
7580         this.disabled = false;
7581         this.fireEvent("enable", this);
7582         return this;
7583     },
7584
7585         // private
7586     onEnable : function(){
7587         this.getActionEl().removeClass(this.disabledClass);
7588         this.el.dom.disabled = false;
7589     },
7590
7591     /**
7592      * Convenience function for setting disabled/enabled by boolean.
7593      * @param {Boolean} disabled
7594      */
7595     setDisabled : function(disabled){
7596         this[disabled ? "disable" : "enable"]();
7597     },
7598
7599     /**
7600      * Show this component.
7601      * @return {Roo.Component} this
7602      */
7603     show: function(){
7604         if(this.fireEvent("beforeshow", this) !== false){
7605             this.hidden = false;
7606             if(this.rendered){
7607                 this.onShow();
7608             }
7609             this.fireEvent("show", this);
7610         }
7611         return this;
7612     },
7613
7614     // private
7615     onShow : function(){
7616         var ae = this.getActionEl();
7617         if(this.hideMode == 'visibility'){
7618             ae.dom.style.visibility = "visible";
7619         }else if(this.hideMode == 'offsets'){
7620             ae.removeClass('x-hidden');
7621         }else{
7622             ae.dom.style.display = "";
7623         }
7624     },
7625
7626     /**
7627      * Hide this component.
7628      * @return {Roo.Component} this
7629      */
7630     hide: function(){
7631         if(this.fireEvent("beforehide", this) !== false){
7632             this.hidden = true;
7633             if(this.rendered){
7634                 this.onHide();
7635             }
7636             this.fireEvent("hide", this);
7637         }
7638         return this;
7639     },
7640
7641     // private
7642     onHide : function(){
7643         var ae = this.getActionEl();
7644         if(this.hideMode == 'visibility'){
7645             ae.dom.style.visibility = "hidden";
7646         }else if(this.hideMode == 'offsets'){
7647             ae.addClass('x-hidden');
7648         }else{
7649             ae.dom.style.display = "none";
7650         }
7651     },
7652
7653     /**
7654      * Convenience function to hide or show this component by boolean.
7655      * @param {Boolean} visible True to show, false to hide
7656      * @return {Roo.Component} this
7657      */
7658     setVisible: function(visible){
7659         if(visible) {
7660             this.show();
7661         }else{
7662             this.hide();
7663         }
7664         return this;
7665     },
7666
7667     /**
7668      * Returns true if this component is visible.
7669      */
7670     isVisible : function(){
7671         return this.getActionEl().isVisible();
7672     },
7673
7674     cloneConfig : function(overrides){
7675         overrides = overrides || {};
7676         var id = overrides.id || Roo.id();
7677         var cfg = Roo.applyIf(overrides, this.initialConfig);
7678         cfg.id = id; // prevent dup id
7679         return new this.constructor(cfg);
7680     }
7681 });/*
7682  * Based on:
7683  * Ext JS Library 1.1.1
7684  * Copyright(c) 2006-2007, Ext JS, LLC.
7685  *
7686  * Originally Released Under LGPL - original licence link has changed is not relivant.
7687  *
7688  * Fork - LGPL
7689  * <script type="text/javascript">
7690  */
7691  (function(){ 
7692 /**
7693  * @class Roo.Layer
7694  * @extends Roo.Element
7695  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7696  * automatic maintaining of shadow/shim positions.
7697  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7698  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7699  * you can pass a string with a CSS class name. False turns off the shadow.
7700  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7701  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7702  * @cfg {String} cls CSS class to add to the element
7703  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7704  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7705  * @constructor
7706  * @param {Object} config An object with config options.
7707  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7708  */
7709
7710 Roo.Layer = function(config, existingEl){
7711     config = config || {};
7712     var dh = Roo.DomHelper;
7713     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7714     if(existingEl){
7715         this.dom = Roo.getDom(existingEl);
7716     }
7717     if(!this.dom){
7718         var o = config.dh || {tag: "div", cls: "x-layer"};
7719         this.dom = dh.append(pel, o);
7720     }
7721     if(config.cls){
7722         this.addClass(config.cls);
7723     }
7724     this.constrain = config.constrain !== false;
7725     this.visibilityMode = Roo.Element.VISIBILITY;
7726     if(config.id){
7727         this.id = this.dom.id = config.id;
7728     }else{
7729         this.id = Roo.id(this.dom);
7730     }
7731     this.zindex = config.zindex || this.getZIndex();
7732     this.position("absolute", this.zindex);
7733     if(config.shadow){
7734         this.shadowOffset = config.shadowOffset || 4;
7735         this.shadow = new Roo.Shadow({
7736             offset : this.shadowOffset,
7737             mode : config.shadow
7738         });
7739     }else{
7740         this.shadowOffset = 0;
7741     }
7742     this.useShim = config.shim !== false && Roo.useShims;
7743     this.useDisplay = config.useDisplay;
7744     this.hide();
7745 };
7746
7747 var supr = Roo.Element.prototype;
7748
7749 // shims are shared among layer to keep from having 100 iframes
7750 var shims = [];
7751
7752 Roo.extend(Roo.Layer, Roo.Element, {
7753
7754     getZIndex : function(){
7755         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7756     },
7757
7758     getShim : function(){
7759         if(!this.useShim){
7760             return null;
7761         }
7762         if(this.shim){
7763             return this.shim;
7764         }
7765         var shim = shims.shift();
7766         if(!shim){
7767             shim = this.createShim();
7768             shim.enableDisplayMode('block');
7769             shim.dom.style.display = 'none';
7770             shim.dom.style.visibility = 'visible';
7771         }
7772         var pn = this.dom.parentNode;
7773         if(shim.dom.parentNode != pn){
7774             pn.insertBefore(shim.dom, this.dom);
7775         }
7776         shim.setStyle('z-index', this.getZIndex()-2);
7777         this.shim = shim;
7778         return shim;
7779     },
7780
7781     hideShim : function(){
7782         if(this.shim){
7783             this.shim.setDisplayed(false);
7784             shims.push(this.shim);
7785             delete this.shim;
7786         }
7787     },
7788
7789     disableShadow : function(){
7790         if(this.shadow){
7791             this.shadowDisabled = true;
7792             this.shadow.hide();
7793             this.lastShadowOffset = this.shadowOffset;
7794             this.shadowOffset = 0;
7795         }
7796     },
7797
7798     enableShadow : function(show){
7799         if(this.shadow){
7800             this.shadowDisabled = false;
7801             this.shadowOffset = this.lastShadowOffset;
7802             delete this.lastShadowOffset;
7803             if(show){
7804                 this.sync(true);
7805             }
7806         }
7807     },
7808
7809     // private
7810     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7811     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7812     sync : function(doShow){
7813         var sw = this.shadow;
7814         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7815             var sh = this.getShim();
7816
7817             var w = this.getWidth(),
7818                 h = this.getHeight();
7819
7820             var l = this.getLeft(true),
7821                 t = this.getTop(true);
7822
7823             if(sw && !this.shadowDisabled){
7824                 if(doShow && !sw.isVisible()){
7825                     sw.show(this);
7826                 }else{
7827                     sw.realign(l, t, w, h);
7828                 }
7829                 if(sh){
7830                     if(doShow){
7831                        sh.show();
7832                     }
7833                     // fit the shim behind the shadow, so it is shimmed too
7834                     var a = sw.adjusts, s = sh.dom.style;
7835                     s.left = (Math.min(l, l+a.l))+"px";
7836                     s.top = (Math.min(t, t+a.t))+"px";
7837                     s.width = (w+a.w)+"px";
7838                     s.height = (h+a.h)+"px";
7839                 }
7840             }else if(sh){
7841                 if(doShow){
7842                    sh.show();
7843                 }
7844                 sh.setSize(w, h);
7845                 sh.setLeftTop(l, t);
7846             }
7847             
7848         }
7849     },
7850
7851     // private
7852     destroy : function(){
7853         this.hideShim();
7854         if(this.shadow){
7855             this.shadow.hide();
7856         }
7857         this.removeAllListeners();
7858         var pn = this.dom.parentNode;
7859         if(pn){
7860             pn.removeChild(this.dom);
7861         }
7862         Roo.Element.uncache(this.id);
7863     },
7864
7865     remove : function(){
7866         this.destroy();
7867     },
7868
7869     // private
7870     beginUpdate : function(){
7871         this.updating = true;
7872     },
7873
7874     // private
7875     endUpdate : function(){
7876         this.updating = false;
7877         this.sync(true);
7878     },
7879
7880     // private
7881     hideUnders : function(negOffset){
7882         if(this.shadow){
7883             this.shadow.hide();
7884         }
7885         this.hideShim();
7886     },
7887
7888     // private
7889     constrainXY : function(){
7890         if(this.constrain){
7891             var vw = Roo.lib.Dom.getViewWidth(),
7892                 vh = Roo.lib.Dom.getViewHeight();
7893             var s = Roo.get(document).getScroll();
7894
7895             var xy = this.getXY();
7896             var x = xy[0], y = xy[1];   
7897             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7898             // only move it if it needs it
7899             var moved = false;
7900             // first validate right/bottom
7901             if((x + w) > vw+s.left){
7902                 x = vw - w - this.shadowOffset;
7903                 moved = true;
7904             }
7905             if((y + h) > vh+s.top){
7906                 y = vh - h - this.shadowOffset;
7907                 moved = true;
7908             }
7909             // then make sure top/left isn't negative
7910             if(x < s.left){
7911                 x = s.left;
7912                 moved = true;
7913             }
7914             if(y < s.top){
7915                 y = s.top;
7916                 moved = true;
7917             }
7918             if(moved){
7919                 if(this.avoidY){
7920                     var ay = this.avoidY;
7921                     if(y <= ay && (y+h) >= ay){
7922                         y = ay-h-5;   
7923                     }
7924                 }
7925                 xy = [x, y];
7926                 this.storeXY(xy);
7927                 supr.setXY.call(this, xy);
7928                 this.sync();
7929             }
7930         }
7931     },
7932
7933     isVisible : function(){
7934         return this.visible;    
7935     },
7936
7937     // private
7938     showAction : function(){
7939         this.visible = true; // track visibility to prevent getStyle calls
7940         if(this.useDisplay === true){
7941             this.setDisplayed("");
7942         }else if(this.lastXY){
7943             supr.setXY.call(this, this.lastXY);
7944         }else if(this.lastLT){
7945             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7946         }
7947     },
7948
7949     // private
7950     hideAction : function(){
7951         this.visible = false;
7952         if(this.useDisplay === true){
7953             this.setDisplayed(false);
7954         }else{
7955             this.setLeftTop(-10000,-10000);
7956         }
7957     },
7958
7959     // overridden Element method
7960     setVisible : function(v, a, d, c, e){
7961         if(v){
7962             this.showAction();
7963         }
7964         if(a && v){
7965             var cb = function(){
7966                 this.sync(true);
7967                 if(c){
7968                     c();
7969                 }
7970             }.createDelegate(this);
7971             supr.setVisible.call(this, true, true, d, cb, e);
7972         }else{
7973             if(!v){
7974                 this.hideUnders(true);
7975             }
7976             var cb = c;
7977             if(a){
7978                 cb = function(){
7979                     this.hideAction();
7980                     if(c){
7981                         c();
7982                     }
7983                 }.createDelegate(this);
7984             }
7985             supr.setVisible.call(this, v, a, d, cb, e);
7986             if(v){
7987                 this.sync(true);
7988             }else if(!a){
7989                 this.hideAction();
7990             }
7991         }
7992     },
7993
7994     storeXY : function(xy){
7995         delete this.lastLT;
7996         this.lastXY = xy;
7997     },
7998
7999     storeLeftTop : function(left, top){
8000         delete this.lastXY;
8001         this.lastLT = [left, top];
8002     },
8003
8004     // private
8005     beforeFx : function(){
8006         this.beforeAction();
8007         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8008     },
8009
8010     // private
8011     afterFx : function(){
8012         Roo.Layer.superclass.afterFx.apply(this, arguments);
8013         this.sync(this.isVisible());
8014     },
8015
8016     // private
8017     beforeAction : function(){
8018         if(!this.updating && this.shadow){
8019             this.shadow.hide();
8020         }
8021     },
8022
8023     // overridden Element method
8024     setLeft : function(left){
8025         this.storeLeftTop(left, this.getTop(true));
8026         supr.setLeft.apply(this, arguments);
8027         this.sync();
8028     },
8029
8030     setTop : function(top){
8031         this.storeLeftTop(this.getLeft(true), top);
8032         supr.setTop.apply(this, arguments);
8033         this.sync();
8034     },
8035
8036     setLeftTop : function(left, top){
8037         this.storeLeftTop(left, top);
8038         supr.setLeftTop.apply(this, arguments);
8039         this.sync();
8040     },
8041
8042     setXY : function(xy, a, d, c, e){
8043         this.fixDisplay();
8044         this.beforeAction();
8045         this.storeXY(xy);
8046         var cb = this.createCB(c);
8047         supr.setXY.call(this, xy, a, d, cb, e);
8048         if(!a){
8049             cb();
8050         }
8051     },
8052
8053     // private
8054     createCB : function(c){
8055         var el = this;
8056         return function(){
8057             el.constrainXY();
8058             el.sync(true);
8059             if(c){
8060                 c();
8061             }
8062         };
8063     },
8064
8065     // overridden Element method
8066     setX : function(x, a, d, c, e){
8067         this.setXY([x, this.getY()], a, d, c, e);
8068     },
8069
8070     // overridden Element method
8071     setY : function(y, a, d, c, e){
8072         this.setXY([this.getX(), y], a, d, c, e);
8073     },
8074
8075     // overridden Element method
8076     setSize : function(w, h, a, d, c, e){
8077         this.beforeAction();
8078         var cb = this.createCB(c);
8079         supr.setSize.call(this, w, h, a, d, cb, e);
8080         if(!a){
8081             cb();
8082         }
8083     },
8084
8085     // overridden Element method
8086     setWidth : function(w, a, d, c, e){
8087         this.beforeAction();
8088         var cb = this.createCB(c);
8089         supr.setWidth.call(this, w, a, d, cb, e);
8090         if(!a){
8091             cb();
8092         }
8093     },
8094
8095     // overridden Element method
8096     setHeight : function(h, a, d, c, e){
8097         this.beforeAction();
8098         var cb = this.createCB(c);
8099         supr.setHeight.call(this, h, a, d, cb, e);
8100         if(!a){
8101             cb();
8102         }
8103     },
8104
8105     // overridden Element method
8106     setBounds : function(x, y, w, h, a, d, c, e){
8107         this.beforeAction();
8108         var cb = this.createCB(c);
8109         if(!a){
8110             this.storeXY([x, y]);
8111             supr.setXY.call(this, [x, y]);
8112             supr.setSize.call(this, w, h, a, d, cb, e);
8113             cb();
8114         }else{
8115             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8116         }
8117         return this;
8118     },
8119     
8120     /**
8121      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8122      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8123      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8124      * @param {Number} zindex The new z-index to set
8125      * @return {this} The Layer
8126      */
8127     setZIndex : function(zindex){
8128         this.zindex = zindex;
8129         this.setStyle("z-index", zindex + 2);
8130         if(this.shadow){
8131             this.shadow.setZIndex(zindex + 1);
8132         }
8133         if(this.shim){
8134             this.shim.setStyle("z-index", zindex);
8135         }
8136     }
8137 });
8138 })();/*
8139  * Based on:
8140  * Ext JS Library 1.1.1
8141  * Copyright(c) 2006-2007, Ext JS, LLC.
8142  *
8143  * Originally Released Under LGPL - original licence link has changed is not relivant.
8144  *
8145  * Fork - LGPL
8146  * <script type="text/javascript">
8147  */
8148
8149
8150 /**
8151  * @class Roo.Shadow
8152  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8153  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8154  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8155  * @constructor
8156  * Create a new Shadow
8157  * @param {Object} config The config object
8158  */
8159 Roo.Shadow = function(config){
8160     Roo.apply(this, config);
8161     if(typeof this.mode != "string"){
8162         this.mode = this.defaultMode;
8163     }
8164     var o = this.offset, a = {h: 0};
8165     var rad = Math.floor(this.offset/2);
8166     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8167         case "drop":
8168             a.w = 0;
8169             a.l = a.t = o;
8170             a.t -= 1;
8171             if(Roo.isIE){
8172                 a.l -= this.offset + rad;
8173                 a.t -= this.offset + rad;
8174                 a.w -= rad;
8175                 a.h -= rad;
8176                 a.t += 1;
8177             }
8178         break;
8179         case "sides":
8180             a.w = (o*2);
8181             a.l = -o;
8182             a.t = o-1;
8183             if(Roo.isIE){
8184                 a.l -= (this.offset - rad);
8185                 a.t -= this.offset + rad;
8186                 a.l += 1;
8187                 a.w -= (this.offset - rad)*2;
8188                 a.w -= rad + 1;
8189                 a.h -= 1;
8190             }
8191         break;
8192         case "frame":
8193             a.w = a.h = (o*2);
8194             a.l = a.t = -o;
8195             a.t += 1;
8196             a.h -= 2;
8197             if(Roo.isIE){
8198                 a.l -= (this.offset - rad);
8199                 a.t -= (this.offset - rad);
8200                 a.l += 1;
8201                 a.w -= (this.offset + rad + 1);
8202                 a.h -= (this.offset + rad);
8203                 a.h += 1;
8204             }
8205         break;
8206     };
8207
8208     this.adjusts = a;
8209 };
8210
8211 Roo.Shadow.prototype = {
8212     /**
8213      * @cfg {String} mode
8214      * The shadow display mode.  Supports the following options:<br />
8215      * sides: Shadow displays on both sides and bottom only<br />
8216      * frame: Shadow displays equally on all four sides<br />
8217      * drop: Traditional bottom-right drop shadow (default)
8218      */
8219     /**
8220      * @cfg {String} offset
8221      * The number of pixels to offset the shadow from the element (defaults to 4)
8222      */
8223     offset: 4,
8224
8225     // private
8226     defaultMode: "drop",
8227
8228     /**
8229      * Displays the shadow under the target element
8230      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8231      */
8232     show : function(target){
8233         target = Roo.get(target);
8234         if(!this.el){
8235             this.el = Roo.Shadow.Pool.pull();
8236             if(this.el.dom.nextSibling != target.dom){
8237                 this.el.insertBefore(target);
8238             }
8239         }
8240         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8241         if(Roo.isIE){
8242             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8243         }
8244         this.realign(
8245             target.getLeft(true),
8246             target.getTop(true),
8247             target.getWidth(),
8248             target.getHeight()
8249         );
8250         this.el.dom.style.display = "block";
8251     },
8252
8253     /**
8254      * Returns true if the shadow is visible, else false
8255      */
8256     isVisible : function(){
8257         return this.el ? true : false;  
8258     },
8259
8260     /**
8261      * Direct alignment when values are already available. Show must be called at least once before
8262      * calling this method to ensure it is initialized.
8263      * @param {Number} left The target element left position
8264      * @param {Number} top The target element top position
8265      * @param {Number} width The target element width
8266      * @param {Number} height The target element height
8267      */
8268     realign : function(l, t, w, h){
8269         if(!this.el){
8270             return;
8271         }
8272         var a = this.adjusts, d = this.el.dom, s = d.style;
8273         var iea = 0;
8274         s.left = (l+a.l)+"px";
8275         s.top = (t+a.t)+"px";
8276         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8277  
8278         if(s.width != sws || s.height != shs){
8279             s.width = sws;
8280             s.height = shs;
8281             if(!Roo.isIE){
8282                 var cn = d.childNodes;
8283                 var sww = Math.max(0, (sw-12))+"px";
8284                 cn[0].childNodes[1].style.width = sww;
8285                 cn[1].childNodes[1].style.width = sww;
8286                 cn[2].childNodes[1].style.width = sww;
8287                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8288             }
8289         }
8290     },
8291
8292     /**
8293      * Hides this shadow
8294      */
8295     hide : function(){
8296         if(this.el){
8297             this.el.dom.style.display = "none";
8298             Roo.Shadow.Pool.push(this.el);
8299             delete this.el;
8300         }
8301     },
8302
8303     /**
8304      * Adjust the z-index of this shadow
8305      * @param {Number} zindex The new z-index
8306      */
8307     setZIndex : function(z){
8308         this.zIndex = z;
8309         if(this.el){
8310             this.el.setStyle("z-index", z);
8311         }
8312     }
8313 };
8314
8315 // Private utility class that manages the internal Shadow cache
8316 Roo.Shadow.Pool = function(){
8317     var p = [];
8318     var markup = Roo.isIE ?
8319                  '<div class="x-ie-shadow"></div>' :
8320                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8321     return {
8322         pull : function(){
8323             var sh = p.shift();
8324             if(!sh){
8325                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8326                 sh.autoBoxAdjust = false;
8327             }
8328             return sh;
8329         },
8330
8331         push : function(sh){
8332             p.push(sh);
8333         }
8334     };
8335 }();/*
8336  * Based on:
8337  * Ext JS Library 1.1.1
8338  * Copyright(c) 2006-2007, Ext JS, LLC.
8339  *
8340  * Originally Released Under LGPL - original licence link has changed is not relivant.
8341  *
8342  * Fork - LGPL
8343  * <script type="text/javascript">
8344  */
8345
8346 /**
8347  * @class Roo.BoxComponent
8348  * @extends Roo.Component
8349  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8350  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8351  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8352  * layout containers.
8353  * @constructor
8354  * @param {Roo.Element/String/Object} config The configuration options.
8355  */
8356 Roo.BoxComponent = function(config){
8357     Roo.Component.call(this, config);
8358     this.addEvents({
8359         /**
8360          * @event resize
8361          * Fires after the component is resized.
8362              * @param {Roo.Component} this
8363              * @param {Number} adjWidth The box-adjusted width that was set
8364              * @param {Number} adjHeight The box-adjusted height that was set
8365              * @param {Number} rawWidth The width that was originally specified
8366              * @param {Number} rawHeight The height that was originally specified
8367              */
8368         resize : true,
8369         /**
8370          * @event move
8371          * Fires after the component is moved.
8372              * @param {Roo.Component} this
8373              * @param {Number} x The new x position
8374              * @param {Number} y The new y position
8375              */
8376         move : true
8377     });
8378 };
8379
8380 Roo.extend(Roo.BoxComponent, Roo.Component, {
8381     // private, set in afterRender to signify that the component has been rendered
8382     boxReady : false,
8383     // private, used to defer height settings to subclasses
8384     deferHeight: false,
8385     /** @cfg {Number} width
8386      * width (optional) size of component
8387      */
8388      /** @cfg {Number} height
8389      * height (optional) size of component
8390      */
8391      
8392     /**
8393      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8394      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8395      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8396      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8397      * @return {Roo.BoxComponent} this
8398      */
8399     setSize : function(w, h){
8400         // support for standard size objects
8401         if(typeof w == 'object'){
8402             h = w.height;
8403             w = w.width;
8404         }
8405         // not rendered
8406         if(!this.boxReady){
8407             this.width = w;
8408             this.height = h;
8409             return this;
8410         }
8411
8412         // prevent recalcs when not needed
8413         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8414             return this;
8415         }
8416         this.lastSize = {width: w, height: h};
8417
8418         var adj = this.adjustSize(w, h);
8419         var aw = adj.width, ah = adj.height;
8420         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8421             var rz = this.getResizeEl();
8422             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8423                 rz.setSize(aw, ah);
8424             }else if(!this.deferHeight && ah !== undefined){
8425                 rz.setHeight(ah);
8426             }else if(aw !== undefined){
8427                 rz.setWidth(aw);
8428             }
8429             this.onResize(aw, ah, w, h);
8430             this.fireEvent('resize', this, aw, ah, w, h);
8431         }
8432         return this;
8433     },
8434
8435     /**
8436      * Gets the current size of the component's underlying element.
8437      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8438      */
8439     getSize : function(){
8440         return this.el.getSize();
8441     },
8442
8443     /**
8444      * Gets the current XY position of the component's underlying element.
8445      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8446      * @return {Array} The XY position of the element (e.g., [100, 200])
8447      */
8448     getPosition : function(local){
8449         if(local === true){
8450             return [this.el.getLeft(true), this.el.getTop(true)];
8451         }
8452         return this.xy || this.el.getXY();
8453     },
8454
8455     /**
8456      * Gets the current box measurements of the component's underlying element.
8457      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8458      * @returns {Object} box An object in the format {x, y, width, height}
8459      */
8460     getBox : function(local){
8461         var s = this.el.getSize();
8462         if(local){
8463             s.x = this.el.getLeft(true);
8464             s.y = this.el.getTop(true);
8465         }else{
8466             var xy = this.xy || this.el.getXY();
8467             s.x = xy[0];
8468             s.y = xy[1];
8469         }
8470         return s;
8471     },
8472
8473     /**
8474      * Sets the current box measurements of the component's underlying element.
8475      * @param {Object} box An object in the format {x, y, width, height}
8476      * @returns {Roo.BoxComponent} this
8477      */
8478     updateBox : function(box){
8479         this.setSize(box.width, box.height);
8480         this.setPagePosition(box.x, box.y);
8481         return this;
8482     },
8483
8484     // protected
8485     getResizeEl : function(){
8486         return this.resizeEl || this.el;
8487     },
8488
8489     // protected
8490     getPositionEl : function(){
8491         return this.positionEl || this.el;
8492     },
8493
8494     /**
8495      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8496      * This method fires the move event.
8497      * @param {Number} left The new left
8498      * @param {Number} top The new top
8499      * @returns {Roo.BoxComponent} this
8500      */
8501     setPosition : function(x, y){
8502         this.x = x;
8503         this.y = y;
8504         if(!this.boxReady){
8505             return this;
8506         }
8507         var adj = this.adjustPosition(x, y);
8508         var ax = adj.x, ay = adj.y;
8509
8510         var el = this.getPositionEl();
8511         if(ax !== undefined || ay !== undefined){
8512             if(ax !== undefined && ay !== undefined){
8513                 el.setLeftTop(ax, ay);
8514             }else if(ax !== undefined){
8515                 el.setLeft(ax);
8516             }else if(ay !== undefined){
8517                 el.setTop(ay);
8518             }
8519             this.onPosition(ax, ay);
8520             this.fireEvent('move', this, ax, ay);
8521         }
8522         return this;
8523     },
8524
8525     /**
8526      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8527      * This method fires the move event.
8528      * @param {Number} x The new x position
8529      * @param {Number} y The new y position
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     setPagePosition : function(x, y){
8533         this.pageX = x;
8534         this.pageY = y;
8535         if(!this.boxReady){
8536             return;
8537         }
8538         if(x === undefined || y === undefined){ // cannot translate undefined points
8539             return;
8540         }
8541         var p = this.el.translatePoints(x, y);
8542         this.setPosition(p.left, p.top);
8543         return this;
8544     },
8545
8546     // private
8547     onRender : function(ct, position){
8548         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8549         if(this.resizeEl){
8550             this.resizeEl = Roo.get(this.resizeEl);
8551         }
8552         if(this.positionEl){
8553             this.positionEl = Roo.get(this.positionEl);
8554         }
8555     },
8556
8557     // private
8558     afterRender : function(){
8559         Roo.BoxComponent.superclass.afterRender.call(this);
8560         this.boxReady = true;
8561         this.setSize(this.width, this.height);
8562         if(this.x || this.y){
8563             this.setPosition(this.x, this.y);
8564         }
8565         if(this.pageX || this.pageY){
8566             this.setPagePosition(this.pageX, this.pageY);
8567         }
8568     },
8569
8570     /**
8571      * Force the component's size to recalculate based on the underlying element's current height and width.
8572      * @returns {Roo.BoxComponent} this
8573      */
8574     syncSize : function(){
8575         delete this.lastSize;
8576         this.setSize(this.el.getWidth(), this.el.getHeight());
8577         return this;
8578     },
8579
8580     /**
8581      * Called after the component is resized, this method is empty by default but can be implemented by any
8582      * subclass that needs to perform custom logic after a resize occurs.
8583      * @param {Number} adjWidth The box-adjusted width that was set
8584      * @param {Number} adjHeight The box-adjusted height that was set
8585      * @param {Number} rawWidth The width that was originally specified
8586      * @param {Number} rawHeight The height that was originally specified
8587      */
8588     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8589
8590     },
8591
8592     /**
8593      * Called after the component is moved, this method is empty by default but can be implemented by any
8594      * subclass that needs to perform custom logic after a move occurs.
8595      * @param {Number} x The new x position
8596      * @param {Number} y The new y position
8597      */
8598     onPosition : function(x, y){
8599
8600     },
8601
8602     // private
8603     adjustSize : function(w, h){
8604         if(this.autoWidth){
8605             w = 'auto';
8606         }
8607         if(this.autoHeight){
8608             h = 'auto';
8609         }
8610         return {width : w, height: h};
8611     },
8612
8613     // private
8614     adjustPosition : function(x, y){
8615         return {x : x, y: y};
8616     }
8617 });/*
8618  * Based on:
8619  * Ext JS Library 1.1.1
8620  * Copyright(c) 2006-2007, Ext JS, LLC.
8621  *
8622  * Originally Released Under LGPL - original licence link has changed is not relivant.
8623  *
8624  * Fork - LGPL
8625  * <script type="text/javascript">
8626  */
8627
8628
8629 /**
8630  * @class Roo.SplitBar
8631  * @extends Roo.util.Observable
8632  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8633  * <br><br>
8634  * Usage:
8635  * <pre><code>
8636 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8637                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8638 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8639 split.minSize = 100;
8640 split.maxSize = 600;
8641 split.animate = true;
8642 split.on('moved', splitterMoved);
8643 </code></pre>
8644  * @constructor
8645  * Create a new SplitBar
8646  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8647  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8648  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8649  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8650                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8651                         position of the SplitBar).
8652  */
8653 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8654     
8655     /** @private */
8656     this.el = Roo.get(dragElement, true);
8657     this.el.dom.unselectable = "on";
8658     /** @private */
8659     this.resizingEl = Roo.get(resizingElement, true);
8660
8661     /**
8662      * @private
8663      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8664      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8665      * @type Number
8666      */
8667     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8668     
8669     /**
8670      * The minimum size of the resizing element. (Defaults to 0)
8671      * @type Number
8672      */
8673     this.minSize = 0;
8674     
8675     /**
8676      * The maximum size of the resizing element. (Defaults to 2000)
8677      * @type Number
8678      */
8679     this.maxSize = 2000;
8680     
8681     /**
8682      * Whether to animate the transition to the new size
8683      * @type Boolean
8684      */
8685     this.animate = false;
8686     
8687     /**
8688      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8689      * @type Boolean
8690      */
8691     this.useShim = false;
8692     
8693     /** @private */
8694     this.shim = null;
8695     
8696     if(!existingProxy){
8697         /** @private */
8698         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8699     }else{
8700         this.proxy = Roo.get(existingProxy).dom;
8701     }
8702     /** @private */
8703     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8704     
8705     /** @private */
8706     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8707     
8708     /** @private */
8709     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8710     
8711     /** @private */
8712     this.dragSpecs = {};
8713     
8714     /**
8715      * @private The adapter to use to positon and resize elements
8716      */
8717     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8718     this.adapter.init(this);
8719     
8720     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8721         /** @private */
8722         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8723         this.el.addClass("x-splitbar-h");
8724     }else{
8725         /** @private */
8726         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8727         this.el.addClass("x-splitbar-v");
8728     }
8729     
8730     this.addEvents({
8731         /**
8732          * @event resize
8733          * Fires when the splitter is moved (alias for {@link #event-moved})
8734          * @param {Roo.SplitBar} this
8735          * @param {Number} newSize the new width or height
8736          */
8737         "resize" : true,
8738         /**
8739          * @event moved
8740          * Fires when the splitter is moved
8741          * @param {Roo.SplitBar} this
8742          * @param {Number} newSize the new width or height
8743          */
8744         "moved" : true,
8745         /**
8746          * @event beforeresize
8747          * Fires before the splitter is dragged
8748          * @param {Roo.SplitBar} this
8749          */
8750         "beforeresize" : true,
8751
8752         "beforeapply" : true
8753     });
8754
8755     Roo.util.Observable.call(this);
8756 };
8757
8758 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8759     onStartProxyDrag : function(x, y){
8760         this.fireEvent("beforeresize", this);
8761         if(!this.overlay){
8762             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8763             o.unselectable();
8764             o.enableDisplayMode("block");
8765             // all splitbars share the same overlay
8766             Roo.SplitBar.prototype.overlay = o;
8767         }
8768         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8769         this.overlay.show();
8770         Roo.get(this.proxy).setDisplayed("block");
8771         var size = this.adapter.getElementSize(this);
8772         this.activeMinSize = this.getMinimumSize();;
8773         this.activeMaxSize = this.getMaximumSize();;
8774         var c1 = size - this.activeMinSize;
8775         var c2 = Math.max(this.activeMaxSize - size, 0);
8776         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8777             this.dd.resetConstraints();
8778             this.dd.setXConstraint(
8779                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8780                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8781             );
8782             this.dd.setYConstraint(0, 0);
8783         }else{
8784             this.dd.resetConstraints();
8785             this.dd.setXConstraint(0, 0);
8786             this.dd.setYConstraint(
8787                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8788                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8789             );
8790          }
8791         this.dragSpecs.startSize = size;
8792         this.dragSpecs.startPoint = [x, y];
8793         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8794     },
8795     
8796     /** 
8797      * @private Called after the drag operation by the DDProxy
8798      */
8799     onEndProxyDrag : function(e){
8800         Roo.get(this.proxy).setDisplayed(false);
8801         var endPoint = Roo.lib.Event.getXY(e);
8802         if(this.overlay){
8803             this.overlay.hide();
8804         }
8805         var newSize;
8806         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8807             newSize = this.dragSpecs.startSize + 
8808                 (this.placement == Roo.SplitBar.LEFT ?
8809                     endPoint[0] - this.dragSpecs.startPoint[0] :
8810                     this.dragSpecs.startPoint[0] - endPoint[0]
8811                 );
8812         }else{
8813             newSize = this.dragSpecs.startSize + 
8814                 (this.placement == Roo.SplitBar.TOP ?
8815                     endPoint[1] - this.dragSpecs.startPoint[1] :
8816                     this.dragSpecs.startPoint[1] - endPoint[1]
8817                 );
8818         }
8819         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8820         if(newSize != this.dragSpecs.startSize){
8821             if(this.fireEvent('beforeapply', this, newSize) !== false){
8822                 this.adapter.setElementSize(this, newSize);
8823                 this.fireEvent("moved", this, newSize);
8824                 this.fireEvent("resize", this, newSize);
8825             }
8826         }
8827     },
8828     
8829     /**
8830      * Get the adapter this SplitBar uses
8831      * @return The adapter object
8832      */
8833     getAdapter : function(){
8834         return this.adapter;
8835     },
8836     
8837     /**
8838      * Set the adapter this SplitBar uses
8839      * @param {Object} adapter A SplitBar adapter object
8840      */
8841     setAdapter : function(adapter){
8842         this.adapter = adapter;
8843         this.adapter.init(this);
8844     },
8845     
8846     /**
8847      * Gets the minimum size for the resizing element
8848      * @return {Number} The minimum size
8849      */
8850     getMinimumSize : function(){
8851         return this.minSize;
8852     },
8853     
8854     /**
8855      * Sets the minimum size for the resizing element
8856      * @param {Number} minSize The minimum size
8857      */
8858     setMinimumSize : function(minSize){
8859         this.minSize = minSize;
8860     },
8861     
8862     /**
8863      * Gets the maximum size for the resizing element
8864      * @return {Number} The maximum size
8865      */
8866     getMaximumSize : function(){
8867         return this.maxSize;
8868     },
8869     
8870     /**
8871      * Sets the maximum size for the resizing element
8872      * @param {Number} maxSize The maximum size
8873      */
8874     setMaximumSize : function(maxSize){
8875         this.maxSize = maxSize;
8876     },
8877     
8878     /**
8879      * Sets the initialize size for the resizing element
8880      * @param {Number} size The initial size
8881      */
8882     setCurrentSize : function(size){
8883         var oldAnimate = this.animate;
8884         this.animate = false;
8885         this.adapter.setElementSize(this, size);
8886         this.animate = oldAnimate;
8887     },
8888     
8889     /**
8890      * Destroy this splitbar. 
8891      * @param {Boolean} removeEl True to remove the element
8892      */
8893     destroy : function(removeEl){
8894         if(this.shim){
8895             this.shim.remove();
8896         }
8897         this.dd.unreg();
8898         this.proxy.parentNode.removeChild(this.proxy);
8899         if(removeEl){
8900             this.el.remove();
8901         }
8902     }
8903 });
8904
8905 /**
8906  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8907  */
8908 Roo.SplitBar.createProxy = function(dir){
8909     var proxy = new Roo.Element(document.createElement("div"));
8910     proxy.unselectable();
8911     var cls = 'x-splitbar-proxy';
8912     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8913     document.body.appendChild(proxy.dom);
8914     return proxy.dom;
8915 };
8916
8917 /** 
8918  * @class Roo.SplitBar.BasicLayoutAdapter
8919  * Default Adapter. It assumes the splitter and resizing element are not positioned
8920  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8921  */
8922 Roo.SplitBar.BasicLayoutAdapter = function(){
8923 };
8924
8925 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8926     // do nothing for now
8927     init : function(s){
8928     
8929     },
8930     /**
8931      * Called before drag operations to get the current size of the resizing element. 
8932      * @param {Roo.SplitBar} s The SplitBar using this adapter
8933      */
8934      getElementSize : function(s){
8935         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8936             return s.resizingEl.getWidth();
8937         }else{
8938             return s.resizingEl.getHeight();
8939         }
8940     },
8941     
8942     /**
8943      * Called after drag operations to set the size of the resizing element.
8944      * @param {Roo.SplitBar} s The SplitBar using this adapter
8945      * @param {Number} newSize The new size to set
8946      * @param {Function} onComplete A function to be invoked when resizing is complete
8947      */
8948     setElementSize : function(s, newSize, onComplete){
8949         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8950             if(!s.animate){
8951                 s.resizingEl.setWidth(newSize);
8952                 if(onComplete){
8953                     onComplete(s, newSize);
8954                 }
8955             }else{
8956                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8957             }
8958         }else{
8959             
8960             if(!s.animate){
8961                 s.resizingEl.setHeight(newSize);
8962                 if(onComplete){
8963                     onComplete(s, newSize);
8964                 }
8965             }else{
8966                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8967             }
8968         }
8969     }
8970 };
8971
8972 /** 
8973  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8974  * @extends Roo.SplitBar.BasicLayoutAdapter
8975  * Adapter that  moves the splitter element to align with the resized sizing element. 
8976  * Used with an absolute positioned SplitBar.
8977  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8978  * document.body, make sure you assign an id to the body element.
8979  */
8980 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8981     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8982     this.container = Roo.get(container);
8983 };
8984
8985 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8986     init : function(s){
8987         this.basic.init(s);
8988     },
8989     
8990     getElementSize : function(s){
8991         return this.basic.getElementSize(s);
8992     },
8993     
8994     setElementSize : function(s, newSize, onComplete){
8995         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8996     },
8997     
8998     moveSplitter : function(s){
8999         var yes = Roo.SplitBar;
9000         switch(s.placement){
9001             case yes.LEFT:
9002                 s.el.setX(s.resizingEl.getRight());
9003                 break;
9004             case yes.RIGHT:
9005                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9006                 break;
9007             case yes.TOP:
9008                 s.el.setY(s.resizingEl.getBottom());
9009                 break;
9010             case yes.BOTTOM:
9011                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9012                 break;
9013         }
9014     }
9015 };
9016
9017 /**
9018  * Orientation constant - Create a vertical SplitBar
9019  * @static
9020  * @type Number
9021  */
9022 Roo.SplitBar.VERTICAL = 1;
9023
9024 /**
9025  * Orientation constant - Create a horizontal SplitBar
9026  * @static
9027  * @type Number
9028  */
9029 Roo.SplitBar.HORIZONTAL = 2;
9030
9031 /**
9032  * Placement constant - The resizing element is to the left of the splitter element
9033  * @static
9034  * @type Number
9035  */
9036 Roo.SplitBar.LEFT = 1;
9037
9038 /**
9039  * Placement constant - The resizing element is to the right of the splitter element
9040  * @static
9041  * @type Number
9042  */
9043 Roo.SplitBar.RIGHT = 2;
9044
9045 /**
9046  * Placement constant - The resizing element is positioned above the splitter element
9047  * @static
9048  * @type Number
9049  */
9050 Roo.SplitBar.TOP = 3;
9051
9052 /**
9053  * Placement constant - The resizing element is positioned under splitter element
9054  * @static
9055  * @type Number
9056  */
9057 Roo.SplitBar.BOTTOM = 4;
9058 /*
9059  * Based on:
9060  * Ext JS Library 1.1.1
9061  * Copyright(c) 2006-2007, Ext JS, LLC.
9062  *
9063  * Originally Released Under LGPL - original licence link has changed is not relivant.
9064  *
9065  * Fork - LGPL
9066  * <script type="text/javascript">
9067  */
9068
9069 /**
9070  * @class Roo.View
9071  * @extends Roo.util.Observable
9072  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9073  * This class also supports single and multi selection modes. <br>
9074  * Create a data model bound view:
9075  <pre><code>
9076  var store = new Roo.data.Store(...);
9077
9078  var view = new Roo.View({
9079     el : "my-element",
9080     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9081  
9082     singleSelect: true,
9083     selectedClass: "ydataview-selected",
9084     store: store
9085  });
9086
9087  // listen for node click?
9088  view.on("click", function(vw, index, node, e){
9089  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9090  });
9091
9092  // load XML data
9093  dataModel.load("foobar.xml");
9094  </code></pre>
9095  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9096  * <br><br>
9097  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9098  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9099  * 
9100  * Note: old style constructor is still suported (container, template, config)
9101  * 
9102  * @constructor
9103  * Create a new View
9104  * @param {Object} config The config object
9105  * 
9106  */
9107 Roo.View = function(config, depreciated_tpl, depreciated_config){
9108     
9109     if (typeof(depreciated_tpl) == 'undefined') {
9110         // new way.. - universal constructor.
9111         Roo.apply(this, config);
9112         this.el  = Roo.get(this.el);
9113     } else {
9114         // old format..
9115         this.el  = Roo.get(config);
9116         this.tpl = depreciated_tpl;
9117         Roo.apply(this, depreciated_config);
9118     }
9119      
9120     
9121     if(typeof(this.tpl) == "string"){
9122         this.tpl = new Roo.Template(this.tpl);
9123     } else {
9124         // support xtype ctors..
9125         this.tpl = new Roo.factory(this.tpl, Roo);
9126     }
9127     
9128     
9129     this.tpl.compile();
9130    
9131
9132      
9133     /** @private */
9134     this.addEvents({
9135     /**
9136      * @event beforeclick
9137      * Fires before a click is processed. Returns false to cancel the default action.
9138      * @param {Roo.View} this
9139      * @param {Number} index The index of the target node
9140      * @param {HTMLElement} node The target node
9141      * @param {Roo.EventObject} e The raw event object
9142      */
9143         "beforeclick" : true,
9144     /**
9145      * @event click
9146      * Fires when a template node is clicked.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "click" : true,
9153     /**
9154      * @event dblclick
9155      * Fires when a template node is double clicked.
9156      * @param {Roo.View} this
9157      * @param {Number} index The index of the target node
9158      * @param {HTMLElement} node The target node
9159      * @param {Roo.EventObject} e The raw event object
9160      */
9161         "dblclick" : true,
9162     /**
9163      * @event contextmenu
9164      * Fires when a template node is right clicked.
9165      * @param {Roo.View} this
9166      * @param {Number} index The index of the target node
9167      * @param {HTMLElement} node The target node
9168      * @param {Roo.EventObject} e The raw event object
9169      */
9170         "contextmenu" : true,
9171     /**
9172      * @event selectionchange
9173      * Fires when the selected nodes change.
9174      * @param {Roo.View} this
9175      * @param {Array} selections Array of the selected nodes
9176      */
9177         "selectionchange" : true,
9178
9179     /**
9180      * @event beforeselect
9181      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9182      * @param {Roo.View} this
9183      * @param {HTMLElement} node The node to be selected
9184      * @param {Array} selections Array of currently selected nodes
9185      */
9186         "beforeselect" : true
9187     });
9188
9189     this.el.on({
9190         "click": this.onClick,
9191         "dblclick": this.onDblClick,
9192         "contextmenu": this.onContextMenu,
9193         scope:this
9194     });
9195
9196     this.selections = [];
9197     this.nodes = [];
9198     this.cmp = new Roo.CompositeElementLite([]);
9199     if(this.store){
9200         this.store = Roo.factory(this.store, Roo.data);
9201         this.setStore(this.store, true);
9202     }
9203     Roo.View.superclass.constructor.call(this);
9204 };
9205
9206 Roo.extend(Roo.View, Roo.util.Observable, {
9207     
9208      /**
9209      * @cfg {Roo.data.Store} store Data store to load data from.
9210      */
9211     store : false,
9212     
9213     /**
9214      * @cfg {String|Roo.Element} el The container element.
9215      */
9216     el : '',
9217     
9218     /**
9219      * @cfg {String|Roo.Template} tpl The template used by this View 
9220      */
9221     tpl : false,
9222     
9223     /**
9224      * @cfg {String} selectedClass The css class to add to selected nodes
9225      */
9226     selectedClass : "x-view-selected",
9227      /**
9228      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9229      */
9230     emptyText : "",
9231     /**
9232      * @cfg {Boolean} multiSelect Allow multiple selection
9233      */
9234     
9235     multiSelect : false,
9236     /**
9237      * @cfg {Boolean} singleSelect Allow single selection
9238      */
9239     singleSelect:  false,
9240     
9241     /**
9242      * Returns the element this view is bound to.
9243      * @return {Roo.Element}
9244      */
9245     getEl : function(){
9246         return this.el;
9247     },
9248
9249     /**
9250      * Refreshes the view.
9251      */
9252     refresh : function(){
9253         var t = this.tpl;
9254         this.clearSelections();
9255         this.el.update("");
9256         var html = [];
9257         var records = this.store.getRange();
9258         if(records.length < 1){
9259             this.el.update(this.emptyText);
9260             return;
9261         }
9262         for(var i = 0, len = records.length; i < len; i++){
9263             var data = this.prepareData(records[i].data, i, records[i]);
9264             html[html.length] = t.apply(data);
9265         }
9266         this.el.update(html.join(""));
9267         this.nodes = this.el.dom.childNodes;
9268         this.updateIndexes(0);
9269     },
9270
9271     /**
9272      * Function to override to reformat the data that is sent to
9273      * the template for each node.
9274      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9275      * a JSON object for an UpdateManager bound view).
9276      */
9277     prepareData : function(data){
9278         return data;
9279     },
9280
9281     onUpdate : function(ds, record){
9282         this.clearSelections();
9283         var index = this.store.indexOf(record);
9284         var n = this.nodes[index];
9285         this.tpl.insertBefore(n, this.prepareData(record.data));
9286         n.parentNode.removeChild(n);
9287         this.updateIndexes(index, index);
9288     },
9289
9290     onAdd : function(ds, records, index){
9291         this.clearSelections();
9292         if(this.nodes.length == 0){
9293             this.refresh();
9294             return;
9295         }
9296         var n = this.nodes[index];
9297         for(var i = 0, len = records.length; i < len; i++){
9298             var d = this.prepareData(records[i].data);
9299             if(n){
9300                 this.tpl.insertBefore(n, d);
9301             }else{
9302                 this.tpl.append(this.el, d);
9303             }
9304         }
9305         this.updateIndexes(index);
9306     },
9307
9308     onRemove : function(ds, record, index){
9309         this.clearSelections();
9310         this.el.dom.removeChild(this.nodes[index]);
9311         this.updateIndexes(index);
9312     },
9313
9314     /**
9315      * Refresh an individual node.
9316      * @param {Number} index
9317      */
9318     refreshNode : function(index){
9319         this.onUpdate(this.store, this.store.getAt(index));
9320     },
9321
9322     updateIndexes : function(startIndex, endIndex){
9323         var ns = this.nodes;
9324         startIndex = startIndex || 0;
9325         endIndex = endIndex || ns.length - 1;
9326         for(var i = startIndex; i <= endIndex; i++){
9327             ns[i].nodeIndex = i;
9328         }
9329     },
9330
9331     /**
9332      * Changes the data store this view uses and refresh the view.
9333      * @param {Store} store
9334      */
9335     setStore : function(store, initial){
9336         if(!initial && this.store){
9337             this.store.un("datachanged", this.refresh);
9338             this.store.un("add", this.onAdd);
9339             this.store.un("remove", this.onRemove);
9340             this.store.un("update", this.onUpdate);
9341             this.store.un("clear", this.refresh);
9342         }
9343         if(store){
9344           
9345             store.on("datachanged", this.refresh, this);
9346             store.on("add", this.onAdd, this);
9347             store.on("remove", this.onRemove, this);
9348             store.on("update", this.onUpdate, this);
9349             store.on("clear", this.refresh, this);
9350         }
9351         
9352         if(store){
9353             this.refresh();
9354         }
9355     },
9356
9357     /**
9358      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9359      * @param {HTMLElement} node
9360      * @return {HTMLElement} The template node
9361      */
9362     findItemFromChild : function(node){
9363         var el = this.el.dom;
9364         if(!node || node.parentNode == el){
9365                     return node;
9366             }
9367             var p = node.parentNode;
9368             while(p && p != el){
9369             if(p.parentNode == el){
9370                 return p;
9371             }
9372             p = p.parentNode;
9373         }
9374             return null;
9375     },
9376
9377     /** @ignore */
9378     onClick : function(e){
9379         var item = this.findItemFromChild(e.getTarget());
9380         if(item){
9381             var index = this.indexOf(item);
9382             if(this.onItemClick(item, index, e) !== false){
9383                 this.fireEvent("click", this, index, item, e);
9384             }
9385         }else{
9386             this.clearSelections();
9387         }
9388     },
9389
9390     /** @ignore */
9391     onContextMenu : function(e){
9392         var item = this.findItemFromChild(e.getTarget());
9393         if(item){
9394             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9395         }
9396     },
9397
9398     /** @ignore */
9399     onDblClick : function(e){
9400         var item = this.findItemFromChild(e.getTarget());
9401         if(item){
9402             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9403         }
9404     },
9405
9406     onItemClick : function(item, index, e){
9407         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9408             return false;
9409         }
9410         if(this.multiSelect || this.singleSelect){
9411             if(this.multiSelect && e.shiftKey && this.lastSelection){
9412                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9413             }else{
9414                 this.select(item, this.multiSelect && e.ctrlKey);
9415                 this.lastSelection = item;
9416             }
9417             e.preventDefault();
9418         }
9419         return true;
9420     },
9421
9422     /**
9423      * Get the number of selected nodes.
9424      * @return {Number}
9425      */
9426     getSelectionCount : function(){
9427         return this.selections.length;
9428     },
9429
9430     /**
9431      * Get the currently selected nodes.
9432      * @return {Array} An array of HTMLElements
9433      */
9434     getSelectedNodes : function(){
9435         return this.selections;
9436     },
9437
9438     /**
9439      * Get the indexes of the selected nodes.
9440      * @return {Array}
9441      */
9442     getSelectedIndexes : function(){
9443         var indexes = [], s = this.selections;
9444         for(var i = 0, len = s.length; i < len; i++){
9445             indexes.push(s[i].nodeIndex);
9446         }
9447         return indexes;
9448     },
9449
9450     /**
9451      * Clear all selections
9452      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9453      */
9454     clearSelections : function(suppressEvent){
9455         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9456             this.cmp.elements = this.selections;
9457             this.cmp.removeClass(this.selectedClass);
9458             this.selections = [];
9459             if(!suppressEvent){
9460                 this.fireEvent("selectionchange", this, this.selections);
9461             }
9462         }
9463     },
9464
9465     /**
9466      * Returns true if the passed node is selected
9467      * @param {HTMLElement/Number} node The node or node index
9468      * @return {Boolean}
9469      */
9470     isSelected : function(node){
9471         var s = this.selections;
9472         if(s.length < 1){
9473             return false;
9474         }
9475         node = this.getNode(node);
9476         return s.indexOf(node) !== -1;
9477     },
9478
9479     /**
9480      * Selects nodes.
9481      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9482      * @param {Boolean} keepExisting (optional) true to keep existing selections
9483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9484      */
9485     select : function(nodeInfo, keepExisting, suppressEvent){
9486         if(nodeInfo instanceof Array){
9487             if(!keepExisting){
9488                 this.clearSelections(true);
9489             }
9490             for(var i = 0, len = nodeInfo.length; i < len; i++){
9491                 this.select(nodeInfo[i], true, true);
9492             }
9493         } else{
9494             var node = this.getNode(nodeInfo);
9495             if(node && !this.isSelected(node)){
9496                 if(!keepExisting){
9497                     this.clearSelections(true);
9498                 }
9499                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9500                     Roo.fly(node).addClass(this.selectedClass);
9501                     this.selections.push(node);
9502                     if(!suppressEvent){
9503                         this.fireEvent("selectionchange", this, this.selections);
9504                     }
9505                 }
9506             }
9507         }
9508     },
9509
9510     /**
9511      * Gets a template node.
9512      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9513      * @return {HTMLElement} The node or null if it wasn't found
9514      */
9515     getNode : function(nodeInfo){
9516         if(typeof nodeInfo == "string"){
9517             return document.getElementById(nodeInfo);
9518         }else if(typeof nodeInfo == "number"){
9519             return this.nodes[nodeInfo];
9520         }
9521         return nodeInfo;
9522     },
9523
9524     /**
9525      * Gets a range template nodes.
9526      * @param {Number} startIndex
9527      * @param {Number} endIndex
9528      * @return {Array} An array of nodes
9529      */
9530     getNodes : function(start, end){
9531         var ns = this.nodes;
9532         start = start || 0;
9533         end = typeof end == "undefined" ? ns.length - 1 : end;
9534         var nodes = [];
9535         if(start <= end){
9536             for(var i = start; i <= end; i++){
9537                 nodes.push(ns[i]);
9538             }
9539         } else{
9540             for(var i = start; i >= end; i--){
9541                 nodes.push(ns[i]);
9542             }
9543         }
9544         return nodes;
9545     },
9546
9547     /**
9548      * Finds the index of the passed node
9549      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9550      * @return {Number} The index of the node or -1
9551      */
9552     indexOf : function(node){
9553         node = this.getNode(node);
9554         if(typeof node.nodeIndex == "number"){
9555             return node.nodeIndex;
9556         }
9557         var ns = this.nodes;
9558         for(var i = 0, len = ns.length; i < len; i++){
9559             if(ns[i] == node){
9560                 return i;
9561             }
9562         }
9563         return -1;
9564     }
9565 });
9566 /*
9567  * Based on:
9568  * Ext JS Library 1.1.1
9569  * Copyright(c) 2006-2007, Ext JS, LLC.
9570  *
9571  * Originally Released Under LGPL - original licence link has changed is not relivant.
9572  *
9573  * Fork - LGPL
9574  * <script type="text/javascript">
9575  */
9576
9577 /**
9578  * @class Roo.JsonView
9579  * @extends Roo.View
9580  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9581 <pre><code>
9582 var view = new Roo.JsonView({
9583     container: "my-element",
9584     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9585     multiSelect: true, 
9586     jsonRoot: "data" 
9587 });
9588
9589 // listen for node click?
9590 view.on("click", function(vw, index, node, e){
9591     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9592 });
9593
9594 // direct load of JSON data
9595 view.load("foobar.php");
9596
9597 // Example from my blog list
9598 var tpl = new Roo.Template(
9599     '&lt;div class="entry"&gt;' +
9600     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9601     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9602     "&lt;/div&gt;&lt;hr /&gt;"
9603 );
9604
9605 var moreView = new Roo.JsonView({
9606     container :  "entry-list", 
9607     template : tpl,
9608     jsonRoot: "posts"
9609 });
9610 moreView.on("beforerender", this.sortEntries, this);
9611 moreView.load({
9612     url: "/blog/get-posts.php",
9613     params: "allposts=true",
9614     text: "Loading Blog Entries..."
9615 });
9616 </code></pre>
9617
9618 * Note: old code is supported with arguments : (container, template, config)
9619
9620
9621  * @constructor
9622  * Create a new JsonView
9623  * 
9624  * @param {Object} config The config object
9625  * 
9626  */
9627 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9628     
9629     
9630     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9631
9632     var um = this.el.getUpdateManager();
9633     um.setRenderer(this);
9634     um.on("update", this.onLoad, this);
9635     um.on("failure", this.onLoadException, this);
9636
9637     /**
9638      * @event beforerender
9639      * Fires before rendering of the downloaded JSON data.
9640      * @param {Roo.JsonView} this
9641      * @param {Object} data The JSON data loaded
9642      */
9643     /**
9644      * @event load
9645      * Fires when data is loaded.
9646      * @param {Roo.JsonView} this
9647      * @param {Object} data The JSON data loaded
9648      * @param {Object} response The raw Connect response object
9649      */
9650     /**
9651      * @event loadexception
9652      * Fires when loading fails.
9653      * @param {Roo.JsonView} this
9654      * @param {Object} response The raw Connect response object
9655      */
9656     this.addEvents({
9657         'beforerender' : true,
9658         'load' : true,
9659         'loadexception' : true
9660     });
9661 };
9662 Roo.extend(Roo.JsonView, Roo.View, {
9663     /**
9664      * @type {String} The root property in the loaded JSON object that contains the data
9665      */
9666     jsonRoot : "",
9667
9668     /**
9669      * Refreshes the view.
9670      */
9671     refresh : function(){
9672         this.clearSelections();
9673         this.el.update("");
9674         var html = [];
9675         var o = this.jsonData;
9676         if(o && o.length > 0){
9677             for(var i = 0, len = o.length; i < len; i++){
9678                 var data = this.prepareData(o[i], i, o);
9679                 html[html.length] = this.tpl.apply(data);
9680             }
9681         }else{
9682             html.push(this.emptyText);
9683         }
9684         this.el.update(html.join(""));
9685         this.nodes = this.el.dom.childNodes;
9686         this.updateIndexes(0);
9687     },
9688
9689     /**
9690      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9691      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9692      <pre><code>
9693      view.load({
9694          url: "your-url.php",
9695          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9696          callback: yourFunction,
9697          scope: yourObject, //(optional scope)
9698          discardUrl: false,
9699          nocache: false,
9700          text: "Loading...",
9701          timeout: 30,
9702          scripts: false
9703      });
9704      </code></pre>
9705      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9706      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9707      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9708      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9709      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9710      */
9711     load : function(){
9712         var um = this.el.getUpdateManager();
9713         um.update.apply(um, arguments);
9714     },
9715
9716     render : function(el, response){
9717         this.clearSelections();
9718         this.el.update("");
9719         var o;
9720         try{
9721             o = Roo.util.JSON.decode(response.responseText);
9722             if(this.jsonRoot){
9723                 
9724                 o = o[this.jsonRoot];
9725             }
9726         } catch(e){
9727         }
9728         /**
9729          * The current JSON data or null
9730          */
9731         this.jsonData = o;
9732         this.beforeRender();
9733         this.refresh();
9734     },
9735
9736 /**
9737  * Get the number of records in the current JSON dataset
9738  * @return {Number}
9739  */
9740     getCount : function(){
9741         return this.jsonData ? this.jsonData.length : 0;
9742     },
9743
9744 /**
9745  * Returns the JSON object for the specified node(s)
9746  * @param {HTMLElement/Array} node The node or an array of nodes
9747  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9748  * you get the JSON object for the node
9749  */
9750     getNodeData : function(node){
9751         if(node instanceof Array){
9752             var data = [];
9753             for(var i = 0, len = node.length; i < len; i++){
9754                 data.push(this.getNodeData(node[i]));
9755             }
9756             return data;
9757         }
9758         return this.jsonData[this.indexOf(node)] || null;
9759     },
9760
9761     beforeRender : function(){
9762         this.snapshot = this.jsonData;
9763         if(this.sortInfo){
9764             this.sort.apply(this, this.sortInfo);
9765         }
9766         this.fireEvent("beforerender", this, this.jsonData);
9767     },
9768
9769     onLoad : function(el, o){
9770         this.fireEvent("load", this, this.jsonData, o);
9771     },
9772
9773     onLoadException : function(el, o){
9774         this.fireEvent("loadexception", this, o);
9775     },
9776
9777 /**
9778  * Filter the data by a specific property.
9779  * @param {String} property A property on your JSON objects
9780  * @param {String/RegExp} value Either string that the property values
9781  * should start with, or a RegExp to test against the property
9782  */
9783     filter : function(property, value){
9784         if(this.jsonData){
9785             var data = [];
9786             var ss = this.snapshot;
9787             if(typeof value == "string"){
9788                 var vlen = value.length;
9789                 if(vlen == 0){
9790                     this.clearFilter();
9791                     return;
9792                 }
9793                 value = value.toLowerCase();
9794                 for(var i = 0, len = ss.length; i < len; i++){
9795                     var o = ss[i];
9796                     if(o[property].substr(0, vlen).toLowerCase() == value){
9797                         data.push(o);
9798                     }
9799                 }
9800             } else if(value.exec){ // regex?
9801                 for(var i = 0, len = ss.length; i < len; i++){
9802                     var o = ss[i];
9803                     if(value.test(o[property])){
9804                         data.push(o);
9805                     }
9806                 }
9807             } else{
9808                 return;
9809             }
9810             this.jsonData = data;
9811             this.refresh();
9812         }
9813     },
9814
9815 /**
9816  * Filter by a function. The passed function will be called with each
9817  * object in the current dataset. If the function returns true the value is kept,
9818  * otherwise it is filtered.
9819  * @param {Function} fn
9820  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9821  */
9822     filterBy : function(fn, scope){
9823         if(this.jsonData){
9824             var data = [];
9825             var ss = this.snapshot;
9826             for(var i = 0, len = ss.length; i < len; i++){
9827                 var o = ss[i];
9828                 if(fn.call(scope || this, o)){
9829                     data.push(o);
9830                 }
9831             }
9832             this.jsonData = data;
9833             this.refresh();
9834         }
9835     },
9836
9837 /**
9838  * Clears the current filter.
9839  */
9840     clearFilter : function(){
9841         if(this.snapshot && this.jsonData != this.snapshot){
9842             this.jsonData = this.snapshot;
9843             this.refresh();
9844         }
9845     },
9846
9847
9848 /**
9849  * Sorts the data for this view and refreshes it.
9850  * @param {String} property A property on your JSON objects to sort on
9851  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9852  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9853  */
9854     sort : function(property, dir, sortType){
9855         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9856         if(this.jsonData){
9857             var p = property;
9858             var dsc = dir && dir.toLowerCase() == "desc";
9859             var f = function(o1, o2){
9860                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9861                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9862                 ;
9863                 if(v1 < v2){
9864                     return dsc ? +1 : -1;
9865                 } else if(v1 > v2){
9866                     return dsc ? -1 : +1;
9867                 } else{
9868                     return 0;
9869                 }
9870             };
9871             this.jsonData.sort(f);
9872             this.refresh();
9873             if(this.jsonData != this.snapshot){
9874                 this.snapshot.sort(f);
9875             }
9876         }
9877     }
9878 });/*
9879  * Based on:
9880  * Ext JS Library 1.1.1
9881  * Copyright(c) 2006-2007, Ext JS, LLC.
9882  *
9883  * Originally Released Under LGPL - original licence link has changed is not relivant.
9884  *
9885  * Fork - LGPL
9886  * <script type="text/javascript">
9887  */
9888  
9889
9890 /**
9891  * @class Roo.ColorPalette
9892  * @extends Roo.Component
9893  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9894  * Here's an example of typical usage:
9895  * <pre><code>
9896 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9897 cp.render('my-div');
9898
9899 cp.on('select', function(palette, selColor){
9900     // do something with selColor
9901 });
9902 </code></pre>
9903  * @constructor
9904  * Create a new ColorPalette
9905  * @param {Object} config The config object
9906  */
9907 Roo.ColorPalette = function(config){
9908     Roo.ColorPalette.superclass.constructor.call(this, config);
9909     this.addEvents({
9910         /**
9911              * @event select
9912              * Fires when a color is selected
9913              * @param {ColorPalette} this
9914              * @param {String} color The 6-digit color hex code (without the # symbol)
9915              */
9916         select: true
9917     });
9918
9919     if(this.handler){
9920         this.on("select", this.handler, this.scope, true);
9921     }
9922 };
9923 Roo.extend(Roo.ColorPalette, Roo.Component, {
9924     /**
9925      * @cfg {String} itemCls
9926      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9927      */
9928     itemCls : "x-color-palette",
9929     /**
9930      * @cfg {String} value
9931      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9932      * the hex codes are case-sensitive.
9933      */
9934     value : null,
9935     clickEvent:'click',
9936     // private
9937     ctype: "Roo.ColorPalette",
9938
9939     /**
9940      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9941      */
9942     allowReselect : false,
9943
9944     /**
9945      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9946      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9947      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9948      * of colors with the width setting until the box is symmetrical.</p>
9949      * <p>You can override individual colors if needed:</p>
9950      * <pre><code>
9951 var cp = new Roo.ColorPalette();
9952 cp.colors[0] = "FF0000";  // change the first box to red
9953 </code></pre>
9954
9955 Or you can provide a custom array of your own for complete control:
9956 <pre><code>
9957 var cp = new Roo.ColorPalette();
9958 cp.colors = ["000000", "993300", "333300"];
9959 </code></pre>
9960      * @type Array
9961      */
9962     colors : [
9963         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9964         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9965         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9966         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9967         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9968     ],
9969
9970     // private
9971     onRender : function(container, position){
9972         var t = new Roo.MasterTemplate(
9973             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9974         );
9975         var c = this.colors;
9976         for(var i = 0, len = c.length; i < len; i++){
9977             t.add([c[i]]);
9978         }
9979         var el = document.createElement("div");
9980         el.className = this.itemCls;
9981         t.overwrite(el);
9982         container.dom.insertBefore(el, position);
9983         this.el = Roo.get(el);
9984         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9985         if(this.clickEvent != 'click'){
9986             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9987         }
9988     },
9989
9990     // private
9991     afterRender : function(){
9992         Roo.ColorPalette.superclass.afterRender.call(this);
9993         if(this.value){
9994             var s = this.value;
9995             this.value = null;
9996             this.select(s);
9997         }
9998     },
9999
10000     // private
10001     handleClick : function(e, t){
10002         e.preventDefault();
10003         if(!this.disabled){
10004             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10005             this.select(c.toUpperCase());
10006         }
10007     },
10008
10009     /**
10010      * Selects the specified color in the palette (fires the select event)
10011      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10012      */
10013     select : function(color){
10014         color = color.replace("#", "");
10015         if(color != this.value || this.allowReselect){
10016             var el = this.el;
10017             if(this.value){
10018                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10019             }
10020             el.child("a.color-"+color).addClass("x-color-palette-sel");
10021             this.value = color;
10022             this.fireEvent("select", this, color);
10023         }
10024     }
10025 });/*
10026  * Based on:
10027  * Ext JS Library 1.1.1
10028  * Copyright(c) 2006-2007, Ext JS, LLC.
10029  *
10030  * Originally Released Under LGPL - original licence link has changed is not relivant.
10031  *
10032  * Fork - LGPL
10033  * <script type="text/javascript">
10034  */
10035  
10036 /**
10037  * @class Roo.DatePicker
10038  * @extends Roo.Component
10039  * Simple date picker class.
10040  * @constructor
10041  * Create a new DatePicker
10042  * @param {Object} config The config object
10043  */
10044 Roo.DatePicker = function(config){
10045     Roo.DatePicker.superclass.constructor.call(this, config);
10046
10047     this.value = config && config.value ?
10048                  config.value.clearTime() : new Date().clearTime();
10049
10050     this.addEvents({
10051         /**
10052              * @event select
10053              * Fires when a date is selected
10054              * @param {DatePicker} this
10055              * @param {Date} date The selected date
10056              */
10057         select: true
10058     });
10059
10060     if(this.handler){
10061         this.on("select", this.handler,  this.scope || this);
10062     }
10063     // build the disabledDatesRE
10064     if(!this.disabledDatesRE && this.disabledDates){
10065         var dd = this.disabledDates;
10066         var re = "(?:";
10067         for(var i = 0; i < dd.length; i++){
10068             re += dd[i];
10069             if(i != dd.length-1) re += "|";
10070         }
10071         this.disabledDatesRE = new RegExp(re + ")");
10072     }
10073 };
10074
10075 Roo.extend(Roo.DatePicker, Roo.Component, {
10076     /**
10077      * @cfg {String} todayText
10078      * The text to display on the button that selects the current date (defaults to "Today")
10079      */
10080     todayText : "Today",
10081     /**
10082      * @cfg {String} okText
10083      * The text to display on the ok button
10084      */
10085     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10086     /**
10087      * @cfg {String} cancelText
10088      * The text to display on the cancel button
10089      */
10090     cancelText : "Cancel",
10091     /**
10092      * @cfg {String} todayTip
10093      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10094      */
10095     todayTip : "{0} (Spacebar)",
10096     /**
10097      * @cfg {Date} minDate
10098      * Minimum allowable date (JavaScript date object, defaults to null)
10099      */
10100     minDate : null,
10101     /**
10102      * @cfg {Date} maxDate
10103      * Maximum allowable date (JavaScript date object, defaults to null)
10104      */
10105     maxDate : null,
10106     /**
10107      * @cfg {String} minText
10108      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10109      */
10110     minText : "This date is before the minimum date",
10111     /**
10112      * @cfg {String} maxText
10113      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10114      */
10115     maxText : "This date is after the maximum date",
10116     /**
10117      * @cfg {String} format
10118      * The default date format string which can be overriden for localization support.  The format must be
10119      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10120      */
10121     format : "m/d/y",
10122     /**
10123      * @cfg {Array} disabledDays
10124      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10125      */
10126     disabledDays : null,
10127     /**
10128      * @cfg {String} disabledDaysText
10129      * The tooltip to display when the date falls on a disabled day (defaults to "")
10130      */
10131     disabledDaysText : "",
10132     /**
10133      * @cfg {RegExp} disabledDatesRE
10134      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10135      */
10136     disabledDatesRE : null,
10137     /**
10138      * @cfg {String} disabledDatesText
10139      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10140      */
10141     disabledDatesText : "",
10142     /**
10143      * @cfg {Boolean} constrainToViewport
10144      * True to constrain the date picker to the viewport (defaults to true)
10145      */
10146     constrainToViewport : true,
10147     /**
10148      * @cfg {Array} monthNames
10149      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10150      */
10151     monthNames : Date.monthNames,
10152     /**
10153      * @cfg {Array} dayNames
10154      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10155      */
10156     dayNames : Date.dayNames,
10157     /**
10158      * @cfg {String} nextText
10159      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10160      */
10161     nextText: 'Next Month (Control+Right)',
10162     /**
10163      * @cfg {String} prevText
10164      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10165      */
10166     prevText: 'Previous Month (Control+Left)',
10167     /**
10168      * @cfg {String} monthYearText
10169      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10170      */
10171     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10172     /**
10173      * @cfg {Number} startDay
10174      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10175      */
10176     startDay : 0,
10177     /**
10178      * @cfg {Bool} showClear
10179      * Show a clear button (usefull for date form elements that can be blank.)
10180      */
10181     
10182     showClear: false,
10183     
10184     /**
10185      * Sets the value of the date field
10186      * @param {Date} value The date to set
10187      */
10188     setValue : function(value){
10189         var old = this.value;
10190         this.value = value.clearTime(true);
10191         if(this.el){
10192             this.update(this.value);
10193         }
10194     },
10195
10196     /**
10197      * Gets the current selected value of the date field
10198      * @return {Date} The selected date
10199      */
10200     getValue : function(){
10201         return this.value;
10202     },
10203
10204     // private
10205     focus : function(){
10206         if(this.el){
10207             this.update(this.activeDate);
10208         }
10209     },
10210
10211     // private
10212     onRender : function(container, position){
10213         var m = [
10214              '<table cellspacing="0">',
10215                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10216                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10217         var dn = this.dayNames;
10218         for(var i = 0; i < 7; i++){
10219             var d = this.startDay+i;
10220             if(d > 6){
10221                 d = d-7;
10222             }
10223             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10224         }
10225         m[m.length] = "</tr></thead><tbody><tr>";
10226         for(var i = 0; i < 42; i++) {
10227             if(i % 7 == 0 && i != 0){
10228                 m[m.length] = "</tr><tr>";
10229             }
10230             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10231         }
10232         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10233             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10234
10235         var el = document.createElement("div");
10236         el.className = "x-date-picker";
10237         el.innerHTML = m.join("");
10238
10239         container.dom.insertBefore(el, position);
10240
10241         this.el = Roo.get(el);
10242         this.eventEl = Roo.get(el.firstChild);
10243
10244         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10245             handler: this.showPrevMonth,
10246             scope: this,
10247             preventDefault:true,
10248             stopDefault:true
10249         });
10250
10251         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10252             handler: this.showNextMonth,
10253             scope: this,
10254             preventDefault:true,
10255             stopDefault:true
10256         });
10257
10258         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10259
10260         this.monthPicker = this.el.down('div.x-date-mp');
10261         this.monthPicker.enableDisplayMode('block');
10262         
10263         var kn = new Roo.KeyNav(this.eventEl, {
10264             "left" : function(e){
10265                 e.ctrlKey ?
10266                     this.showPrevMonth() :
10267                     this.update(this.activeDate.add("d", -1));
10268             },
10269
10270             "right" : function(e){
10271                 e.ctrlKey ?
10272                     this.showNextMonth() :
10273                     this.update(this.activeDate.add("d", 1));
10274             },
10275
10276             "up" : function(e){
10277                 e.ctrlKey ?
10278                     this.showNextYear() :
10279                     this.update(this.activeDate.add("d", -7));
10280             },
10281
10282             "down" : function(e){
10283                 e.ctrlKey ?
10284                     this.showPrevYear() :
10285                     this.update(this.activeDate.add("d", 7));
10286             },
10287
10288             "pageUp" : function(e){
10289                 this.showNextMonth();
10290             },
10291
10292             "pageDown" : function(e){
10293                 this.showPrevMonth();
10294             },
10295
10296             "enter" : function(e){
10297                 e.stopPropagation();
10298                 return true;
10299             },
10300
10301             scope : this
10302         });
10303
10304         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10305
10306         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10307
10308         this.el.unselectable();
10309         
10310         this.cells = this.el.select("table.x-date-inner tbody td");
10311         this.textNodes = this.el.query("table.x-date-inner tbody span");
10312
10313         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10314             text: "&#160;",
10315             tooltip: this.monthYearText
10316         });
10317
10318         this.mbtn.on('click', this.showMonthPicker, this);
10319         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10320
10321
10322         var today = (new Date()).dateFormat(this.format);
10323         
10324         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10325         if (this.showClear) {
10326             baseTb.add( new Roo.Toolbar.Fill());
10327         }
10328         baseTb.add({
10329             text: String.format(this.todayText, today),
10330             tooltip: String.format(this.todayTip, today),
10331             handler: this.selectToday,
10332             scope: this
10333         });
10334         
10335         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10336             
10337         //});
10338         if (this.showClear) {
10339             
10340             baseTb.add( new Roo.Toolbar.Fill());
10341             baseTb.add({
10342                 text: '&#160;',
10343                 cls: 'x-btn-icon x-btn-clear',
10344                 handler: function() {
10345                     //this.value = '';
10346                     this.fireEvent("select", this, '');
10347                 },
10348                 scope: this
10349             });
10350         }
10351         
10352         
10353         if(Roo.isIE){
10354             this.el.repaint();
10355         }
10356         this.update(this.value);
10357     },
10358
10359     createMonthPicker : function(){
10360         if(!this.monthPicker.dom.firstChild){
10361             var buf = ['<table border="0" cellspacing="0">'];
10362             for(var i = 0; i < 6; i++){
10363                 buf.push(
10364                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10365                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10366                     i == 0 ?
10367                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10368                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10369                 );
10370             }
10371             buf.push(
10372                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10373                     this.okText,
10374                     '</button><button type="button" class="x-date-mp-cancel">',
10375                     this.cancelText,
10376                     '</button></td></tr>',
10377                 '</table>'
10378             );
10379             this.monthPicker.update(buf.join(''));
10380             this.monthPicker.on('click', this.onMonthClick, this);
10381             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10382
10383             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10384             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10385
10386             this.mpMonths.each(function(m, a, i){
10387                 i += 1;
10388                 if((i%2) == 0){
10389                     m.dom.xmonth = 5 + Math.round(i * .5);
10390                 }else{
10391                     m.dom.xmonth = Math.round((i-1) * .5);
10392                 }
10393             });
10394         }
10395     },
10396
10397     showMonthPicker : function(){
10398         this.createMonthPicker();
10399         var size = this.el.getSize();
10400         this.monthPicker.setSize(size);
10401         this.monthPicker.child('table').setSize(size);
10402
10403         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10404         this.updateMPMonth(this.mpSelMonth);
10405         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10406         this.updateMPYear(this.mpSelYear);
10407
10408         this.monthPicker.slideIn('t', {duration:.2});
10409     },
10410
10411     updateMPYear : function(y){
10412         this.mpyear = y;
10413         var ys = this.mpYears.elements;
10414         for(var i = 1; i <= 10; i++){
10415             var td = ys[i-1], y2;
10416             if((i%2) == 0){
10417                 y2 = y + Math.round(i * .5);
10418                 td.firstChild.innerHTML = y2;
10419                 td.xyear = y2;
10420             }else{
10421                 y2 = y - (5-Math.round(i * .5));
10422                 td.firstChild.innerHTML = y2;
10423                 td.xyear = y2;
10424             }
10425             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10426         }
10427     },
10428
10429     updateMPMonth : function(sm){
10430         this.mpMonths.each(function(m, a, i){
10431             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10432         });
10433     },
10434
10435     selectMPMonth: function(m){
10436         
10437     },
10438
10439     onMonthClick : function(e, t){
10440         e.stopEvent();
10441         var el = new Roo.Element(t), pn;
10442         if(el.is('button.x-date-mp-cancel')){
10443             this.hideMonthPicker();
10444         }
10445         else if(el.is('button.x-date-mp-ok')){
10446             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10447             this.hideMonthPicker();
10448         }
10449         else if(pn = el.up('td.x-date-mp-month', 2)){
10450             this.mpMonths.removeClass('x-date-mp-sel');
10451             pn.addClass('x-date-mp-sel');
10452             this.mpSelMonth = pn.dom.xmonth;
10453         }
10454         else if(pn = el.up('td.x-date-mp-year', 2)){
10455             this.mpYears.removeClass('x-date-mp-sel');
10456             pn.addClass('x-date-mp-sel');
10457             this.mpSelYear = pn.dom.xyear;
10458         }
10459         else if(el.is('a.x-date-mp-prev')){
10460             this.updateMPYear(this.mpyear-10);
10461         }
10462         else if(el.is('a.x-date-mp-next')){
10463             this.updateMPYear(this.mpyear+10);
10464         }
10465     },
10466
10467     onMonthDblClick : function(e, t){
10468         e.stopEvent();
10469         var el = new Roo.Element(t), pn;
10470         if(pn = el.up('td.x-date-mp-month', 2)){
10471             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10472             this.hideMonthPicker();
10473         }
10474         else if(pn = el.up('td.x-date-mp-year', 2)){
10475             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10476             this.hideMonthPicker();
10477         }
10478     },
10479
10480     hideMonthPicker : function(disableAnim){
10481         if(this.monthPicker){
10482             if(disableAnim === true){
10483                 this.monthPicker.hide();
10484             }else{
10485                 this.monthPicker.slideOut('t', {duration:.2});
10486             }
10487         }
10488     },
10489
10490     // private
10491     showPrevMonth : function(e){
10492         this.update(this.activeDate.add("mo", -1));
10493     },
10494
10495     // private
10496     showNextMonth : function(e){
10497         this.update(this.activeDate.add("mo", 1));
10498     },
10499
10500     // private
10501     showPrevYear : function(){
10502         this.update(this.activeDate.add("y", -1));
10503     },
10504
10505     // private
10506     showNextYear : function(){
10507         this.update(this.activeDate.add("y", 1));
10508     },
10509
10510     // private
10511     handleMouseWheel : function(e){
10512         var delta = e.getWheelDelta();
10513         if(delta > 0){
10514             this.showPrevMonth();
10515             e.stopEvent();
10516         } else if(delta < 0){
10517             this.showNextMonth();
10518             e.stopEvent();
10519         }
10520     },
10521
10522     // private
10523     handleDateClick : function(e, t){
10524         e.stopEvent();
10525         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10526             this.setValue(new Date(t.dateValue));
10527             this.fireEvent("select", this, this.value);
10528         }
10529     },
10530
10531     // private
10532     selectToday : function(){
10533         this.setValue(new Date().clearTime());
10534         this.fireEvent("select", this, this.value);
10535     },
10536
10537     // private
10538     update : function(date){
10539         var vd = this.activeDate;
10540         this.activeDate = date;
10541         if(vd && this.el){
10542             var t = date.getTime();
10543             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10544                 this.cells.removeClass("x-date-selected");
10545                 this.cells.each(function(c){
10546                    if(c.dom.firstChild.dateValue == t){
10547                        c.addClass("x-date-selected");
10548                        setTimeout(function(){
10549                             try{c.dom.firstChild.focus();}catch(e){}
10550                        }, 50);
10551                        return false;
10552                    }
10553                 });
10554                 return;
10555             }
10556         }
10557         var days = date.getDaysInMonth();
10558         var firstOfMonth = date.getFirstDateOfMonth();
10559         var startingPos = firstOfMonth.getDay()-this.startDay;
10560
10561         if(startingPos <= this.startDay){
10562             startingPos += 7;
10563         }
10564
10565         var pm = date.add("mo", -1);
10566         var prevStart = pm.getDaysInMonth()-startingPos;
10567
10568         var cells = this.cells.elements;
10569         var textEls = this.textNodes;
10570         days += startingPos;
10571
10572         // convert everything to numbers so it's fast
10573         var day = 86400000;
10574         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10575         var today = new Date().clearTime().getTime();
10576         var sel = date.clearTime().getTime();
10577         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10578         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10579         var ddMatch = this.disabledDatesRE;
10580         var ddText = this.disabledDatesText;
10581         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10582         var ddaysText = this.disabledDaysText;
10583         var format = this.format;
10584
10585         var setCellClass = function(cal, cell){
10586             cell.title = "";
10587             var t = d.getTime();
10588             cell.firstChild.dateValue = t;
10589             if(t == today){
10590                 cell.className += " x-date-today";
10591                 cell.title = cal.todayText;
10592             }
10593             if(t == sel){
10594                 cell.className += " x-date-selected";
10595                 setTimeout(function(){
10596                     try{cell.firstChild.focus();}catch(e){}
10597                 }, 50);
10598             }
10599             // disabling
10600             if(t < min) {
10601                 cell.className = " x-date-disabled";
10602                 cell.title = cal.minText;
10603                 return;
10604             }
10605             if(t > max) {
10606                 cell.className = " x-date-disabled";
10607                 cell.title = cal.maxText;
10608                 return;
10609             }
10610             if(ddays){
10611                 if(ddays.indexOf(d.getDay()) != -1){
10612                     cell.title = ddaysText;
10613                     cell.className = " x-date-disabled";
10614                 }
10615             }
10616             if(ddMatch && format){
10617                 var fvalue = d.dateFormat(format);
10618                 if(ddMatch.test(fvalue)){
10619                     cell.title = ddText.replace("%0", fvalue);
10620                     cell.className = " x-date-disabled";
10621                 }
10622             }
10623         };
10624
10625         var i = 0;
10626         for(; i < startingPos; i++) {
10627             textEls[i].innerHTML = (++prevStart);
10628             d.setDate(d.getDate()+1);
10629             cells[i].className = "x-date-prevday";
10630             setCellClass(this, cells[i]);
10631         }
10632         for(; i < days; i++){
10633             intDay = i - startingPos + 1;
10634             textEls[i].innerHTML = (intDay);
10635             d.setDate(d.getDate()+1);
10636             cells[i].className = "x-date-active";
10637             setCellClass(this, cells[i]);
10638         }
10639         var extraDays = 0;
10640         for(; i < 42; i++) {
10641              textEls[i].innerHTML = (++extraDays);
10642              d.setDate(d.getDate()+1);
10643              cells[i].className = "x-date-nextday";
10644              setCellClass(this, cells[i]);
10645         }
10646
10647         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10648
10649         if(!this.internalRender){
10650             var main = this.el.dom.firstChild;
10651             var w = main.offsetWidth;
10652             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10653             Roo.fly(main).setWidth(w);
10654             this.internalRender = true;
10655             // opera does not respect the auto grow header center column
10656             // then, after it gets a width opera refuses to recalculate
10657             // without a second pass
10658             if(Roo.isOpera && !this.secondPass){
10659                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10660                 this.secondPass = true;
10661                 this.update.defer(10, this, [date]);
10662             }
10663         }
10664     }
10665 });/*
10666  * Based on:
10667  * Ext JS Library 1.1.1
10668  * Copyright(c) 2006-2007, Ext JS, LLC.
10669  *
10670  * Originally Released Under LGPL - original licence link has changed is not relivant.
10671  *
10672  * Fork - LGPL
10673  * <script type="text/javascript">
10674  */
10675 /**
10676  * @class Roo.TabPanel
10677  * @extends Roo.util.Observable
10678  * A lightweight tab container.
10679  * <br><br>
10680  * Usage:
10681  * <pre><code>
10682 // basic tabs 1, built from existing content
10683 var tabs = new Roo.TabPanel("tabs1");
10684 tabs.addTab("script", "View Script");
10685 tabs.addTab("markup", "View Markup");
10686 tabs.activate("script");
10687
10688 // more advanced tabs, built from javascript
10689 var jtabs = new Roo.TabPanel("jtabs");
10690 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10691
10692 // set up the UpdateManager
10693 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10694 var updater = tab2.getUpdateManager();
10695 updater.setDefaultUrl("ajax1.htm");
10696 tab2.on('activate', updater.refresh, updater, true);
10697
10698 // Use setUrl for Ajax loading
10699 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10700 tab3.setUrl("ajax2.htm", null, true);
10701
10702 // Disabled tab
10703 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10704 tab4.disable();
10705
10706 jtabs.activate("jtabs-1");
10707  * </code></pre>
10708  * @constructor
10709  * Create a new TabPanel.
10710  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10711  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10712  */
10713 Roo.TabPanel = function(container, config){
10714     /**
10715     * The container element for this TabPanel.
10716     * @type Roo.Element
10717     */
10718     this.el = Roo.get(container, true);
10719     if(config){
10720         if(typeof config == "boolean"){
10721             this.tabPosition = config ? "bottom" : "top";
10722         }else{
10723             Roo.apply(this, config);
10724         }
10725     }
10726     if(this.tabPosition == "bottom"){
10727         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10728         this.el.addClass("x-tabs-bottom");
10729     }
10730     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10731     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10732     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10733     if(Roo.isIE){
10734         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10735     }
10736     if(this.tabPosition != "bottom"){
10737     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10738      * @type Roo.Element
10739      */
10740       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10741       this.el.addClass("x-tabs-top");
10742     }
10743     this.items = [];
10744
10745     this.bodyEl.setStyle("position", "relative");
10746
10747     this.active = null;
10748     this.activateDelegate = this.activate.createDelegate(this);
10749
10750     this.addEvents({
10751         /**
10752          * @event tabchange
10753          * Fires when the active tab changes
10754          * @param {Roo.TabPanel} this
10755          * @param {Roo.TabPanelItem} activePanel The new active tab
10756          */
10757         "tabchange": true,
10758         /**
10759          * @event beforetabchange
10760          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10761          * @param {Roo.TabPanel} this
10762          * @param {Object} e Set cancel to true on this object to cancel the tab change
10763          * @param {Roo.TabPanelItem} tab The tab being changed to
10764          */
10765         "beforetabchange" : true
10766     });
10767
10768     Roo.EventManager.onWindowResize(this.onResize, this);
10769     this.cpad = this.el.getPadding("lr");
10770     this.hiddenCount = 0;
10771
10772     Roo.TabPanel.superclass.constructor.call(this);
10773 };
10774
10775 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10776         /*
10777          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10778          */
10779     tabPosition : "top",
10780         /*
10781          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10782          */
10783     currentTabWidth : 0,
10784         /*
10785          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10786          */
10787     minTabWidth : 40,
10788         /*
10789          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10790          */
10791     maxTabWidth : 250,
10792         /*
10793          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10794          */
10795     preferredTabWidth : 175,
10796         /*
10797          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10798          */
10799     resizeTabs : false,
10800         /*
10801          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10802          */
10803     monitorResize : true,
10804
10805     /**
10806      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10807      * @param {String} id The id of the div to use <b>or create</b>
10808      * @param {String} text The text for the tab
10809      * @param {String} content (optional) Content to put in the TabPanelItem body
10810      * @param {Boolean} closable (optional) True to create a close icon on the tab
10811      * @return {Roo.TabPanelItem} The created TabPanelItem
10812      */
10813     addTab : function(id, text, content, closable){
10814         var item = new Roo.TabPanelItem(this, id, text, closable);
10815         this.addTabItem(item);
10816         if(content){
10817             item.setContent(content);
10818         }
10819         return item;
10820     },
10821
10822     /**
10823      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10824      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10825      * @return {Roo.TabPanelItem}
10826      */
10827     getTab : function(id){
10828         return this.items[id];
10829     },
10830
10831     /**
10832      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10833      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10834      */
10835     hideTab : function(id){
10836         var t = this.items[id];
10837         if(!t.isHidden()){
10838            t.setHidden(true);
10839            this.hiddenCount++;
10840            this.autoSizeTabs();
10841         }
10842     },
10843
10844     /**
10845      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10846      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10847      */
10848     unhideTab : function(id){
10849         var t = this.items[id];
10850         if(t.isHidden()){
10851            t.setHidden(false);
10852            this.hiddenCount--;
10853            this.autoSizeTabs();
10854         }
10855     },
10856
10857     /**
10858      * Adds an existing {@link Roo.TabPanelItem}.
10859      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10860      */
10861     addTabItem : function(item){
10862         this.items[item.id] = item;
10863         this.items.push(item);
10864         if(this.resizeTabs){
10865            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10866            this.autoSizeTabs();
10867         }else{
10868             item.autoSize();
10869         }
10870     },
10871
10872     /**
10873      * Removes a {@link Roo.TabPanelItem}.
10874      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10875      */
10876     removeTab : function(id){
10877         var items = this.items;
10878         var tab = items[id];
10879         if(!tab) { return; }
10880         var index = items.indexOf(tab);
10881         if(this.active == tab && items.length > 1){
10882             var newTab = this.getNextAvailable(index);
10883             if(newTab) {
10884                 newTab.activate();
10885             }
10886         }
10887         this.stripEl.dom.removeChild(tab.pnode.dom);
10888         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10889             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10890         }
10891         items.splice(index, 1);
10892         delete this.items[tab.id];
10893         tab.fireEvent("close", tab);
10894         tab.purgeListeners();
10895         this.autoSizeTabs();
10896     },
10897
10898     getNextAvailable : function(start){
10899         var items = this.items;
10900         var index = start;
10901         // look for a next tab that will slide over to
10902         // replace the one being removed
10903         while(index < items.length){
10904             var item = items[++index];
10905             if(item && !item.isHidden()){
10906                 return item;
10907             }
10908         }
10909         // if one isn't found select the previous tab (on the left)
10910         index = start;
10911         while(index >= 0){
10912             var item = items[--index];
10913             if(item && !item.isHidden()){
10914                 return item;
10915             }
10916         }
10917         return null;
10918     },
10919
10920     /**
10921      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10922      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10923      */
10924     disableTab : function(id){
10925         var tab = this.items[id];
10926         if(tab && this.active != tab){
10927             tab.disable();
10928         }
10929     },
10930
10931     /**
10932      * Enables a {@link Roo.TabPanelItem} that is disabled.
10933      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10934      */
10935     enableTab : function(id){
10936         var tab = this.items[id];
10937         tab.enable();
10938     },
10939
10940     /**
10941      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10942      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10943      * @return {Roo.TabPanelItem} The TabPanelItem.
10944      */
10945     activate : function(id){
10946         var tab = this.items[id];
10947         if(!tab){
10948             return null;
10949         }
10950         if(tab == this.active || tab.disabled){
10951             return tab;
10952         }
10953         var e = {};
10954         this.fireEvent("beforetabchange", this, e, tab);
10955         if(e.cancel !== true && !tab.disabled){
10956             if(this.active){
10957                 this.active.hide();
10958             }
10959             this.active = this.items[id];
10960             this.active.show();
10961             this.fireEvent("tabchange", this, this.active);
10962         }
10963         return tab;
10964     },
10965
10966     /**
10967      * Gets the active {@link Roo.TabPanelItem}.
10968      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10969      */
10970     getActiveTab : function(){
10971         return this.active;
10972     },
10973
10974     /**
10975      * Updates the tab body element to fit the height of the container element
10976      * for overflow scrolling
10977      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10978      */
10979     syncHeight : function(targetHeight){
10980         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10981         var bm = this.bodyEl.getMargins();
10982         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10983         this.bodyEl.setHeight(newHeight);
10984         return newHeight;
10985     },
10986
10987     onResize : function(){
10988         if(this.monitorResize){
10989             this.autoSizeTabs();
10990         }
10991     },
10992
10993     /**
10994      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10995      */
10996     beginUpdate : function(){
10997         this.updating = true;
10998     },
10999
11000     /**
11001      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11002      */
11003     endUpdate : function(){
11004         this.updating = false;
11005         this.autoSizeTabs();
11006     },
11007
11008     /**
11009      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11010      */
11011     autoSizeTabs : function(){
11012         var count = this.items.length;
11013         var vcount = count - this.hiddenCount;
11014         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11015         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11016         var availWidth = Math.floor(w / vcount);
11017         var b = this.stripBody;
11018         if(b.getWidth() > w){
11019             var tabs = this.items;
11020             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11021             if(availWidth < this.minTabWidth){
11022                 /*if(!this.sleft){    // incomplete scrolling code
11023                     this.createScrollButtons();
11024                 }
11025                 this.showScroll();
11026                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11027             }
11028         }else{
11029             if(this.currentTabWidth < this.preferredTabWidth){
11030                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11031             }
11032         }
11033     },
11034
11035     /**
11036      * Returns the number of tabs in this TabPanel.
11037      * @return {Number}
11038      */
11039      getCount : function(){
11040          return this.items.length;
11041      },
11042
11043     /**
11044      * Resizes all the tabs to the passed width
11045      * @param {Number} The new width
11046      */
11047     setTabWidth : function(width){
11048         this.currentTabWidth = width;
11049         for(var i = 0, len = this.items.length; i < len; i++) {
11050                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11051         }
11052     },
11053
11054     /**
11055      * Destroys this TabPanel
11056      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11057      */
11058     destroy : function(removeEl){
11059         Roo.EventManager.removeResizeListener(this.onResize, this);
11060         for(var i = 0, len = this.items.length; i < len; i++){
11061             this.items[i].purgeListeners();
11062         }
11063         if(removeEl === true){
11064             this.el.update("");
11065             this.el.remove();
11066         }
11067     }
11068 });
11069
11070 /**
11071  * @class Roo.TabPanelItem
11072  * @extends Roo.util.Observable
11073  * Represents an individual item (tab plus body) in a TabPanel.
11074  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11075  * @param {String} id The id of this TabPanelItem
11076  * @param {String} text The text for the tab of this TabPanelItem
11077  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11078  */
11079 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11080     /**
11081      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11082      * @type Roo.TabPanel
11083      */
11084     this.tabPanel = tabPanel;
11085     /**
11086      * The id for this TabPanelItem
11087      * @type String
11088      */
11089     this.id = id;
11090     /** @private */
11091     this.disabled = false;
11092     /** @private */
11093     this.text = text;
11094     /** @private */
11095     this.loaded = false;
11096     this.closable = closable;
11097
11098     /**
11099      * The body element for this TabPanelItem.
11100      * @type Roo.Element
11101      */
11102     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11103     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11104     this.bodyEl.setStyle("display", "block");
11105     this.bodyEl.setStyle("zoom", "1");
11106     this.hideAction();
11107
11108     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11109     /** @private */
11110     this.el = Roo.get(els.el, true);
11111     this.inner = Roo.get(els.inner, true);
11112     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11113     this.pnode = Roo.get(els.el.parentNode, true);
11114     this.el.on("mousedown", this.onTabMouseDown, this);
11115     this.el.on("click", this.onTabClick, this);
11116     /** @private */
11117     if(closable){
11118         var c = Roo.get(els.close, true);
11119         c.dom.title = this.closeText;
11120         c.addClassOnOver("close-over");
11121         c.on("click", this.closeClick, this);
11122      }
11123
11124     this.addEvents({
11125          /**
11126          * @event activate
11127          * Fires when this tab becomes the active tab.
11128          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11129          * @param {Roo.TabPanelItem} this
11130          */
11131         "activate": true,
11132         /**
11133          * @event beforeclose
11134          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11135          * @param {Roo.TabPanelItem} this
11136          * @param {Object} e Set cancel to true on this object to cancel the close.
11137          */
11138         "beforeclose": true,
11139         /**
11140          * @event close
11141          * Fires when this tab is closed.
11142          * @param {Roo.TabPanelItem} this
11143          */
11144          "close": true,
11145         /**
11146          * @event deactivate
11147          * Fires when this tab is no longer the active tab.
11148          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11149          * @param {Roo.TabPanelItem} this
11150          */
11151          "deactivate" : true
11152     });
11153     this.hidden = false;
11154
11155     Roo.TabPanelItem.superclass.constructor.call(this);
11156 };
11157
11158 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11159     purgeListeners : function(){
11160        Roo.util.Observable.prototype.purgeListeners.call(this);
11161        this.el.removeAllListeners();
11162     },
11163     /**
11164      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11165      */
11166     show : function(){
11167         this.pnode.addClass("on");
11168         this.showAction();
11169         if(Roo.isOpera){
11170             this.tabPanel.stripWrap.repaint();
11171         }
11172         this.fireEvent("activate", this.tabPanel, this);
11173     },
11174
11175     /**
11176      * Returns true if this tab is the active tab.
11177      * @return {Boolean}
11178      */
11179     isActive : function(){
11180         return this.tabPanel.getActiveTab() == this;
11181     },
11182
11183     /**
11184      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11185      */
11186     hide : function(){
11187         this.pnode.removeClass("on");
11188         this.hideAction();
11189         this.fireEvent("deactivate", this.tabPanel, this);
11190     },
11191
11192     hideAction : function(){
11193         this.bodyEl.hide();
11194         this.bodyEl.setStyle("position", "absolute");
11195         this.bodyEl.setLeft("-20000px");
11196         this.bodyEl.setTop("-20000px");
11197     },
11198
11199     showAction : function(){
11200         this.bodyEl.setStyle("position", "relative");
11201         this.bodyEl.setTop("");
11202         this.bodyEl.setLeft("");
11203         this.bodyEl.show();
11204     },
11205
11206     /**
11207      * Set the tooltip for the tab.
11208      * @param {String} tooltip The tab's tooltip
11209      */
11210     setTooltip : function(text){
11211         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11212             this.textEl.dom.qtip = text;
11213             this.textEl.dom.removeAttribute('title');
11214         }else{
11215             this.textEl.dom.title = text;
11216         }
11217     },
11218
11219     onTabClick : function(e){
11220         e.preventDefault();
11221         this.tabPanel.activate(this.id);
11222     },
11223
11224     onTabMouseDown : function(e){
11225         e.preventDefault();
11226         this.tabPanel.activate(this.id);
11227     },
11228
11229     getWidth : function(){
11230         return this.inner.getWidth();
11231     },
11232
11233     setWidth : function(width){
11234         var iwidth = width - this.pnode.getPadding("lr");
11235         this.inner.setWidth(iwidth);
11236         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11237         this.pnode.setWidth(width);
11238     },
11239
11240     /**
11241      * Show or hide the tab
11242      * @param {Boolean} hidden True to hide or false to show.
11243      */
11244     setHidden : function(hidden){
11245         this.hidden = hidden;
11246         this.pnode.setStyle("display", hidden ? "none" : "");
11247     },
11248
11249     /**
11250      * Returns true if this tab is "hidden"
11251      * @return {Boolean}
11252      */
11253     isHidden : function(){
11254         return this.hidden;
11255     },
11256
11257     /**
11258      * Returns the text for this tab
11259      * @return {String}
11260      */
11261     getText : function(){
11262         return this.text;
11263     },
11264
11265     autoSize : function(){
11266         //this.el.beginMeasure();
11267         this.textEl.setWidth(1);
11268         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11269         //this.el.endMeasure();
11270     },
11271
11272     /**
11273      * Sets the text for the tab (Note: this also sets the tooltip text)
11274      * @param {String} text The tab's text and tooltip
11275      */
11276     setText : function(text){
11277         this.text = text;
11278         this.textEl.update(text);
11279         this.setTooltip(text);
11280         if(!this.tabPanel.resizeTabs){
11281             this.autoSize();
11282         }
11283     },
11284     /**
11285      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11286      */
11287     activate : function(){
11288         this.tabPanel.activate(this.id);
11289     },
11290
11291     /**
11292      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11293      */
11294     disable : function(){
11295         if(this.tabPanel.active != this){
11296             this.disabled = true;
11297             this.pnode.addClass("disabled");
11298         }
11299     },
11300
11301     /**
11302      * Enables this TabPanelItem if it was previously disabled.
11303      */
11304     enable : function(){
11305         this.disabled = false;
11306         this.pnode.removeClass("disabled");
11307     },
11308
11309     /**
11310      * Sets the content for this TabPanelItem.
11311      * @param {String} content The content
11312      * @param {Boolean} loadScripts true to look for and load scripts
11313      */
11314     setContent : function(content, loadScripts){
11315         this.bodyEl.update(content, loadScripts);
11316     },
11317
11318     /**
11319      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11320      * @return {Roo.UpdateManager} The UpdateManager
11321      */
11322     getUpdateManager : function(){
11323         return this.bodyEl.getUpdateManager();
11324     },
11325
11326     /**
11327      * Set a URL to be used to load the content for this TabPanelItem.
11328      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11329      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11330      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11331      * @return {Roo.UpdateManager} The UpdateManager
11332      */
11333     setUrl : function(url, params, loadOnce){
11334         if(this.refreshDelegate){
11335             this.un('activate', this.refreshDelegate);
11336         }
11337         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11338         this.on("activate", this.refreshDelegate);
11339         return this.bodyEl.getUpdateManager();
11340     },
11341
11342     /** @private */
11343     _handleRefresh : function(url, params, loadOnce){
11344         if(!loadOnce || !this.loaded){
11345             var updater = this.bodyEl.getUpdateManager();
11346             updater.update(url, params, this._setLoaded.createDelegate(this));
11347         }
11348     },
11349
11350     /**
11351      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11352      *   Will fail silently if the setUrl method has not been called.
11353      *   This does not activate the panel, just updates its content.
11354      */
11355     refresh : function(){
11356         if(this.refreshDelegate){
11357            this.loaded = false;
11358            this.refreshDelegate();
11359         }
11360     },
11361
11362     /** @private */
11363     _setLoaded : function(){
11364         this.loaded = true;
11365     },
11366
11367     /** @private */
11368     closeClick : function(e){
11369         var o = {};
11370         e.stopEvent();
11371         this.fireEvent("beforeclose", this, o);
11372         if(o.cancel !== true){
11373             this.tabPanel.removeTab(this.id);
11374         }
11375     },
11376     /**
11377      * The text displayed in the tooltip for the close icon.
11378      * @type String
11379      */
11380     closeText : "Close this tab"
11381 });
11382
11383 /** @private */
11384 Roo.TabPanel.prototype.createStrip = function(container){
11385     var strip = document.createElement("div");
11386     strip.className = "x-tabs-wrap";
11387     container.appendChild(strip);
11388     return strip;
11389 };
11390 /** @private */
11391 Roo.TabPanel.prototype.createStripList = function(strip){
11392     // div wrapper for retard IE
11393     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11394     return strip.firstChild.firstChild.firstChild.firstChild;
11395 };
11396 /** @private */
11397 Roo.TabPanel.prototype.createBody = function(container){
11398     var body = document.createElement("div");
11399     Roo.id(body, "tab-body");
11400     Roo.fly(body).addClass("x-tabs-body");
11401     container.appendChild(body);
11402     return body;
11403 };
11404 /** @private */
11405 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11406     var body = Roo.getDom(id);
11407     if(!body){
11408         body = document.createElement("div");
11409         body.id = id;
11410     }
11411     Roo.fly(body).addClass("x-tabs-item-body");
11412     bodyEl.insertBefore(body, bodyEl.firstChild);
11413     return body;
11414 };
11415 /** @private */
11416 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11417     var td = document.createElement("td");
11418     stripEl.appendChild(td);
11419     if(closable){
11420         td.className = "x-tabs-closable";
11421         if(!this.closeTpl){
11422             this.closeTpl = new Roo.Template(
11423                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11424                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11425                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11426             );
11427         }
11428         var el = this.closeTpl.overwrite(td, {"text": text});
11429         var close = el.getElementsByTagName("div")[0];
11430         var inner = el.getElementsByTagName("em")[0];
11431         return {"el": el, "close": close, "inner": inner};
11432     } else {
11433         if(!this.tabTpl){
11434             this.tabTpl = new Roo.Template(
11435                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11436                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11437             );
11438         }
11439         var el = this.tabTpl.overwrite(td, {"text": text});
11440         var inner = el.getElementsByTagName("em")[0];
11441         return {"el": el, "inner": inner};
11442     }
11443 };/*
11444  * Based on:
11445  * Ext JS Library 1.1.1
11446  * Copyright(c) 2006-2007, Ext JS, LLC.
11447  *
11448  * Originally Released Under LGPL - original licence link has changed is not relivant.
11449  *
11450  * Fork - LGPL
11451  * <script type="text/javascript">
11452  */
11453
11454 /**
11455  * @class Roo.Button
11456  * @extends Roo.util.Observable
11457  * Simple Button class
11458  * @cfg {String} text The button text
11459  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11460  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11461  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11462  * @cfg {Object} scope The scope of the handler
11463  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11464  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11465  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11466  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11467  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11468  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11469    applies if enableToggle = true)
11470  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11471  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11472   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11473  * @constructor
11474  * Create a new button
11475  * @param {Object} config The config object
11476  */
11477 Roo.Button = function(renderTo, config)
11478 {
11479     if (!config) {
11480         config = renderTo;
11481         renderTo = config.renderTo || false;
11482     }
11483     
11484     Roo.apply(this, config);
11485     this.addEvents({
11486         /**
11487              * @event click
11488              * Fires when this button is clicked
11489              * @param {Button} this
11490              * @param {EventObject} e The click event
11491              */
11492             "click" : true,
11493         /**
11494              * @event toggle
11495              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11496              * @param {Button} this
11497              * @param {Boolean} pressed
11498              */
11499             "toggle" : true,
11500         /**
11501              * @event mouseover
11502              * Fires when the mouse hovers over the button
11503              * @param {Button} this
11504              * @param {Event} e The event object
11505              */
11506         'mouseover' : true,
11507         /**
11508              * @event mouseout
11509              * Fires when the mouse exits the button
11510              * @param {Button} this
11511              * @param {Event} e The event object
11512              */
11513         'mouseout': true,
11514          /**
11515              * @event render
11516              * Fires when the button is rendered
11517              * @param {Button} this
11518              */
11519         'render': true
11520     });
11521     if(this.menu){
11522         this.menu = Roo.menu.MenuMgr.get(this.menu);
11523     }
11524     // register listeners first!!  - so render can be captured..
11525     Roo.util.Observable.call(this);
11526     if(renderTo){
11527         this.render(renderTo);
11528     }
11529     
11530   
11531 };
11532
11533 Roo.extend(Roo.Button, Roo.util.Observable, {
11534     /**
11535      * 
11536      */
11537     
11538     /**
11539      * Read-only. True if this button is hidden
11540      * @type Boolean
11541      */
11542     hidden : false,
11543     /**
11544      * Read-only. True if this button is disabled
11545      * @type Boolean
11546      */
11547     disabled : false,
11548     /**
11549      * Read-only. True if this button is pressed (only if enableToggle = true)
11550      * @type Boolean
11551      */
11552     pressed : false,
11553
11554     /**
11555      * @cfg {Number} tabIndex 
11556      * The DOM tabIndex for this button (defaults to undefined)
11557      */
11558     tabIndex : undefined,
11559
11560     /**
11561      * @cfg {Boolean} enableToggle
11562      * True to enable pressed/not pressed toggling (defaults to false)
11563      */
11564     enableToggle: false,
11565     /**
11566      * @cfg {Mixed} menu
11567      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11568      */
11569     menu : undefined,
11570     /**
11571      * @cfg {String} menuAlign
11572      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11573      */
11574     menuAlign : "tl-bl?",
11575
11576     /**
11577      * @cfg {String} iconCls
11578      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11579      */
11580     iconCls : undefined,
11581     /**
11582      * @cfg {String} type
11583      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11584      */
11585     type : 'button',
11586
11587     // private
11588     menuClassTarget: 'tr',
11589
11590     /**
11591      * @cfg {String} clickEvent
11592      * The type of event to map to the button's event handler (defaults to 'click')
11593      */
11594     clickEvent : 'click',
11595
11596     /**
11597      * @cfg {Boolean} handleMouseEvents
11598      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11599      */
11600     handleMouseEvents : true,
11601
11602     /**
11603      * @cfg {String} tooltipType
11604      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11605      */
11606     tooltipType : 'qtip',
11607
11608     /**
11609      * @cfg {String} cls
11610      * A CSS class to apply to the button's main element.
11611      */
11612     
11613     /**
11614      * @cfg {Roo.Template} template (Optional)
11615      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11616      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11617      * require code modifications if required elements (e.g. a button) aren't present.
11618      */
11619
11620     // private
11621     render : function(renderTo){
11622         var btn;
11623         if(this.hideParent){
11624             this.parentEl = Roo.get(renderTo);
11625         }
11626         if(!this.dhconfig){
11627             if(!this.template){
11628                 if(!Roo.Button.buttonTemplate){
11629                     // hideous table template
11630                     Roo.Button.buttonTemplate = new Roo.Template(
11631                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11632                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11633                         "</tr></tbody></table>");
11634                 }
11635                 this.template = Roo.Button.buttonTemplate;
11636             }
11637             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11638             var btnEl = btn.child("button:first");
11639             btnEl.on('focus', this.onFocus, this);
11640             btnEl.on('blur', this.onBlur, this);
11641             if(this.cls){
11642                 btn.addClass(this.cls);
11643             }
11644             if(this.icon){
11645                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11646             }
11647             if(this.iconCls){
11648                 btnEl.addClass(this.iconCls);
11649                 if(!this.cls){
11650                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11651                 }
11652             }
11653             if(this.tabIndex !== undefined){
11654                 btnEl.dom.tabIndex = this.tabIndex;
11655             }
11656             if(this.tooltip){
11657                 if(typeof this.tooltip == 'object'){
11658                     Roo.QuickTips.tips(Roo.apply({
11659                           target: btnEl.id
11660                     }, this.tooltip));
11661                 } else {
11662                     btnEl.dom[this.tooltipType] = this.tooltip;
11663                 }
11664             }
11665         }else{
11666             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11667         }
11668         this.el = btn;
11669         if(this.id){
11670             this.el.dom.id = this.el.id = this.id;
11671         }
11672         if(this.menu){
11673             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11674             this.menu.on("show", this.onMenuShow, this);
11675             this.menu.on("hide", this.onMenuHide, this);
11676         }
11677         btn.addClass("x-btn");
11678         if(Roo.isIE && !Roo.isIE7){
11679             this.autoWidth.defer(1, this);
11680         }else{
11681             this.autoWidth();
11682         }
11683         if(this.handleMouseEvents){
11684             btn.on("mouseover", this.onMouseOver, this);
11685             btn.on("mouseout", this.onMouseOut, this);
11686             btn.on("mousedown", this.onMouseDown, this);
11687         }
11688         btn.on(this.clickEvent, this.onClick, this);
11689         //btn.on("mouseup", this.onMouseUp, this);
11690         if(this.hidden){
11691             this.hide();
11692         }
11693         if(this.disabled){
11694             this.disable();
11695         }
11696         Roo.ButtonToggleMgr.register(this);
11697         if(this.pressed){
11698             this.el.addClass("x-btn-pressed");
11699         }
11700         if(this.repeat){
11701             var repeater = new Roo.util.ClickRepeater(btn,
11702                 typeof this.repeat == "object" ? this.repeat : {}
11703             );
11704             repeater.on("click", this.onClick,  this);
11705         }
11706         
11707         this.fireEvent('render', this);
11708         
11709     },
11710     /**
11711      * Returns the button's underlying element
11712      * @return {Roo.Element} The element
11713      */
11714     getEl : function(){
11715         return this.el;  
11716     },
11717     
11718     /**
11719      * Destroys this Button and removes any listeners.
11720      */
11721     destroy : function(){
11722         Roo.ButtonToggleMgr.unregister(this);
11723         this.el.removeAllListeners();
11724         this.purgeListeners();
11725         this.el.remove();
11726     },
11727
11728     // private
11729     autoWidth : function(){
11730         if(this.el){
11731             this.el.setWidth("auto");
11732             if(Roo.isIE7 && Roo.isStrict){
11733                 var ib = this.el.child('button');
11734                 if(ib && ib.getWidth() > 20){
11735                     ib.clip();
11736                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11737                 }
11738             }
11739             if(this.minWidth){
11740                 if(this.hidden){
11741                     this.el.beginMeasure();
11742                 }
11743                 if(this.el.getWidth() < this.minWidth){
11744                     this.el.setWidth(this.minWidth);
11745                 }
11746                 if(this.hidden){
11747                     this.el.endMeasure();
11748                 }
11749             }
11750         }
11751     },
11752
11753     /**
11754      * Assigns this button's click handler
11755      * @param {Function} handler The function to call when the button is clicked
11756      * @param {Object} scope (optional) Scope for the function passed in
11757      */
11758     setHandler : function(handler, scope){
11759         this.handler = handler;
11760         this.scope = scope;  
11761     },
11762     
11763     /**
11764      * Sets this button's text
11765      * @param {String} text The button text
11766      */
11767     setText : function(text){
11768         this.text = text;
11769         if(this.el){
11770             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11771         }
11772         this.autoWidth();
11773     },
11774     
11775     /**
11776      * Gets the text for this button
11777      * @return {String} The button text
11778      */
11779     getText : function(){
11780         return this.text;  
11781     },
11782     
11783     /**
11784      * Show this button
11785      */
11786     show: function(){
11787         this.hidden = false;
11788         if(this.el){
11789             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11790         }
11791     },
11792     
11793     /**
11794      * Hide this button
11795      */
11796     hide: function(){
11797         this.hidden = true;
11798         if(this.el){
11799             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11800         }
11801     },
11802     
11803     /**
11804      * Convenience function for boolean show/hide
11805      * @param {Boolean} visible True to show, false to hide
11806      */
11807     setVisible: function(visible){
11808         if(visible) {
11809             this.show();
11810         }else{
11811             this.hide();
11812         }
11813     },
11814     
11815     /**
11816      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11817      * @param {Boolean} state (optional) Force a particular state
11818      */
11819     toggle : function(state){
11820         state = state === undefined ? !this.pressed : state;
11821         if(state != this.pressed){
11822             if(state){
11823                 this.el.addClass("x-btn-pressed");
11824                 this.pressed = true;
11825                 this.fireEvent("toggle", this, true);
11826             }else{
11827                 this.el.removeClass("x-btn-pressed");
11828                 this.pressed = false;
11829                 this.fireEvent("toggle", this, false);
11830             }
11831             if(this.toggleHandler){
11832                 this.toggleHandler.call(this.scope || this, this, state);
11833             }
11834         }
11835     },
11836     
11837     /**
11838      * Focus the button
11839      */
11840     focus : function(){
11841         this.el.child('button:first').focus();
11842     },
11843     
11844     /**
11845      * Disable this button
11846      */
11847     disable : function(){
11848         if(this.el){
11849             this.el.addClass("x-btn-disabled");
11850         }
11851         this.disabled = true;
11852     },
11853     
11854     /**
11855      * Enable this button
11856      */
11857     enable : function(){
11858         if(this.el){
11859             this.el.removeClass("x-btn-disabled");
11860         }
11861         this.disabled = false;
11862     },
11863
11864     /**
11865      * Convenience function for boolean enable/disable
11866      * @param {Boolean} enabled True to enable, false to disable
11867      */
11868     setDisabled : function(v){
11869         this[v !== true ? "enable" : "disable"]();
11870     },
11871
11872     // private
11873     onClick : function(e){
11874         if(e){
11875             e.preventDefault();
11876         }
11877         if(e.button != 0){
11878             return;
11879         }
11880         if(!this.disabled){
11881             if(this.enableToggle){
11882                 this.toggle();
11883             }
11884             if(this.menu && !this.menu.isVisible()){
11885                 this.menu.show(this.el, this.menuAlign);
11886             }
11887             this.fireEvent("click", this, e);
11888             if(this.handler){
11889                 this.el.removeClass("x-btn-over");
11890                 this.handler.call(this.scope || this, this, e);
11891             }
11892         }
11893     },
11894     // private
11895     onMouseOver : function(e){
11896         if(!this.disabled){
11897             this.el.addClass("x-btn-over");
11898             this.fireEvent('mouseover', this, e);
11899         }
11900     },
11901     // private
11902     onMouseOut : function(e){
11903         if(!e.within(this.el,  true)){
11904             this.el.removeClass("x-btn-over");
11905             this.fireEvent('mouseout', this, e);
11906         }
11907     },
11908     // private
11909     onFocus : function(e){
11910         if(!this.disabled){
11911             this.el.addClass("x-btn-focus");
11912         }
11913     },
11914     // private
11915     onBlur : function(e){
11916         this.el.removeClass("x-btn-focus");
11917     },
11918     // private
11919     onMouseDown : function(e){
11920         if(!this.disabled && e.button == 0){
11921             this.el.addClass("x-btn-click");
11922             Roo.get(document).on('mouseup', this.onMouseUp, this);
11923         }
11924     },
11925     // private
11926     onMouseUp : function(e){
11927         if(e.button == 0){
11928             this.el.removeClass("x-btn-click");
11929             Roo.get(document).un('mouseup', this.onMouseUp, this);
11930         }
11931     },
11932     // private
11933     onMenuShow : function(e){
11934         this.el.addClass("x-btn-menu-active");
11935     },
11936     // private
11937     onMenuHide : function(e){
11938         this.el.removeClass("x-btn-menu-active");
11939     }   
11940 });
11941
11942 // Private utility class used by Button
11943 Roo.ButtonToggleMgr = function(){
11944    var groups = {};
11945    
11946    function toggleGroup(btn, state){
11947        if(state){
11948            var g = groups[btn.toggleGroup];
11949            for(var i = 0, l = g.length; i < l; i++){
11950                if(g[i] != btn){
11951                    g[i].toggle(false);
11952                }
11953            }
11954        }
11955    }
11956    
11957    return {
11958        register : function(btn){
11959            if(!btn.toggleGroup){
11960                return;
11961            }
11962            var g = groups[btn.toggleGroup];
11963            if(!g){
11964                g = groups[btn.toggleGroup] = [];
11965            }
11966            g.push(btn);
11967            btn.on("toggle", toggleGroup);
11968        },
11969        
11970        unregister : function(btn){
11971            if(!btn.toggleGroup){
11972                return;
11973            }
11974            var g = groups[btn.toggleGroup];
11975            if(g){
11976                g.remove(btn);
11977                btn.un("toggle", toggleGroup);
11978            }
11979        }
11980    };
11981 }();/*
11982  * Based on:
11983  * Ext JS Library 1.1.1
11984  * Copyright(c) 2006-2007, Ext JS, LLC.
11985  *
11986  * Originally Released Under LGPL - original licence link has changed is not relivant.
11987  *
11988  * Fork - LGPL
11989  * <script type="text/javascript">
11990  */
11991  
11992 /**
11993  * @class Roo.SplitButton
11994  * @extends Roo.Button
11995  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11996  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11997  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11998  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11999  * @cfg {String} arrowTooltip The title attribute of the arrow
12000  * @constructor
12001  * Create a new menu button
12002  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12003  * @param {Object} config The config object
12004  */
12005 Roo.SplitButton = function(renderTo, config){
12006     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12007     /**
12008      * @event arrowclick
12009      * Fires when this button's arrow is clicked
12010      * @param {SplitButton} this
12011      * @param {EventObject} e The click event
12012      */
12013     this.addEvents({"arrowclick":true});
12014 };
12015
12016 Roo.extend(Roo.SplitButton, Roo.Button, {
12017     render : function(renderTo){
12018         // this is one sweet looking template!
12019         var tpl = new Roo.Template(
12020             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12021             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12022             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12023             "</tbody></table></td><td>",
12024             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12025             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12026             "</tbody></table></td></tr></table>"
12027         );
12028         var btn = tpl.append(renderTo, [this.text, this.type], true);
12029         var btnEl = btn.child("button");
12030         if(this.cls){
12031             btn.addClass(this.cls);
12032         }
12033         if(this.icon){
12034             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12035         }
12036         if(this.iconCls){
12037             btnEl.addClass(this.iconCls);
12038             if(!this.cls){
12039                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12040             }
12041         }
12042         this.el = btn;
12043         if(this.handleMouseEvents){
12044             btn.on("mouseover", this.onMouseOver, this);
12045             btn.on("mouseout", this.onMouseOut, this);
12046             btn.on("mousedown", this.onMouseDown, this);
12047             btn.on("mouseup", this.onMouseUp, this);
12048         }
12049         btn.on(this.clickEvent, this.onClick, this);
12050         if(this.tooltip){
12051             if(typeof this.tooltip == 'object'){
12052                 Roo.QuickTips.tips(Roo.apply({
12053                       target: btnEl.id
12054                 }, this.tooltip));
12055             } else {
12056                 btnEl.dom[this.tooltipType] = this.tooltip;
12057             }
12058         }
12059         if(this.arrowTooltip){
12060             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12061         }
12062         if(this.hidden){
12063             this.hide();
12064         }
12065         if(this.disabled){
12066             this.disable();
12067         }
12068         if(this.pressed){
12069             this.el.addClass("x-btn-pressed");
12070         }
12071         if(Roo.isIE && !Roo.isIE7){
12072             this.autoWidth.defer(1, this);
12073         }else{
12074             this.autoWidth();
12075         }
12076         if(this.menu){
12077             this.menu.on("show", this.onMenuShow, this);
12078             this.menu.on("hide", this.onMenuHide, this);
12079         }
12080         this.fireEvent('render', this);
12081     },
12082
12083     // private
12084     autoWidth : function(){
12085         if(this.el){
12086             var tbl = this.el.child("table:first");
12087             var tbl2 = this.el.child("table:last");
12088             this.el.setWidth("auto");
12089             tbl.setWidth("auto");
12090             if(Roo.isIE7 && Roo.isStrict){
12091                 var ib = this.el.child('button:first');
12092                 if(ib && ib.getWidth() > 20){
12093                     ib.clip();
12094                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12095                 }
12096             }
12097             if(this.minWidth){
12098                 if(this.hidden){
12099                     this.el.beginMeasure();
12100                 }
12101                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12102                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12103                 }
12104                 if(this.hidden){
12105                     this.el.endMeasure();
12106                 }
12107             }
12108             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12109         } 
12110     },
12111     /**
12112      * Sets this button's click handler
12113      * @param {Function} handler The function to call when the button is clicked
12114      * @param {Object} scope (optional) Scope for the function passed above
12115      */
12116     setHandler : function(handler, scope){
12117         this.handler = handler;
12118         this.scope = scope;  
12119     },
12120     
12121     /**
12122      * Sets this button's arrow click handler
12123      * @param {Function} handler The function to call when the arrow is clicked
12124      * @param {Object} scope (optional) Scope for the function passed above
12125      */
12126     setArrowHandler : function(handler, scope){
12127         this.arrowHandler = handler;
12128         this.scope = scope;  
12129     },
12130     
12131     /**
12132      * Focus the button
12133      */
12134     focus : function(){
12135         if(this.el){
12136             this.el.child("button:first").focus();
12137         }
12138     },
12139
12140     // private
12141     onClick : function(e){
12142         e.preventDefault();
12143         if(!this.disabled){
12144             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12145                 if(this.menu && !this.menu.isVisible()){
12146                     this.menu.show(this.el, this.menuAlign);
12147                 }
12148                 this.fireEvent("arrowclick", this, e);
12149                 if(this.arrowHandler){
12150                     this.arrowHandler.call(this.scope || this, this, e);
12151                 }
12152             }else{
12153                 this.fireEvent("click", this, e);
12154                 if(this.handler){
12155                     this.handler.call(this.scope || this, this, e);
12156                 }
12157             }
12158         }
12159     },
12160     // private
12161     onMouseDown : function(e){
12162         if(!this.disabled){
12163             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12164         }
12165     },
12166     // private
12167     onMouseUp : function(e){
12168         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12169     }   
12170 });
12171
12172
12173 // backwards compat
12174 Roo.MenuButton = Roo.SplitButton;/*
12175  * Based on:
12176  * Ext JS Library 1.1.1
12177  * Copyright(c) 2006-2007, Ext JS, LLC.
12178  *
12179  * Originally Released Under LGPL - original licence link has changed is not relivant.
12180  *
12181  * Fork - LGPL
12182  * <script type="text/javascript">
12183  */
12184
12185 /**
12186  * @class Roo.Toolbar
12187  * Basic Toolbar class.
12188  * @constructor
12189  * Creates a new Toolbar
12190  * @param {Object} config The config object
12191  */ 
12192 Roo.Toolbar = function(container, buttons, config)
12193 {
12194     /// old consturctor format still supported..
12195     if(container instanceof Array){ // omit the container for later rendering
12196         buttons = container;
12197         config = buttons;
12198         container = null;
12199     }
12200     if (typeof(container) == 'object' && container.xtype) {
12201         config = container;
12202         container = config.container;
12203         buttons = config.buttons; // not really - use items!!
12204     }
12205     var xitems = [];
12206     if (config && config.items) {
12207         xitems = config.items;
12208         delete config.items;
12209     }
12210     Roo.apply(this, config);
12211     this.buttons = buttons;
12212     
12213     if(container){
12214         this.render(container);
12215     }
12216     Roo.each(xitems, function(b) {
12217         this.add(b);
12218     }, this);
12219     
12220 };
12221
12222 Roo.Toolbar.prototype = {
12223     /**
12224      * @cfg {Roo.data.Store} items
12225      * array of button configs or elements to add
12226      */
12227     
12228     /**
12229      * @cfg {String/HTMLElement/Element} container
12230      * The id or element that will contain the toolbar
12231      */
12232     // private
12233     render : function(ct){
12234         this.el = Roo.get(ct);
12235         if(this.cls){
12236             this.el.addClass(this.cls);
12237         }
12238         // using a table allows for vertical alignment
12239         // 100% width is needed by Safari...
12240         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12241         this.tr = this.el.child("tr", true);
12242         var autoId = 0;
12243         this.items = new Roo.util.MixedCollection(false, function(o){
12244             return o.id || ("item" + (++autoId));
12245         });
12246         if(this.buttons){
12247             this.add.apply(this, this.buttons);
12248             delete this.buttons;
12249         }
12250     },
12251
12252     /**
12253      * Adds element(s) to the toolbar -- this function takes a variable number of 
12254      * arguments of mixed type and adds them to the toolbar.
12255      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12256      * <ul>
12257      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12258      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12259      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12260      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12261      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12262      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12263      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12264      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12265      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12266      * </ul>
12267      * @param {Mixed} arg2
12268      * @param {Mixed} etc.
12269      */
12270     add : function(){
12271         var a = arguments, l = a.length;
12272         for(var i = 0; i < l; i++){
12273             this._add(a[i]);
12274         }
12275     },
12276     // private..
12277     _add : function(el) {
12278         
12279         if (el.xtype) {
12280             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12281         }
12282         
12283         if (el.applyTo){ // some kind of form field
12284             return this.addField(el);
12285         } 
12286         if (el.render){ // some kind of Toolbar.Item
12287             return this.addItem(el);
12288         }
12289         if (typeof el == "string"){ // string
12290             if(el == "separator" || el == "-"){
12291                 return this.addSeparator();
12292             }
12293             if (el == " "){
12294                 return this.addSpacer();
12295             }
12296             if(el == "->"){
12297                 return this.addFill();
12298             }
12299             return this.addText(el);
12300             
12301         }
12302         if(el.tagName){ // element
12303             return this.addElement(el);
12304         }
12305         if(typeof el == "object"){ // must be button config?
12306             return this.addButton(el);
12307         }
12308         // and now what?!?!
12309         return false;
12310         
12311     },
12312     
12313     /**
12314      * Add an Xtype element
12315      * @param {Object} xtype Xtype Object
12316      * @return {Object} created Object
12317      */
12318     addxtype : function(e){
12319         return this.add(e);  
12320     },
12321     
12322     /**
12323      * Returns the Element for this toolbar.
12324      * @return {Roo.Element}
12325      */
12326     getEl : function(){
12327         return this.el;  
12328     },
12329     
12330     /**
12331      * Adds a separator
12332      * @return {Roo.Toolbar.Item} The separator item
12333      */
12334     addSeparator : function(){
12335         return this.addItem(new Roo.Toolbar.Separator());
12336     },
12337
12338     /**
12339      * Adds a spacer element
12340      * @return {Roo.Toolbar.Spacer} The spacer item
12341      */
12342     addSpacer : function(){
12343         return this.addItem(new Roo.Toolbar.Spacer());
12344     },
12345
12346     /**
12347      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12348      * @return {Roo.Toolbar.Fill} The fill item
12349      */
12350     addFill : function(){
12351         return this.addItem(new Roo.Toolbar.Fill());
12352     },
12353
12354     /**
12355      * Adds any standard HTML element to the toolbar
12356      * @param {String/HTMLElement/Element} el The element or id of the element to add
12357      * @return {Roo.Toolbar.Item} The element's item
12358      */
12359     addElement : function(el){
12360         return this.addItem(new Roo.Toolbar.Item(el));
12361     },
12362     /**
12363      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12364      * @type Roo.util.MixedCollection  
12365      */
12366     items : false,
12367      
12368     /**
12369      * Adds any Toolbar.Item or subclass
12370      * @param {Roo.Toolbar.Item} item
12371      * @return {Roo.Toolbar.Item} The item
12372      */
12373     addItem : function(item){
12374         var td = this.nextBlock();
12375         item.render(td);
12376         this.items.add(item);
12377         return item;
12378     },
12379     
12380     /**
12381      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12382      * @param {Object/Array} config A button config or array of configs
12383      * @return {Roo.Toolbar.Button/Array}
12384      */
12385     addButton : function(config){
12386         if(config instanceof Array){
12387             var buttons = [];
12388             for(var i = 0, len = config.length; i < len; i++) {
12389                 buttons.push(this.addButton(config[i]));
12390             }
12391             return buttons;
12392         }
12393         var b = config;
12394         if(!(config instanceof Roo.Toolbar.Button)){
12395             b = config.split ?
12396                 new Roo.Toolbar.SplitButton(config) :
12397                 new Roo.Toolbar.Button(config);
12398         }
12399         var td = this.nextBlock();
12400         b.render(td);
12401         this.items.add(b);
12402         return b;
12403     },
12404     
12405     /**
12406      * Adds text to the toolbar
12407      * @param {String} text The text to add
12408      * @return {Roo.Toolbar.Item} The element's item
12409      */
12410     addText : function(text){
12411         return this.addItem(new Roo.Toolbar.TextItem(text));
12412     },
12413     
12414     /**
12415      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12416      * @param {Number} index The index where the item is to be inserted
12417      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12418      * @return {Roo.Toolbar.Button/Item}
12419      */
12420     insertButton : function(index, item){
12421         if(item instanceof Array){
12422             var buttons = [];
12423             for(var i = 0, len = item.length; i < len; i++) {
12424                buttons.push(this.insertButton(index + i, item[i]));
12425             }
12426             return buttons;
12427         }
12428         if (!(item instanceof Roo.Toolbar.Button)){
12429            item = new Roo.Toolbar.Button(item);
12430         }
12431         var td = document.createElement("td");
12432         this.tr.insertBefore(td, this.tr.childNodes[index]);
12433         item.render(td);
12434         this.items.insert(index, item);
12435         return item;
12436     },
12437     
12438     /**
12439      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12440      * @param {Object} config
12441      * @return {Roo.Toolbar.Item} The element's item
12442      */
12443     addDom : function(config, returnEl){
12444         var td = this.nextBlock();
12445         Roo.DomHelper.overwrite(td, config);
12446         var ti = new Roo.Toolbar.Item(td.firstChild);
12447         ti.render(td);
12448         this.items.add(ti);
12449         return ti;
12450     },
12451
12452     /**
12453      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12454      * @type Roo.util.MixedCollection  
12455      */
12456     fields : false,
12457     
12458     /**
12459      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12460      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12461      * @param {Roo.form.Field} field
12462      * @return {Roo.ToolbarItem}
12463      */
12464      
12465       
12466     addField : function(field) {
12467         if (!this.fields) {
12468             var autoId = 0;
12469             this.fields = new Roo.util.MixedCollection(false, function(o){
12470                 return o.id || ("item" + (++autoId));
12471             });
12472
12473         }
12474         
12475         var td = this.nextBlock();
12476         field.render(td);
12477         var ti = new Roo.Toolbar.Item(td.firstChild);
12478         ti.render(td);
12479         this.items.add(ti);
12480         this.fields.add(field);
12481         return ti;
12482     },
12483     /**
12484      * Hide the toolbar
12485      * @method hide
12486      */
12487      
12488       
12489     hide : function()
12490     {
12491         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12492         this.el.child('div').hide();
12493     },
12494     /**
12495      * Show the toolbar
12496      * @method show
12497      */
12498     show : function()
12499     {
12500         this.el.child('div').show();
12501     },
12502       
12503     // private
12504     nextBlock : function(){
12505         var td = document.createElement("td");
12506         this.tr.appendChild(td);
12507         return td;
12508     },
12509
12510     // private
12511     destroy : function(){
12512         if(this.items){ // rendered?
12513             Roo.destroy.apply(Roo, this.items.items);
12514         }
12515         if(this.fields){ // rendered?
12516             Roo.destroy.apply(Roo, this.fields.items);
12517         }
12518         Roo.Element.uncache(this.el, this.tr);
12519     }
12520 };
12521
12522 /**
12523  * @class Roo.Toolbar.Item
12524  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12525  * @constructor
12526  * Creates a new Item
12527  * @param {HTMLElement} el 
12528  */
12529 Roo.Toolbar.Item = function(el){
12530     this.el = Roo.getDom(el);
12531     this.id = Roo.id(this.el);
12532     this.hidden = false;
12533 };
12534
12535 Roo.Toolbar.Item.prototype = {
12536     
12537     /**
12538      * Get this item's HTML Element
12539      * @return {HTMLElement}
12540      */
12541     getEl : function(){
12542        return this.el;  
12543     },
12544
12545     // private
12546     render : function(td){
12547         this.td = td;
12548         td.appendChild(this.el);
12549     },
12550     
12551     /**
12552      * Removes and destroys this item.
12553      */
12554     destroy : function(){
12555         this.td.parentNode.removeChild(this.td);
12556     },
12557     
12558     /**
12559      * Shows this item.
12560      */
12561     show: function(){
12562         this.hidden = false;
12563         this.td.style.display = "";
12564     },
12565     
12566     /**
12567      * Hides this item.
12568      */
12569     hide: function(){
12570         this.hidden = true;
12571         this.td.style.display = "none";
12572     },
12573     
12574     /**
12575      * Convenience function for boolean show/hide.
12576      * @param {Boolean} visible true to show/false to hide
12577      */
12578     setVisible: function(visible){
12579         if(visible) {
12580             this.show();
12581         }else{
12582             this.hide();
12583         }
12584     },
12585     
12586     /**
12587      * Try to focus this item.
12588      */
12589     focus : function(){
12590         Roo.fly(this.el).focus();
12591     },
12592     
12593     /**
12594      * Disables this item.
12595      */
12596     disable : function(){
12597         Roo.fly(this.td).addClass("x-item-disabled");
12598         this.disabled = true;
12599         this.el.disabled = true;
12600     },
12601     
12602     /**
12603      * Enables this item.
12604      */
12605     enable : function(){
12606         Roo.fly(this.td).removeClass("x-item-disabled");
12607         this.disabled = false;
12608         this.el.disabled = false;
12609     }
12610 };
12611
12612
12613 /**
12614  * @class Roo.Toolbar.Separator
12615  * @extends Roo.Toolbar.Item
12616  * A simple toolbar separator class
12617  * @constructor
12618  * Creates a new Separator
12619  */
12620 Roo.Toolbar.Separator = function(){
12621     var s = document.createElement("span");
12622     s.className = "ytb-sep";
12623     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12624 };
12625 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12626     enable:Roo.emptyFn,
12627     disable:Roo.emptyFn,
12628     focus:Roo.emptyFn
12629 });
12630
12631 /**
12632  * @class Roo.Toolbar.Spacer
12633  * @extends Roo.Toolbar.Item
12634  * A simple element that adds extra horizontal space to a toolbar.
12635  * @constructor
12636  * Creates a new Spacer
12637  */
12638 Roo.Toolbar.Spacer = function(){
12639     var s = document.createElement("div");
12640     s.className = "ytb-spacer";
12641     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12642 };
12643 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12644     enable:Roo.emptyFn,
12645     disable:Roo.emptyFn,
12646     focus:Roo.emptyFn
12647 });
12648
12649 /**
12650  * @class Roo.Toolbar.Fill
12651  * @extends Roo.Toolbar.Spacer
12652  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12653  * @constructor
12654  * Creates a new Spacer
12655  */
12656 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12657     // private
12658     render : function(td){
12659         td.style.width = '100%';
12660         Roo.Toolbar.Fill.superclass.render.call(this, td);
12661     }
12662 });
12663
12664 /**
12665  * @class Roo.Toolbar.TextItem
12666  * @extends Roo.Toolbar.Item
12667  * A simple class that renders text directly into a toolbar.
12668  * @constructor
12669  * Creates a new TextItem
12670  * @param {String} text
12671  */
12672 Roo.Toolbar.TextItem = function(text){
12673     if (typeof(text) == 'object') {
12674         text = text.text;
12675     }
12676     var s = document.createElement("span");
12677     s.className = "ytb-text";
12678     s.innerHTML = text;
12679     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12680 };
12681 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12682     enable:Roo.emptyFn,
12683     disable:Roo.emptyFn,
12684     focus:Roo.emptyFn
12685 });
12686
12687 /**
12688  * @class Roo.Toolbar.Button
12689  * @extends Roo.Button
12690  * A button that renders into a toolbar.
12691  * @constructor
12692  * Creates a new Button
12693  * @param {Object} config A standard {@link Roo.Button} config object
12694  */
12695 Roo.Toolbar.Button = function(config){
12696     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12697 };
12698 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12699     render : function(td){
12700         this.td = td;
12701         Roo.Toolbar.Button.superclass.render.call(this, td);
12702     },
12703     
12704     /**
12705      * Removes and destroys this button
12706      */
12707     destroy : function(){
12708         Roo.Toolbar.Button.superclass.destroy.call(this);
12709         this.td.parentNode.removeChild(this.td);
12710     },
12711     
12712     /**
12713      * Shows this button
12714      */
12715     show: function(){
12716         this.hidden = false;
12717         this.td.style.display = "";
12718     },
12719     
12720     /**
12721      * Hides this button
12722      */
12723     hide: function(){
12724         this.hidden = true;
12725         this.td.style.display = "none";
12726     },
12727
12728     /**
12729      * Disables this item
12730      */
12731     disable : function(){
12732         Roo.fly(this.td).addClass("x-item-disabled");
12733         this.disabled = true;
12734     },
12735
12736     /**
12737      * Enables this item
12738      */
12739     enable : function(){
12740         Roo.fly(this.td).removeClass("x-item-disabled");
12741         this.disabled = false;
12742     }
12743 });
12744 // backwards compat
12745 Roo.ToolbarButton = Roo.Toolbar.Button;
12746
12747 /**
12748  * @class Roo.Toolbar.SplitButton
12749  * @extends Roo.SplitButton
12750  * A menu button that renders into a toolbar.
12751  * @constructor
12752  * Creates a new SplitButton
12753  * @param {Object} config A standard {@link Roo.SplitButton} config object
12754  */
12755 Roo.Toolbar.SplitButton = function(config){
12756     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12757 };
12758 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12759     render : function(td){
12760         this.td = td;
12761         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12762     },
12763     
12764     /**
12765      * Removes and destroys this button
12766      */
12767     destroy : function(){
12768         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12769         this.td.parentNode.removeChild(this.td);
12770     },
12771     
12772     /**
12773      * Shows this button
12774      */
12775     show: function(){
12776         this.hidden = false;
12777         this.td.style.display = "";
12778     },
12779     
12780     /**
12781      * Hides this button
12782      */
12783     hide: function(){
12784         this.hidden = true;
12785         this.td.style.display = "none";
12786     }
12787 });
12788
12789 // backwards compat
12790 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12791  * Based on:
12792  * Ext JS Library 1.1.1
12793  * Copyright(c) 2006-2007, Ext JS, LLC.
12794  *
12795  * Originally Released Under LGPL - original licence link has changed is not relivant.
12796  *
12797  * Fork - LGPL
12798  * <script type="text/javascript">
12799  */
12800  
12801 /**
12802  * @class Roo.PagingToolbar
12803  * @extends Roo.Toolbar
12804  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12805  * @constructor
12806  * Create a new PagingToolbar
12807  * @param {Object} config The config object
12808  */
12809 Roo.PagingToolbar = function(el, ds, config)
12810 {
12811     // old args format still supported... - xtype is prefered..
12812     if (typeof(el) == 'object' && el.xtype) {
12813         // created from xtype...
12814         config = el;
12815         ds = el.dataSource;
12816         el = config.container;
12817     }
12818     var items = [];
12819     if (config.items) {
12820         items = config.items;
12821         config.items = [];
12822     }
12823     
12824     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12825     this.ds = ds;
12826     this.cursor = 0;
12827     this.renderButtons(this.el);
12828     this.bind(ds);
12829     
12830     // supprot items array.
12831    
12832     Roo.each(items, function(e) {
12833         this.add(Roo.factory(e));
12834     },this);
12835     
12836 };
12837
12838 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12839     /**
12840      * @cfg {Roo.data.Store} dataSource
12841      * The underlying data store providing the paged data
12842      */
12843     /**
12844      * @cfg {String/HTMLElement/Element} container
12845      * container The id or element that will contain the toolbar
12846      */
12847     /**
12848      * @cfg {Boolean} displayInfo
12849      * True to display the displayMsg (defaults to false)
12850      */
12851     /**
12852      * @cfg {Number} pageSize
12853      * The number of records to display per page (defaults to 20)
12854      */
12855     pageSize: 20,
12856     /**
12857      * @cfg {String} displayMsg
12858      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12859      */
12860     displayMsg : 'Displaying {0} - {1} of {2}',
12861     /**
12862      * @cfg {String} emptyMsg
12863      * The message to display when no records are found (defaults to "No data to display")
12864      */
12865     emptyMsg : 'No data to display',
12866     /**
12867      * Customizable piece of the default paging text (defaults to "Page")
12868      * @type String
12869      */
12870     beforePageText : "Page",
12871     /**
12872      * Customizable piece of the default paging text (defaults to "of %0")
12873      * @type String
12874      */
12875     afterPageText : "of {0}",
12876     /**
12877      * Customizable piece of the default paging text (defaults to "First Page")
12878      * @type String
12879      */
12880     firstText : "First Page",
12881     /**
12882      * Customizable piece of the default paging text (defaults to "Previous Page")
12883      * @type String
12884      */
12885     prevText : "Previous Page",
12886     /**
12887      * Customizable piece of the default paging text (defaults to "Next Page")
12888      * @type String
12889      */
12890     nextText : "Next Page",
12891     /**
12892      * Customizable piece of the default paging text (defaults to "Last Page")
12893      * @type String
12894      */
12895     lastText : "Last Page",
12896     /**
12897      * Customizable piece of the default paging text (defaults to "Refresh")
12898      * @type String
12899      */
12900     refreshText : "Refresh",
12901
12902     // private
12903     renderButtons : function(el){
12904         Roo.PagingToolbar.superclass.render.call(this, el);
12905         this.first = this.addButton({
12906             tooltip: this.firstText,
12907             cls: "x-btn-icon x-grid-page-first",
12908             disabled: true,
12909             handler: this.onClick.createDelegate(this, ["first"])
12910         });
12911         this.prev = this.addButton({
12912             tooltip: this.prevText,
12913             cls: "x-btn-icon x-grid-page-prev",
12914             disabled: true,
12915             handler: this.onClick.createDelegate(this, ["prev"])
12916         });
12917         //this.addSeparator();
12918         this.add(this.beforePageText);
12919         this.field = Roo.get(this.addDom({
12920            tag: "input",
12921            type: "text",
12922            size: "3",
12923            value: "1",
12924            cls: "x-grid-page-number"
12925         }).el);
12926         this.field.on("keydown", this.onPagingKeydown, this);
12927         this.field.on("focus", function(){this.dom.select();});
12928         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12929         this.field.setHeight(18);
12930         //this.addSeparator();
12931         this.next = this.addButton({
12932             tooltip: this.nextText,
12933             cls: "x-btn-icon x-grid-page-next",
12934             disabled: true,
12935             handler: this.onClick.createDelegate(this, ["next"])
12936         });
12937         this.last = this.addButton({
12938             tooltip: this.lastText,
12939             cls: "x-btn-icon x-grid-page-last",
12940             disabled: true,
12941             handler: this.onClick.createDelegate(this, ["last"])
12942         });
12943         //this.addSeparator();
12944         this.loading = this.addButton({
12945             tooltip: this.refreshText,
12946             cls: "x-btn-icon x-grid-loading",
12947             handler: this.onClick.createDelegate(this, ["refresh"])
12948         });
12949
12950         if(this.displayInfo){
12951             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12952         }
12953     },
12954
12955     // private
12956     updateInfo : function(){
12957         if(this.displayEl){
12958             var count = this.ds.getCount();
12959             var msg = count == 0 ?
12960                 this.emptyMsg :
12961                 String.format(
12962                     this.displayMsg,
12963                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12964                 );
12965             this.displayEl.update(msg);
12966         }
12967     },
12968
12969     // private
12970     onLoad : function(ds, r, o){
12971        this.cursor = o.params ? o.params.start : 0;
12972        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12973
12974        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12975        this.field.dom.value = ap;
12976        this.first.setDisabled(ap == 1);
12977        this.prev.setDisabled(ap == 1);
12978        this.next.setDisabled(ap == ps);
12979        this.last.setDisabled(ap == ps);
12980        this.loading.enable();
12981        this.updateInfo();
12982     },
12983
12984     // private
12985     getPageData : function(){
12986         var total = this.ds.getTotalCount();
12987         return {
12988             total : total,
12989             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12990             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12991         };
12992     },
12993
12994     // private
12995     onLoadError : function(){
12996         this.loading.enable();
12997     },
12998
12999     // private
13000     onPagingKeydown : function(e){
13001         var k = e.getKey();
13002         var d = this.getPageData();
13003         if(k == e.RETURN){
13004             var v = this.field.dom.value, pageNum;
13005             if(!v || isNaN(pageNum = parseInt(v, 10))){
13006                 this.field.dom.value = d.activePage;
13007                 return;
13008             }
13009             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13010             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13011             e.stopEvent();
13012         }
13013         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13014         {
13015           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13016           this.field.dom.value = pageNum;
13017           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13018           e.stopEvent();
13019         }
13020         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13021         {
13022           var v = this.field.dom.value, pageNum; 
13023           var increment = (e.shiftKey) ? 10 : 1;
13024           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13025             increment *= -1;
13026           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13027             this.field.dom.value = d.activePage;
13028             return;
13029           }
13030           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13031           {
13032             this.field.dom.value = parseInt(v, 10) + increment;
13033             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13034             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13035           }
13036           e.stopEvent();
13037         }
13038     },
13039
13040     // private
13041     beforeLoad : function(){
13042         if(this.loading){
13043             this.loading.disable();
13044         }
13045     },
13046
13047     // private
13048     onClick : function(which){
13049         var ds = this.ds;
13050         switch(which){
13051             case "first":
13052                 ds.load({params:{start: 0, limit: this.pageSize}});
13053             break;
13054             case "prev":
13055                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13056             break;
13057             case "next":
13058                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13059             break;
13060             case "last":
13061                 var total = ds.getTotalCount();
13062                 var extra = total % this.pageSize;
13063                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13064                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13065             break;
13066             case "refresh":
13067                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13068             break;
13069         }
13070     },
13071
13072     /**
13073      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13074      * @param {Roo.data.Store} store The data store to unbind
13075      */
13076     unbind : function(ds){
13077         ds.un("beforeload", this.beforeLoad, this);
13078         ds.un("load", this.onLoad, this);
13079         ds.un("loadexception", this.onLoadError, this);
13080         ds.un("remove", this.updateInfo, this);
13081         ds.un("add", this.updateInfo, this);
13082         this.ds = undefined;
13083     },
13084
13085     /**
13086      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13087      * @param {Roo.data.Store} store The data store to bind
13088      */
13089     bind : function(ds){
13090         ds.on("beforeload", this.beforeLoad, this);
13091         ds.on("load", this.onLoad, this);
13092         ds.on("loadexception", this.onLoadError, this);
13093         ds.on("remove", this.updateInfo, this);
13094         ds.on("add", this.updateInfo, this);
13095         this.ds = ds;
13096     }
13097 });/*
13098  * Based on:
13099  * Ext JS Library 1.1.1
13100  * Copyright(c) 2006-2007, Ext JS, LLC.
13101  *
13102  * Originally Released Under LGPL - original licence link has changed is not relivant.
13103  *
13104  * Fork - LGPL
13105  * <script type="text/javascript">
13106  */
13107
13108 /**
13109  * @class Roo.Resizable
13110  * @extends Roo.util.Observable
13111  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13112  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13113  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13114  * the element will be wrapped for you automatically.</p>
13115  * <p>Here is the list of valid resize handles:</p>
13116  * <pre>
13117 Value   Description
13118 ------  -------------------
13119  'n'     north
13120  's'     south
13121  'e'     east
13122  'w'     west
13123  'nw'    northwest
13124  'sw'    southwest
13125  'se'    southeast
13126  'ne'    northeast
13127  'hd'    horizontal drag
13128  'all'   all
13129 </pre>
13130  * <p>Here's an example showing the creation of a typical Resizable:</p>
13131  * <pre><code>
13132 var resizer = new Roo.Resizable("element-id", {
13133     handles: 'all',
13134     minWidth: 200,
13135     minHeight: 100,
13136     maxWidth: 500,
13137     maxHeight: 400,
13138     pinned: true
13139 });
13140 resizer.on("resize", myHandler);
13141 </code></pre>
13142  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13143  * resizer.east.setDisplayed(false);</p>
13144  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13145  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13146  * resize operation's new size (defaults to [0, 0])
13147  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13148  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13149  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13150  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13151  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13152  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13153  * @cfg {Number} width The width of the element in pixels (defaults to null)
13154  * @cfg {Number} height The height of the element in pixels (defaults to null)
13155  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13156  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13157  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13158  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13159  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13160  * in favor of the handles config option (defaults to false)
13161  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13162  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13163  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13164  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13165  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13166  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13167  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13168  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13169  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13170  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13171  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13172  * @constructor
13173  * Create a new resizable component
13174  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13175  * @param {Object} config configuration options
13176   */
13177 Roo.Resizable = function(el, config)
13178 {
13179     this.el = Roo.get(el);
13180
13181     if(config && config.wrap){
13182         config.resizeChild = this.el;
13183         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13184         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13185         this.el.setStyle("overflow", "hidden");
13186         this.el.setPositioning(config.resizeChild.getPositioning());
13187         config.resizeChild.clearPositioning();
13188         if(!config.width || !config.height){
13189             var csize = config.resizeChild.getSize();
13190             this.el.setSize(csize.width, csize.height);
13191         }
13192         if(config.pinned && !config.adjustments){
13193             config.adjustments = "auto";
13194         }
13195     }
13196
13197     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13198     this.proxy.unselectable();
13199     this.proxy.enableDisplayMode('block');
13200
13201     Roo.apply(this, config);
13202
13203     if(this.pinned){
13204         this.disableTrackOver = true;
13205         this.el.addClass("x-resizable-pinned");
13206     }
13207     // if the element isn't positioned, make it relative
13208     var position = this.el.getStyle("position");
13209     if(position != "absolute" && position != "fixed"){
13210         this.el.setStyle("position", "relative");
13211     }
13212     if(!this.handles){ // no handles passed, must be legacy style
13213         this.handles = 's,e,se';
13214         if(this.multiDirectional){
13215             this.handles += ',n,w';
13216         }
13217     }
13218     if(this.handles == "all"){
13219         this.handles = "n s e w ne nw se sw";
13220     }
13221     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13222     var ps = Roo.Resizable.positions;
13223     for(var i = 0, len = hs.length; i < len; i++){
13224         if(hs[i] && ps[hs[i]]){
13225             var pos = ps[hs[i]];
13226             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13227         }
13228     }
13229     // legacy
13230     this.corner = this.southeast;
13231     
13232     // updateBox = the box can move..
13233     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13234         this.updateBox = true;
13235     }
13236
13237     this.activeHandle = null;
13238
13239     if(this.resizeChild){
13240         if(typeof this.resizeChild == "boolean"){
13241             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13242         }else{
13243             this.resizeChild = Roo.get(this.resizeChild, true);
13244         }
13245     }
13246     
13247     if(this.adjustments == "auto"){
13248         var rc = this.resizeChild;
13249         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13250         if(rc && (hw || hn)){
13251             rc.position("relative");
13252             rc.setLeft(hw ? hw.el.getWidth() : 0);
13253             rc.setTop(hn ? hn.el.getHeight() : 0);
13254         }
13255         this.adjustments = [
13256             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13257             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13258         ];
13259     }
13260
13261     if(this.draggable){
13262         this.dd = this.dynamic ?
13263             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13264         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13265     }
13266
13267     // public events
13268     this.addEvents({
13269         /**
13270          * @event beforeresize
13271          * Fired before resize is allowed. Set enabled to false to cancel resize.
13272          * @param {Roo.Resizable} this
13273          * @param {Roo.EventObject} e The mousedown event
13274          */
13275         "beforeresize" : true,
13276         /**
13277          * @event resize
13278          * Fired after a resize.
13279          * @param {Roo.Resizable} this
13280          * @param {Number} width The new width
13281          * @param {Number} height The new height
13282          * @param {Roo.EventObject} e The mouseup event
13283          */
13284         "resize" : true
13285     });
13286
13287     if(this.width !== null && this.height !== null){
13288         this.resizeTo(this.width, this.height);
13289     }else{
13290         this.updateChildSize();
13291     }
13292     if(Roo.isIE){
13293         this.el.dom.style.zoom = 1;
13294     }
13295     Roo.Resizable.superclass.constructor.call(this);
13296 };
13297
13298 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13299         resizeChild : false,
13300         adjustments : [0, 0],
13301         minWidth : 5,
13302         minHeight : 5,
13303         maxWidth : 10000,
13304         maxHeight : 10000,
13305         enabled : true,
13306         animate : false,
13307         duration : .35,
13308         dynamic : false,
13309         handles : false,
13310         multiDirectional : false,
13311         disableTrackOver : false,
13312         easing : 'easeOutStrong',
13313         widthIncrement : 0,
13314         heightIncrement : 0,
13315         pinned : false,
13316         width : null,
13317         height : null,
13318         preserveRatio : false,
13319         transparent: false,
13320         minX: 0,
13321         minY: 0,
13322         draggable: false,
13323
13324         /**
13325          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13326          */
13327         constrainTo: undefined,
13328         /**
13329          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13330          */
13331         resizeRegion: undefined,
13332
13333
13334     /**
13335      * Perform a manual resize
13336      * @param {Number} width
13337      * @param {Number} height
13338      */
13339     resizeTo : function(width, height){
13340         this.el.setSize(width, height);
13341         this.updateChildSize();
13342         this.fireEvent("resize", this, width, height, null);
13343     },
13344
13345     // private
13346     startSizing : function(e, handle){
13347         this.fireEvent("beforeresize", this, e);
13348         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13349
13350             if(!this.overlay){
13351                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13352                 this.overlay.unselectable();
13353                 this.overlay.enableDisplayMode("block");
13354                 this.overlay.on("mousemove", this.onMouseMove, this);
13355                 this.overlay.on("mouseup", this.onMouseUp, this);
13356             }
13357             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13358
13359             this.resizing = true;
13360             this.startBox = this.el.getBox();
13361             this.startPoint = e.getXY();
13362             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13363                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13364
13365             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13366             this.overlay.show();
13367
13368             if(this.constrainTo) {
13369                 var ct = Roo.get(this.constrainTo);
13370                 this.resizeRegion = ct.getRegion().adjust(
13371                     ct.getFrameWidth('t'),
13372                     ct.getFrameWidth('l'),
13373                     -ct.getFrameWidth('b'),
13374                     -ct.getFrameWidth('r')
13375                 );
13376             }
13377
13378             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13379             this.proxy.show();
13380             this.proxy.setBox(this.startBox);
13381             if(!this.dynamic){
13382                 this.proxy.setStyle('visibility', 'visible');
13383             }
13384         }
13385     },
13386
13387     // private
13388     onMouseDown : function(handle, e){
13389         if(this.enabled){
13390             e.stopEvent();
13391             this.activeHandle = handle;
13392             this.startSizing(e, handle);
13393         }
13394     },
13395
13396     // private
13397     onMouseUp : function(e){
13398         var size = this.resizeElement();
13399         this.resizing = false;
13400         this.handleOut();
13401         this.overlay.hide();
13402         this.proxy.hide();
13403         this.fireEvent("resize", this, size.width, size.height, e);
13404     },
13405
13406     // private
13407     updateChildSize : function(){
13408         if(this.resizeChild){
13409             var el = this.el;
13410             var child = this.resizeChild;
13411             var adj = this.adjustments;
13412             if(el.dom.offsetWidth){
13413                 var b = el.getSize(true);
13414                 child.setSize(b.width+adj[0], b.height+adj[1]);
13415             }
13416             // Second call here for IE
13417             // The first call enables instant resizing and
13418             // the second call corrects scroll bars if they
13419             // exist
13420             if(Roo.isIE){
13421                 setTimeout(function(){
13422                     if(el.dom.offsetWidth){
13423                         var b = el.getSize(true);
13424                         child.setSize(b.width+adj[0], b.height+adj[1]);
13425                     }
13426                 }, 10);
13427             }
13428         }
13429     },
13430
13431     // private
13432     snap : function(value, inc, min){
13433         if(!inc || !value) return value;
13434         var newValue = value;
13435         var m = value % inc;
13436         if(m > 0){
13437             if(m > (inc/2)){
13438                 newValue = value + (inc-m);
13439             }else{
13440                 newValue = value - m;
13441             }
13442         }
13443         return Math.max(min, newValue);
13444     },
13445
13446     // private
13447     resizeElement : function(){
13448         var box = this.proxy.getBox();
13449         if(this.updateBox){
13450             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13451         }else{
13452             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13453         }
13454         this.updateChildSize();
13455         if(!this.dynamic){
13456             this.proxy.hide();
13457         }
13458         return box;
13459     },
13460
13461     // private
13462     constrain : function(v, diff, m, mx){
13463         if(v - diff < m){
13464             diff = v - m;
13465         }else if(v - diff > mx){
13466             diff = mx - v;
13467         }
13468         return diff;
13469     },
13470
13471     // private
13472     onMouseMove : function(e){
13473         if(this.enabled){
13474             try{// try catch so if something goes wrong the user doesn't get hung
13475
13476             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13477                 return;
13478             }
13479
13480             //var curXY = this.startPoint;
13481             var curSize = this.curSize || this.startBox;
13482             var x = this.startBox.x, y = this.startBox.y;
13483             var ox = x, oy = y;
13484             var w = curSize.width, h = curSize.height;
13485             var ow = w, oh = h;
13486             var mw = this.minWidth, mh = this.minHeight;
13487             var mxw = this.maxWidth, mxh = this.maxHeight;
13488             var wi = this.widthIncrement;
13489             var hi = this.heightIncrement;
13490
13491             var eventXY = e.getXY();
13492             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13493             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13494
13495             var pos = this.activeHandle.position;
13496
13497             switch(pos){
13498                 case "east":
13499                     w += diffX;
13500                     w = Math.min(Math.max(mw, w), mxw);
13501                     break;
13502              
13503                 case "south":
13504                     h += diffY;
13505                     h = Math.min(Math.max(mh, h), mxh);
13506                     break;
13507                 case "southeast":
13508                     w += diffX;
13509                     h += diffY;
13510                     w = Math.min(Math.max(mw, w), mxw);
13511                     h = Math.min(Math.max(mh, h), mxh);
13512                     break;
13513                 case "north":
13514                     diffY = this.constrain(h, diffY, mh, mxh);
13515                     y += diffY;
13516                     h -= diffY;
13517                     break;
13518                 case "hdrag":
13519                     
13520                     if (wi) {
13521                         var adiffX = Math.abs(diffX);
13522                         var sub = (adiffX % wi); // how much 
13523                         if (sub > (wi/2)) { // far enough to snap
13524                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13525                         } else {
13526                             // remove difference.. 
13527                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13528                         }
13529                     }
13530                     x += diffX;
13531                     x = Math.max(this.minX, x);
13532                     break;
13533                 case "west":
13534                     diffX = this.constrain(w, diffX, mw, mxw);
13535                     x += diffX;
13536                     w -= diffX;
13537                     break;
13538                 case "northeast":
13539                     w += diffX;
13540                     w = Math.min(Math.max(mw, w), mxw);
13541                     diffY = this.constrain(h, diffY, mh, mxh);
13542                     y += diffY;
13543                     h -= diffY;
13544                     break;
13545                 case "northwest":
13546                     diffX = this.constrain(w, diffX, mw, mxw);
13547                     diffY = this.constrain(h, diffY, mh, mxh);
13548                     y += diffY;
13549                     h -= diffY;
13550                     x += diffX;
13551                     w -= diffX;
13552                     break;
13553                case "southwest":
13554                     diffX = this.constrain(w, diffX, mw, mxw);
13555                     h += diffY;
13556                     h = Math.min(Math.max(mh, h), mxh);
13557                     x += diffX;
13558                     w -= diffX;
13559                     break;
13560             }
13561
13562             var sw = this.snap(w, wi, mw);
13563             var sh = this.snap(h, hi, mh);
13564             if(sw != w || sh != h){
13565                 switch(pos){
13566                     case "northeast":
13567                         y -= sh - h;
13568                     break;
13569                     case "north":
13570                         y -= sh - h;
13571                         break;
13572                     case "southwest":
13573                         x -= sw - w;
13574                     break;
13575                     case "west":
13576                         x -= sw - w;
13577                         break;
13578                     case "northwest":
13579                         x -= sw - w;
13580                         y -= sh - h;
13581                     break;
13582                 }
13583                 w = sw;
13584                 h = sh;
13585             }
13586
13587             if(this.preserveRatio){
13588                 switch(pos){
13589                     case "southeast":
13590                     case "east":
13591                         h = oh * (w/ow);
13592                         h = Math.min(Math.max(mh, h), mxh);
13593                         w = ow * (h/oh);
13594                        break;
13595                     case "south":
13596                         w = ow * (h/oh);
13597                         w = Math.min(Math.max(mw, w), mxw);
13598                         h = oh * (w/ow);
13599                         break;
13600                     case "northeast":
13601                         w = ow * (h/oh);
13602                         w = Math.min(Math.max(mw, w), mxw);
13603                         h = oh * (w/ow);
13604                     break;
13605                     case "north":
13606                         var tw = w;
13607                         w = ow * (h/oh);
13608                         w = Math.min(Math.max(mw, w), mxw);
13609                         h = oh * (w/ow);
13610                         x += (tw - w) / 2;
13611                         break;
13612                     case "southwest":
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         var tw = w;
13616                         w = ow * (h/oh);
13617                         x += tw - w;
13618                         break;
13619                     case "west":
13620                         var th = h;
13621                         h = oh * (w/ow);
13622                         h = Math.min(Math.max(mh, h), mxh);
13623                         y += (th - h) / 2;
13624                         var tw = w;
13625                         w = ow * (h/oh);
13626                         x += tw - w;
13627                        break;
13628                     case "northwest":
13629                         var tw = w;
13630                         var th = h;
13631                         h = oh * (w/ow);
13632                         h = Math.min(Math.max(mh, h), mxh);
13633                         w = ow * (h/oh);
13634                         y += th - h;
13635                         x += tw - w;
13636                        break;
13637
13638                 }
13639             }
13640             if (pos == 'hdrag') {
13641                 w = ow;
13642             }
13643             this.proxy.setBounds(x, y, w, h);
13644             if(this.dynamic){
13645                 this.resizeElement();
13646             }
13647             }catch(e){}
13648         }
13649     },
13650
13651     // private
13652     handleOver : function(){
13653         if(this.enabled){
13654             this.el.addClass("x-resizable-over");
13655         }
13656     },
13657
13658     // private
13659     handleOut : function(){
13660         if(!this.resizing){
13661             this.el.removeClass("x-resizable-over");
13662         }
13663     },
13664
13665     /**
13666      * Returns the element this component is bound to.
13667      * @return {Roo.Element}
13668      */
13669     getEl : function(){
13670         return this.el;
13671     },
13672
13673     /**
13674      * Returns the resizeChild element (or null).
13675      * @return {Roo.Element}
13676      */
13677     getResizeChild : function(){
13678         return this.resizeChild;
13679     },
13680
13681     /**
13682      * Destroys this resizable. If the element was wrapped and
13683      * removeEl is not true then the element remains.
13684      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13685      */
13686     destroy : function(removeEl){
13687         this.proxy.remove();
13688         if(this.overlay){
13689             this.overlay.removeAllListeners();
13690             this.overlay.remove();
13691         }
13692         var ps = Roo.Resizable.positions;
13693         for(var k in ps){
13694             if(typeof ps[k] != "function" && this[ps[k]]){
13695                 var h = this[ps[k]];
13696                 h.el.removeAllListeners();
13697                 h.el.remove();
13698             }
13699         }
13700         if(removeEl){
13701             this.el.update("");
13702             this.el.remove();
13703         }
13704     }
13705 });
13706
13707 // private
13708 // hash to map config positions to true positions
13709 Roo.Resizable.positions = {
13710     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13711     hd: "hdrag"
13712 };
13713
13714 // private
13715 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13716     if(!this.tpl){
13717         // only initialize the template if resizable is used
13718         var tpl = Roo.DomHelper.createTemplate(
13719             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13720         );
13721         tpl.compile();
13722         Roo.Resizable.Handle.prototype.tpl = tpl;
13723     }
13724     this.position = pos;
13725     this.rz = rz;
13726     // show north drag fro topdra
13727     var handlepos = pos == 'hdrag' ? 'north' : pos;
13728     
13729     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13730     if (pos == 'hdrag') {
13731         this.el.setStyle('cursor', 'pointer');
13732     }
13733     this.el.unselectable();
13734     if(transparent){
13735         this.el.setOpacity(0);
13736     }
13737     this.el.on("mousedown", this.onMouseDown, this);
13738     if(!disableTrackOver){
13739         this.el.on("mouseover", this.onMouseOver, this);
13740         this.el.on("mouseout", this.onMouseOut, this);
13741     }
13742 };
13743
13744 // private
13745 Roo.Resizable.Handle.prototype = {
13746     afterResize : function(rz){
13747         // do nothing
13748     },
13749     // private
13750     onMouseDown : function(e){
13751         this.rz.onMouseDown(this, e);
13752     },
13753     // private
13754     onMouseOver : function(e){
13755         this.rz.handleOver(this, e);
13756     },
13757     // private
13758     onMouseOut : function(e){
13759         this.rz.handleOut(this, e);
13760     }
13761 };/*
13762  * Based on:
13763  * Ext JS Library 1.1.1
13764  * Copyright(c) 2006-2007, Ext JS, LLC.
13765  *
13766  * Originally Released Under LGPL - original licence link has changed is not relivant.
13767  *
13768  * Fork - LGPL
13769  * <script type="text/javascript">
13770  */
13771
13772 /**
13773  * @class Roo.Editor
13774  * @extends Roo.Component
13775  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13776  * @constructor
13777  * Create a new Editor
13778  * @param {Roo.form.Field} field The Field object (or descendant)
13779  * @param {Object} config The config object
13780  */
13781 Roo.Editor = function(field, config){
13782     Roo.Editor.superclass.constructor.call(this, config);
13783     this.field = field;
13784     this.addEvents({
13785         /**
13786              * @event beforestartedit
13787              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13788              * false from the handler of this event.
13789              * @param {Editor} this
13790              * @param {Roo.Element} boundEl The underlying element bound to this editor
13791              * @param {Mixed} value The field value being set
13792              */
13793         "beforestartedit" : true,
13794         /**
13795              * @event startedit
13796              * Fires when this editor is displayed
13797              * @param {Roo.Element} boundEl The underlying element bound to this editor
13798              * @param {Mixed} value The starting field value
13799              */
13800         "startedit" : true,
13801         /**
13802              * @event beforecomplete
13803              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13804              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13805              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13806              * event will not fire since no edit actually occurred.
13807              * @param {Editor} this
13808              * @param {Mixed} value The current field value
13809              * @param {Mixed} startValue The original field value
13810              */
13811         "beforecomplete" : true,
13812         /**
13813              * @event complete
13814              * Fires after editing is complete and any changed value has been written to the underlying field.
13815              * @param {Editor} this
13816              * @param {Mixed} value The current field value
13817              * @param {Mixed} startValue The original field value
13818              */
13819         "complete" : true,
13820         /**
13821          * @event specialkey
13822          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13823          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13824          * @param {Roo.form.Field} this
13825          * @param {Roo.EventObject} e The event object
13826          */
13827         "specialkey" : true
13828     });
13829 };
13830
13831 Roo.extend(Roo.Editor, Roo.Component, {
13832     /**
13833      * @cfg {Boolean/String} autosize
13834      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13835      * or "height" to adopt the height only (defaults to false)
13836      */
13837     /**
13838      * @cfg {Boolean} revertInvalid
13839      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13840      * validation fails (defaults to true)
13841      */
13842     /**
13843      * @cfg {Boolean} ignoreNoChange
13844      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13845      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13846      * will never be ignored.
13847      */
13848     /**
13849      * @cfg {Boolean} hideEl
13850      * False to keep the bound element visible while the editor is displayed (defaults to true)
13851      */
13852     /**
13853      * @cfg {Mixed} value
13854      * The data value of the underlying field (defaults to "")
13855      */
13856     value : "",
13857     /**
13858      * @cfg {String} alignment
13859      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13860      */
13861     alignment: "c-c?",
13862     /**
13863      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13864      * for bottom-right shadow (defaults to "frame")
13865      */
13866     shadow : "frame",
13867     /**
13868      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13869      */
13870     constrain : false,
13871     /**
13872      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13873      */
13874     completeOnEnter : false,
13875     /**
13876      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13877      */
13878     cancelOnEsc : false,
13879     /**
13880      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13881      */
13882     updateEl : false,
13883
13884     // private
13885     onRender : function(ct, position){
13886         this.el = new Roo.Layer({
13887             shadow: this.shadow,
13888             cls: "x-editor",
13889             parentEl : ct,
13890             shim : this.shim,
13891             shadowOffset:4,
13892             id: this.id,
13893             constrain: this.constrain
13894         });
13895         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13896         if(this.field.msgTarget != 'title'){
13897             this.field.msgTarget = 'qtip';
13898         }
13899         this.field.render(this.el);
13900         if(Roo.isGecko){
13901             this.field.el.dom.setAttribute('autocomplete', 'off');
13902         }
13903         this.field.on("specialkey", this.onSpecialKey, this);
13904         if(this.swallowKeys){
13905             this.field.el.swallowEvent(['keydown','keypress']);
13906         }
13907         this.field.show();
13908         this.field.on("blur", this.onBlur, this);
13909         if(this.field.grow){
13910             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13911         }
13912     },
13913
13914     onSpecialKey : function(field, e){
13915         //Roo.log('editor onSpecialKey');
13916         if(this.completeOnEnter && e.getKey() == e.ENTER){
13917             e.stopEvent();
13918             this.completeEdit();
13919         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13920             this.cancelEdit();
13921         }else{
13922             this.fireEvent('specialkey', field, e);
13923         }
13924     },
13925
13926     /**
13927      * Starts the editing process and shows the editor.
13928      * @param {String/HTMLElement/Element} el The element to edit
13929      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13930       * to the innerHTML of el.
13931      */
13932     startEdit : function(el, value){
13933         if(this.editing){
13934             this.completeEdit();
13935         }
13936         this.boundEl = Roo.get(el);
13937         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13938         if(!this.rendered){
13939             this.render(this.parentEl || document.body);
13940         }
13941         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13942             return;
13943         }
13944         this.startValue = v;
13945         this.field.setValue(v);
13946         if(this.autoSize){
13947             var sz = this.boundEl.getSize();
13948             switch(this.autoSize){
13949                 case "width":
13950                 this.setSize(sz.width,  "");
13951                 break;
13952                 case "height":
13953                 this.setSize("",  sz.height);
13954                 break;
13955                 default:
13956                 this.setSize(sz.width,  sz.height);
13957             }
13958         }
13959         this.el.alignTo(this.boundEl, this.alignment);
13960         this.editing = true;
13961         if(Roo.QuickTips){
13962             Roo.QuickTips.disable();
13963         }
13964         this.show();
13965     },
13966
13967     /**
13968      * Sets the height and width of this editor.
13969      * @param {Number} width The new width
13970      * @param {Number} height The new height
13971      */
13972     setSize : function(w, h){
13973         this.field.setSize(w, h);
13974         if(this.el){
13975             this.el.sync();
13976         }
13977     },
13978
13979     /**
13980      * Realigns the editor to the bound field based on the current alignment config value.
13981      */
13982     realign : function(){
13983         this.el.alignTo(this.boundEl, this.alignment);
13984     },
13985
13986     /**
13987      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13988      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13989      */
13990     completeEdit : function(remainVisible){
13991         if(!this.editing){
13992             return;
13993         }
13994         var v = this.getValue();
13995         if(this.revertInvalid !== false && !this.field.isValid()){
13996             v = this.startValue;
13997             this.cancelEdit(true);
13998         }
13999         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14000             this.editing = false;
14001             this.hide();
14002             return;
14003         }
14004         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14005             this.editing = false;
14006             if(this.updateEl && this.boundEl){
14007                 this.boundEl.update(v);
14008             }
14009             if(remainVisible !== true){
14010                 this.hide();
14011             }
14012             this.fireEvent("complete", this, v, this.startValue);
14013         }
14014     },
14015
14016     // private
14017     onShow : function(){
14018         this.el.show();
14019         if(this.hideEl !== false){
14020             this.boundEl.hide();
14021         }
14022         this.field.show();
14023         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14024             this.fixIEFocus = true;
14025             this.deferredFocus.defer(50, this);
14026         }else{
14027             this.field.focus();
14028         }
14029         this.fireEvent("startedit", this.boundEl, this.startValue);
14030     },
14031
14032     deferredFocus : function(){
14033         if(this.editing){
14034             this.field.focus();
14035         }
14036     },
14037
14038     /**
14039      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14040      * reverted to the original starting value.
14041      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14042      * cancel (defaults to false)
14043      */
14044     cancelEdit : function(remainVisible){
14045         if(this.editing){
14046             this.setValue(this.startValue);
14047             if(remainVisible !== true){
14048                 this.hide();
14049             }
14050         }
14051     },
14052
14053     // private
14054     onBlur : function(){
14055         if(this.allowBlur !== true && this.editing){
14056             this.completeEdit();
14057         }
14058     },
14059
14060     // private
14061     onHide : function(){
14062         if(this.editing){
14063             this.completeEdit();
14064             return;
14065         }
14066         this.field.blur();
14067         if(this.field.collapse){
14068             this.field.collapse();
14069         }
14070         this.el.hide();
14071         if(this.hideEl !== false){
14072             this.boundEl.show();
14073         }
14074         if(Roo.QuickTips){
14075             Roo.QuickTips.enable();
14076         }
14077     },
14078
14079     /**
14080      * Sets the data value of the editor
14081      * @param {Mixed} value Any valid value supported by the underlying field
14082      */
14083     setValue : function(v){
14084         this.field.setValue(v);
14085     },
14086
14087     /**
14088      * Gets the data value of the editor
14089      * @return {Mixed} The data value
14090      */
14091     getValue : function(){
14092         return this.field.getValue();
14093     }
14094 });/*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104  
14105 /**
14106  * @class Roo.BasicDialog
14107  * @extends Roo.util.Observable
14108  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14109  * <pre><code>
14110 var dlg = new Roo.BasicDialog("my-dlg", {
14111     height: 200,
14112     width: 300,
14113     minHeight: 100,
14114     minWidth: 150,
14115     modal: true,
14116     proxyDrag: true,
14117     shadow: true
14118 });
14119 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14120 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14121 dlg.addButton('Cancel', dlg.hide, dlg);
14122 dlg.show();
14123 </code></pre>
14124   <b>A Dialog should always be a direct child of the body element.</b>
14125  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14126  * @cfg {String} title Default text to display in the title bar (defaults to null)
14127  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14128  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14129  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14130  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14131  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14132  * (defaults to null with no animation)
14133  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14134  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14135  * property for valid values (defaults to 'all')
14136  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14137  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14138  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14139  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14140  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14141  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14142  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14143  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14144  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14145  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14146  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14147  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14148  * draggable = true (defaults to false)
14149  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14150  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14151  * shadow (defaults to false)
14152  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14153  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14154  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14155  * @cfg {Array} buttons Array of buttons
14156  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14157  * @constructor
14158  * Create a new BasicDialog.
14159  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14160  * @param {Object} config Configuration options
14161  */
14162 Roo.BasicDialog = function(el, config){
14163     this.el = Roo.get(el);
14164     var dh = Roo.DomHelper;
14165     if(!this.el && config && config.autoCreate){
14166         if(typeof config.autoCreate == "object"){
14167             if(!config.autoCreate.id){
14168                 config.autoCreate.id = el;
14169             }
14170             this.el = dh.append(document.body,
14171                         config.autoCreate, true);
14172         }else{
14173             this.el = dh.append(document.body,
14174                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14175         }
14176     }
14177     el = this.el;
14178     el.setDisplayed(true);
14179     el.hide = this.hideAction;
14180     this.id = el.id;
14181     el.addClass("x-dlg");
14182
14183     Roo.apply(this, config);
14184
14185     this.proxy = el.createProxy("x-dlg-proxy");
14186     this.proxy.hide = this.hideAction;
14187     this.proxy.setOpacity(.5);
14188     this.proxy.hide();
14189
14190     if(config.width){
14191         el.setWidth(config.width);
14192     }
14193     if(config.height){
14194         el.setHeight(config.height);
14195     }
14196     this.size = el.getSize();
14197     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14198         this.xy = [config.x,config.y];
14199     }else{
14200         this.xy = el.getCenterXY(true);
14201     }
14202     /** The header element @type Roo.Element */
14203     this.header = el.child("> .x-dlg-hd");
14204     /** The body element @type Roo.Element */
14205     this.body = el.child("> .x-dlg-bd");
14206     /** The footer element @type Roo.Element */
14207     this.footer = el.child("> .x-dlg-ft");
14208
14209     if(!this.header){
14210         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14211     }
14212     if(!this.body){
14213         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14214     }
14215
14216     this.header.unselectable();
14217     if(this.title){
14218         this.header.update(this.title);
14219     }
14220     // this element allows the dialog to be focused for keyboard event
14221     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14222     this.focusEl.swallowEvent("click", true);
14223
14224     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14225
14226     // wrap the body and footer for special rendering
14227     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14228     if(this.footer){
14229         this.bwrap.dom.appendChild(this.footer.dom);
14230     }
14231
14232     this.bg = this.el.createChild({
14233         tag: "div", cls:"x-dlg-bg",
14234         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14235     });
14236     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14237
14238
14239     if(this.autoScroll !== false && !this.autoTabs){
14240         this.body.setStyle("overflow", "auto");
14241     }
14242
14243     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14244
14245     if(this.closable !== false){
14246         this.el.addClass("x-dlg-closable");
14247         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14248         this.close.on("click", this.closeClick, this);
14249         this.close.addClassOnOver("x-dlg-close-over");
14250     }
14251     if(this.collapsible !== false){
14252         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14253         this.collapseBtn.on("click", this.collapseClick, this);
14254         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14255         this.header.on("dblclick", this.collapseClick, this);
14256     }
14257     if(this.resizable !== false){
14258         this.el.addClass("x-dlg-resizable");
14259         this.resizer = new Roo.Resizable(el, {
14260             minWidth: this.minWidth || 80,
14261             minHeight:this.minHeight || 80,
14262             handles: this.resizeHandles || "all",
14263             pinned: true
14264         });
14265         this.resizer.on("beforeresize", this.beforeResize, this);
14266         this.resizer.on("resize", this.onResize, this);
14267     }
14268     if(this.draggable !== false){
14269         el.addClass("x-dlg-draggable");
14270         if (!this.proxyDrag) {
14271             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14272         }
14273         else {
14274             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14275         }
14276         dd.setHandleElId(this.header.id);
14277         dd.endDrag = this.endMove.createDelegate(this);
14278         dd.startDrag = this.startMove.createDelegate(this);
14279         dd.onDrag = this.onDrag.createDelegate(this);
14280         dd.scroll = false;
14281         this.dd = dd;
14282     }
14283     if(this.modal){
14284         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14285         this.mask.enableDisplayMode("block");
14286         this.mask.hide();
14287         this.el.addClass("x-dlg-modal");
14288     }
14289     if(this.shadow){
14290         this.shadow = new Roo.Shadow({
14291             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14292             offset : this.shadowOffset
14293         });
14294     }else{
14295         this.shadowOffset = 0;
14296     }
14297     if(Roo.useShims && this.shim !== false){
14298         this.shim = this.el.createShim();
14299         this.shim.hide = this.hideAction;
14300         this.shim.hide();
14301     }else{
14302         this.shim = false;
14303     }
14304     if(this.autoTabs){
14305         this.initTabs();
14306     }
14307     if (this.buttons) { 
14308         var bts= this.buttons;
14309         this.buttons = [];
14310         Roo.each(bts, function(b) {
14311             this.addButton(b);
14312         }, this);
14313     }
14314     
14315     
14316     this.addEvents({
14317         /**
14318          * @event keydown
14319          * Fires when a key is pressed
14320          * @param {Roo.BasicDialog} this
14321          * @param {Roo.EventObject} e
14322          */
14323         "keydown" : true,
14324         /**
14325          * @event move
14326          * Fires when this dialog is moved by the user.
14327          * @param {Roo.BasicDialog} this
14328          * @param {Number} x The new page X
14329          * @param {Number} y The new page Y
14330          */
14331         "move" : true,
14332         /**
14333          * @event resize
14334          * Fires when this dialog is resized by the user.
14335          * @param {Roo.BasicDialog} this
14336          * @param {Number} width The new width
14337          * @param {Number} height The new height
14338          */
14339         "resize" : true,
14340         /**
14341          * @event beforehide
14342          * Fires before this dialog is hidden.
14343          * @param {Roo.BasicDialog} this
14344          */
14345         "beforehide" : true,
14346         /**
14347          * @event hide
14348          * Fires when this dialog is hidden.
14349          * @param {Roo.BasicDialog} this
14350          */
14351         "hide" : true,
14352         /**
14353          * @event beforeshow
14354          * Fires before this dialog is shown.
14355          * @param {Roo.BasicDialog} this
14356          */
14357         "beforeshow" : true,
14358         /**
14359          * @event show
14360          * Fires when this dialog is shown.
14361          * @param {Roo.BasicDialog} this
14362          */
14363         "show" : true
14364     });
14365     el.on("keydown", this.onKeyDown, this);
14366     el.on("mousedown", this.toFront, this);
14367     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14368     this.el.hide();
14369     Roo.DialogManager.register(this);
14370     Roo.BasicDialog.superclass.constructor.call(this);
14371 };
14372
14373 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14374     shadowOffset: Roo.isIE ? 6 : 5,
14375     minHeight: 80,
14376     minWidth: 200,
14377     minButtonWidth: 75,
14378     defaultButton: null,
14379     buttonAlign: "right",
14380     tabTag: 'div',
14381     firstShow: true,
14382
14383     /**
14384      * Sets the dialog title text
14385      * @param {String} text The title text to display
14386      * @return {Roo.BasicDialog} this
14387      */
14388     setTitle : function(text){
14389         this.header.update(text);
14390         return this;
14391     },
14392
14393     // private
14394     closeClick : function(){
14395         this.hide();
14396     },
14397
14398     // private
14399     collapseClick : function(){
14400         this[this.collapsed ? "expand" : "collapse"]();
14401     },
14402
14403     /**
14404      * Collapses the dialog to its minimized state (only the title bar is visible).
14405      * Equivalent to the user clicking the collapse dialog button.
14406      */
14407     collapse : function(){
14408         if(!this.collapsed){
14409             this.collapsed = true;
14410             this.el.addClass("x-dlg-collapsed");
14411             this.restoreHeight = this.el.getHeight();
14412             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14413         }
14414     },
14415
14416     /**
14417      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14418      * clicking the expand dialog button.
14419      */
14420     expand : function(){
14421         if(this.collapsed){
14422             this.collapsed = false;
14423             this.el.removeClass("x-dlg-collapsed");
14424             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14425         }
14426     },
14427
14428     /**
14429      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14430      * @return {Roo.TabPanel} The tabs component
14431      */
14432     initTabs : function(){
14433         var tabs = this.getTabs();
14434         while(tabs.getTab(0)){
14435             tabs.removeTab(0);
14436         }
14437         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14438             var dom = el.dom;
14439             tabs.addTab(Roo.id(dom), dom.title);
14440             dom.title = "";
14441         });
14442         tabs.activate(0);
14443         return tabs;
14444     },
14445
14446     // private
14447     beforeResize : function(){
14448         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14449     },
14450
14451     // private
14452     onResize : function(){
14453         this.refreshSize();
14454         this.syncBodyHeight();
14455         this.adjustAssets();
14456         this.focus();
14457         this.fireEvent("resize", this, this.size.width, this.size.height);
14458     },
14459
14460     // private
14461     onKeyDown : function(e){
14462         if(this.isVisible()){
14463             this.fireEvent("keydown", this, e);
14464         }
14465     },
14466
14467     /**
14468      * Resizes the dialog.
14469      * @param {Number} width
14470      * @param {Number} height
14471      * @return {Roo.BasicDialog} this
14472      */
14473     resizeTo : function(width, height){
14474         this.el.setSize(width, height);
14475         this.size = {width: width, height: height};
14476         this.syncBodyHeight();
14477         if(this.fixedcenter){
14478             this.center();
14479         }
14480         if(this.isVisible()){
14481             this.constrainXY();
14482             this.adjustAssets();
14483         }
14484         this.fireEvent("resize", this, width, height);
14485         return this;
14486     },
14487
14488
14489     /**
14490      * Resizes the dialog to fit the specified content size.
14491      * @param {Number} width
14492      * @param {Number} height
14493      * @return {Roo.BasicDialog} this
14494      */
14495     setContentSize : function(w, h){
14496         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14497         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14498         //if(!this.el.isBorderBox()){
14499             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14500             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14501         //}
14502         if(this.tabs){
14503             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14504             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14505         }
14506         this.resizeTo(w, h);
14507         return this;
14508     },
14509
14510     /**
14511      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14512      * executed in response to a particular key being pressed while the dialog is active.
14513      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14514      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      * @return {Roo.BasicDialog} this
14518      */
14519     addKeyListener : function(key, fn, scope){
14520         var keyCode, shift, ctrl, alt;
14521         if(typeof key == "object" && !(key instanceof Array)){
14522             keyCode = key["key"];
14523             shift = key["shift"];
14524             ctrl = key["ctrl"];
14525             alt = key["alt"];
14526         }else{
14527             keyCode = key;
14528         }
14529         var handler = function(dlg, e){
14530             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14531                 var k = e.getKey();
14532                 if(keyCode instanceof Array){
14533                     for(var i = 0, len = keyCode.length; i < len; i++){
14534                         if(keyCode[i] == k){
14535                           fn.call(scope || window, dlg, k, e);
14536                           return;
14537                         }
14538                     }
14539                 }else{
14540                     if(k == keyCode){
14541                         fn.call(scope || window, dlg, k, e);
14542                     }
14543                 }
14544             }
14545         };
14546         this.on("keydown", handler);
14547         return this;
14548     },
14549
14550     /**
14551      * Returns the TabPanel component (creates it if it doesn't exist).
14552      * Note: If you wish to simply check for the existence of tabs without creating them,
14553      * check for a null 'tabs' property.
14554      * @return {Roo.TabPanel} The tabs component
14555      */
14556     getTabs : function(){
14557         if(!this.tabs){
14558             this.el.addClass("x-dlg-auto-tabs");
14559             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14560             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14561         }
14562         return this.tabs;
14563     },
14564
14565     /**
14566      * Adds a button to the footer section of the dialog.
14567      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14568      * object or a valid Roo.DomHelper element config
14569      * @param {Function} handler The function called when the button is clicked
14570      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14571      * @return {Roo.Button} The new button
14572      */
14573     addButton : function(config, handler, scope){
14574         var dh = Roo.DomHelper;
14575         if(!this.footer){
14576             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14577         }
14578         if(!this.btnContainer){
14579             var tb = this.footer.createChild({
14580
14581                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14582                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14583             }, null, true);
14584             this.btnContainer = tb.firstChild.firstChild.firstChild;
14585         }
14586         var bconfig = {
14587             handler: handler,
14588             scope: scope,
14589             minWidth: this.minButtonWidth,
14590             hideParent:true
14591         };
14592         if(typeof config == "string"){
14593             bconfig.text = config;
14594         }else{
14595             if(config.tag){
14596                 bconfig.dhconfig = config;
14597             }else{
14598                 Roo.apply(bconfig, config);
14599             }
14600         }
14601         var fc = false;
14602         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14603             bconfig.position = Math.max(0, bconfig.position);
14604             fc = this.btnContainer.childNodes[bconfig.position];
14605         }
14606          
14607         var btn = new Roo.Button(
14608             fc ? 
14609                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14610                 : this.btnContainer.appendChild(document.createElement("td")),
14611             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14612             bconfig
14613         );
14614         this.syncBodyHeight();
14615         if(!this.buttons){
14616             /**
14617              * Array of all the buttons that have been added to this dialog via addButton
14618              * @type Array
14619              */
14620             this.buttons = [];
14621         }
14622         this.buttons.push(btn);
14623         return btn;
14624     },
14625
14626     /**
14627      * Sets the default button to be focused when the dialog is displayed.
14628      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14629      * @return {Roo.BasicDialog} this
14630      */
14631     setDefaultButton : function(btn){
14632         this.defaultButton = btn;
14633         return this;
14634     },
14635
14636     // private
14637     getHeaderFooterHeight : function(safe){
14638         var height = 0;
14639         if(this.header){
14640            height += this.header.getHeight();
14641         }
14642         if(this.footer){
14643            var fm = this.footer.getMargins();
14644             height += (this.footer.getHeight()+fm.top+fm.bottom);
14645         }
14646         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14647         height += this.centerBg.getPadding("tb");
14648         return height;
14649     },
14650
14651     // private
14652     syncBodyHeight : function(){
14653         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14654         var height = this.size.height - this.getHeaderFooterHeight(false);
14655         bd.setHeight(height-bd.getMargins("tb"));
14656         var hh = this.header.getHeight();
14657         var h = this.size.height-hh;
14658         cb.setHeight(h);
14659         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14660         bw.setHeight(h-cb.getPadding("tb"));
14661         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14662         bd.setWidth(bw.getWidth(true));
14663         if(this.tabs){
14664             this.tabs.syncHeight();
14665             if(Roo.isIE){
14666                 this.tabs.el.repaint();
14667             }
14668         }
14669     },
14670
14671     /**
14672      * Restores the previous state of the dialog if Roo.state is configured.
14673      * @return {Roo.BasicDialog} this
14674      */
14675     restoreState : function(){
14676         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14677         if(box && box.width){
14678             this.xy = [box.x, box.y];
14679             this.resizeTo(box.width, box.height);
14680         }
14681         return this;
14682     },
14683
14684     // private
14685     beforeShow : function(){
14686         this.expand();
14687         if(this.fixedcenter){
14688             this.xy = this.el.getCenterXY(true);
14689         }
14690         if(this.modal){
14691             Roo.get(document.body).addClass("x-body-masked");
14692             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14693             this.mask.show();
14694         }
14695         this.constrainXY();
14696     },
14697
14698     // private
14699     animShow : function(){
14700         var b = Roo.get(this.animateTarget).getBox();
14701         this.proxy.setSize(b.width, b.height);
14702         this.proxy.setLocation(b.x, b.y);
14703         this.proxy.show();
14704         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14705                     true, .35, this.showEl.createDelegate(this));
14706     },
14707
14708     /**
14709      * Shows the dialog.
14710      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14711      * @return {Roo.BasicDialog} this
14712      */
14713     show : function(animateTarget){
14714         if (this.fireEvent("beforeshow", this) === false){
14715             return;
14716         }
14717         if(this.syncHeightBeforeShow){
14718             this.syncBodyHeight();
14719         }else if(this.firstShow){
14720             this.firstShow = false;
14721             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14722         }
14723         this.animateTarget = animateTarget || this.animateTarget;
14724         if(!this.el.isVisible()){
14725             this.beforeShow();
14726             if(this.animateTarget && Roo.get(this.animateTarget)){
14727                 this.animShow();
14728             }else{
14729                 this.showEl();
14730             }
14731         }
14732         return this;
14733     },
14734
14735     // private
14736     showEl : function(){
14737         this.proxy.hide();
14738         this.el.setXY(this.xy);
14739         this.el.show();
14740         this.adjustAssets(true);
14741         this.toFront();
14742         this.focus();
14743         // IE peekaboo bug - fix found by Dave Fenwick
14744         if(Roo.isIE){
14745             this.el.repaint();
14746         }
14747         this.fireEvent("show", this);
14748     },
14749
14750     /**
14751      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14752      * dialog itself will receive focus.
14753      */
14754     focus : function(){
14755         if(this.defaultButton){
14756             this.defaultButton.focus();
14757         }else{
14758             this.focusEl.focus();
14759         }
14760     },
14761
14762     // private
14763     constrainXY : function(){
14764         if(this.constraintoviewport !== false){
14765             if(!this.viewSize){
14766                 if(this.container){
14767                     var s = this.container.getSize();
14768                     this.viewSize = [s.width, s.height];
14769                 }else{
14770                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14771                 }
14772             }
14773             var s = Roo.get(this.container||document).getScroll();
14774
14775             var x = this.xy[0], y = this.xy[1];
14776             var w = this.size.width, h = this.size.height;
14777             var vw = this.viewSize[0], vh = this.viewSize[1];
14778             // only move it if it needs it
14779             var moved = false;
14780             // first validate right/bottom
14781             if(x + w > vw+s.left){
14782                 x = vw - w;
14783                 moved = true;
14784             }
14785             if(y + h > vh+s.top){
14786                 y = vh - h;
14787                 moved = true;
14788             }
14789             // then make sure top/left isn't negative
14790             if(x < s.left){
14791                 x = s.left;
14792                 moved = true;
14793             }
14794             if(y < s.top){
14795                 y = s.top;
14796                 moved = true;
14797             }
14798             if(moved){
14799                 // cache xy
14800                 this.xy = [x, y];
14801                 if(this.isVisible()){
14802                     this.el.setLocation(x, y);
14803                     this.adjustAssets();
14804                 }
14805             }
14806         }
14807     },
14808
14809     // private
14810     onDrag : function(){
14811         if(!this.proxyDrag){
14812             this.xy = this.el.getXY();
14813             this.adjustAssets();
14814         }
14815     },
14816
14817     // private
14818     adjustAssets : function(doShow){
14819         var x = this.xy[0], y = this.xy[1];
14820         var w = this.size.width, h = this.size.height;
14821         if(doShow === true){
14822             if(this.shadow){
14823                 this.shadow.show(this.el);
14824             }
14825             if(this.shim){
14826                 this.shim.show();
14827             }
14828         }
14829         if(this.shadow && this.shadow.isVisible()){
14830             this.shadow.show(this.el);
14831         }
14832         if(this.shim && this.shim.isVisible()){
14833             this.shim.setBounds(x, y, w, h);
14834         }
14835     },
14836
14837     // private
14838     adjustViewport : function(w, h){
14839         if(!w || !h){
14840             w = Roo.lib.Dom.getViewWidth();
14841             h = Roo.lib.Dom.getViewHeight();
14842         }
14843         // cache the size
14844         this.viewSize = [w, h];
14845         if(this.modal && this.mask.isVisible()){
14846             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14847             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14848         }
14849         if(this.isVisible()){
14850             this.constrainXY();
14851         }
14852     },
14853
14854     /**
14855      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14856      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14857      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14858      */
14859     destroy : function(removeEl){
14860         if(this.isVisible()){
14861             this.animateTarget = null;
14862             this.hide();
14863         }
14864         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14865         if(this.tabs){
14866             this.tabs.destroy(removeEl);
14867         }
14868         Roo.destroy(
14869              this.shim,
14870              this.proxy,
14871              this.resizer,
14872              this.close,
14873              this.mask
14874         );
14875         if(this.dd){
14876             this.dd.unreg();
14877         }
14878         if(this.buttons){
14879            for(var i = 0, len = this.buttons.length; i < len; i++){
14880                this.buttons[i].destroy();
14881            }
14882         }
14883         this.el.removeAllListeners();
14884         if(removeEl === true){
14885             this.el.update("");
14886             this.el.remove();
14887         }
14888         Roo.DialogManager.unregister(this);
14889     },
14890
14891     // private
14892     startMove : function(){
14893         if(this.proxyDrag){
14894             this.proxy.show();
14895         }
14896         if(this.constraintoviewport !== false){
14897             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14898         }
14899     },
14900
14901     // private
14902     endMove : function(){
14903         if(!this.proxyDrag){
14904             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14905         }else{
14906             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14907             this.proxy.hide();
14908         }
14909         this.refreshSize();
14910         this.adjustAssets();
14911         this.focus();
14912         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14913     },
14914
14915     /**
14916      * Brings this dialog to the front of any other visible dialogs
14917      * @return {Roo.BasicDialog} this
14918      */
14919     toFront : function(){
14920         Roo.DialogManager.bringToFront(this);
14921         return this;
14922     },
14923
14924     /**
14925      * Sends this dialog to the back (under) of any other visible dialogs
14926      * @return {Roo.BasicDialog} this
14927      */
14928     toBack : function(){
14929         Roo.DialogManager.sendToBack(this);
14930         return this;
14931     },
14932
14933     /**
14934      * Centers this dialog in the viewport
14935      * @return {Roo.BasicDialog} this
14936      */
14937     center : function(){
14938         var xy = this.el.getCenterXY(true);
14939         this.moveTo(xy[0], xy[1]);
14940         return this;
14941     },
14942
14943     /**
14944      * Moves the dialog's top-left corner to the specified point
14945      * @param {Number} x
14946      * @param {Number} y
14947      * @return {Roo.BasicDialog} this
14948      */
14949     moveTo : function(x, y){
14950         this.xy = [x,y];
14951         if(this.isVisible()){
14952             this.el.setXY(this.xy);
14953             this.adjustAssets();
14954         }
14955         return this;
14956     },
14957
14958     /**
14959      * Aligns the dialog to the specified element
14960      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14961      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14962      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14963      * @return {Roo.BasicDialog} this
14964      */
14965     alignTo : function(element, position, offsets){
14966         this.xy = this.el.getAlignToXY(element, position, offsets);
14967         if(this.isVisible()){
14968             this.el.setXY(this.xy);
14969             this.adjustAssets();
14970         }
14971         return this;
14972     },
14973
14974     /**
14975      * Anchors an element to another element and realigns it when the window is resized.
14976      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14977      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14978      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14979      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14980      * is a number, it is used as the buffer delay (defaults to 50ms).
14981      * @return {Roo.BasicDialog} this
14982      */
14983     anchorTo : function(el, alignment, offsets, monitorScroll){
14984         var action = function(){
14985             this.alignTo(el, alignment, offsets);
14986         };
14987         Roo.EventManager.onWindowResize(action, this);
14988         var tm = typeof monitorScroll;
14989         if(tm != 'undefined'){
14990             Roo.EventManager.on(window, 'scroll', action, this,
14991                 {buffer: tm == 'number' ? monitorScroll : 50});
14992         }
14993         action.call(this);
14994         return this;
14995     },
14996
14997     /**
14998      * Returns true if the dialog is visible
14999      * @return {Boolean}
15000      */
15001     isVisible : function(){
15002         return this.el.isVisible();
15003     },
15004
15005     // private
15006     animHide : function(callback){
15007         var b = Roo.get(this.animateTarget).getBox();
15008         this.proxy.show();
15009         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15010         this.el.hide();
15011         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15012                     this.hideEl.createDelegate(this, [callback]));
15013     },
15014
15015     /**
15016      * Hides the dialog.
15017      * @param {Function} callback (optional) Function to call when the dialog is hidden
15018      * @return {Roo.BasicDialog} this
15019      */
15020     hide : function(callback){
15021         if (this.fireEvent("beforehide", this) === false){
15022             return;
15023         }
15024         if(this.shadow){
15025             this.shadow.hide();
15026         }
15027         if(this.shim) {
15028           this.shim.hide();
15029         }
15030         // sometimes animateTarget seems to get set.. causing problems...
15031         // this just double checks..
15032         if(this.animateTarget && Roo.get(this.animateTarget)) {
15033            this.animHide(callback);
15034         }else{
15035             this.el.hide();
15036             this.hideEl(callback);
15037         }
15038         return this;
15039     },
15040
15041     // private
15042     hideEl : function(callback){
15043         this.proxy.hide();
15044         if(this.modal){
15045             this.mask.hide();
15046             Roo.get(document.body).removeClass("x-body-masked");
15047         }
15048         this.fireEvent("hide", this);
15049         if(typeof callback == "function"){
15050             callback();
15051         }
15052     },
15053
15054     // private
15055     hideAction : function(){
15056         this.setLeft("-10000px");
15057         this.setTop("-10000px");
15058         this.setStyle("visibility", "hidden");
15059     },
15060
15061     // private
15062     refreshSize : function(){
15063         this.size = this.el.getSize();
15064         this.xy = this.el.getXY();
15065         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15066     },
15067
15068     // private
15069     // z-index is managed by the DialogManager and may be overwritten at any time
15070     setZIndex : function(index){
15071         if(this.modal){
15072             this.mask.setStyle("z-index", index);
15073         }
15074         if(this.shim){
15075             this.shim.setStyle("z-index", ++index);
15076         }
15077         if(this.shadow){
15078             this.shadow.setZIndex(++index);
15079         }
15080         this.el.setStyle("z-index", ++index);
15081         if(this.proxy){
15082             this.proxy.setStyle("z-index", ++index);
15083         }
15084         if(this.resizer){
15085             this.resizer.proxy.setStyle("z-index", ++index);
15086         }
15087
15088         this.lastZIndex = index;
15089     },
15090
15091     /**
15092      * Returns the element for this dialog
15093      * @return {Roo.Element} The underlying dialog Element
15094      */
15095     getEl : function(){
15096         return this.el;
15097     }
15098 });
15099
15100 /**
15101  * @class Roo.DialogManager
15102  * Provides global access to BasicDialogs that have been created and
15103  * support for z-indexing (layering) multiple open dialogs.
15104  */
15105 Roo.DialogManager = function(){
15106     var list = {};
15107     var accessList = [];
15108     var front = null;
15109
15110     // private
15111     var sortDialogs = function(d1, d2){
15112         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15113     };
15114
15115     // private
15116     var orderDialogs = function(){
15117         accessList.sort(sortDialogs);
15118         var seed = Roo.DialogManager.zseed;
15119         for(var i = 0, len = accessList.length; i < len; i++){
15120             var dlg = accessList[i];
15121             if(dlg){
15122                 dlg.setZIndex(seed + (i*10));
15123             }
15124         }
15125     };
15126
15127     return {
15128         /**
15129          * The starting z-index for BasicDialogs (defaults to 9000)
15130          * @type Number The z-index value
15131          */
15132         zseed : 9000,
15133
15134         // private
15135         register : function(dlg){
15136             list[dlg.id] = dlg;
15137             accessList.push(dlg);
15138         },
15139
15140         // private
15141         unregister : function(dlg){
15142             delete list[dlg.id];
15143             var i=0;
15144             var len=0;
15145             if(!accessList.indexOf){
15146                 for(  i = 0, len = accessList.length; i < len; i++){
15147                     if(accessList[i] == dlg){
15148                         accessList.splice(i, 1);
15149                         return;
15150                     }
15151                 }
15152             }else{
15153                  i = accessList.indexOf(dlg);
15154                 if(i != -1){
15155                     accessList.splice(i, 1);
15156                 }
15157             }
15158         },
15159
15160         /**
15161          * Gets a registered dialog by id
15162          * @param {String/Object} id The id of the dialog or a dialog
15163          * @return {Roo.BasicDialog} this
15164          */
15165         get : function(id){
15166             return typeof id == "object" ? id : list[id];
15167         },
15168
15169         /**
15170          * Brings the specified dialog to the front
15171          * @param {String/Object} dlg The id of the dialog or a dialog
15172          * @return {Roo.BasicDialog} this
15173          */
15174         bringToFront : function(dlg){
15175             dlg = this.get(dlg);
15176             if(dlg != front){
15177                 front = dlg;
15178                 dlg._lastAccess = new Date().getTime();
15179                 orderDialogs();
15180             }
15181             return dlg;
15182         },
15183
15184         /**
15185          * Sends the specified dialog to the back
15186          * @param {String/Object} dlg The id of the dialog or a dialog
15187          * @return {Roo.BasicDialog} this
15188          */
15189         sendToBack : function(dlg){
15190             dlg = this.get(dlg);
15191             dlg._lastAccess = -(new Date().getTime());
15192             orderDialogs();
15193             return dlg;
15194         },
15195
15196         /**
15197          * Hides all dialogs
15198          */
15199         hideAll : function(){
15200             for(var id in list){
15201                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15202                     list[id].hide();
15203                 }
15204             }
15205         }
15206     };
15207 }();
15208
15209 /**
15210  * @class Roo.LayoutDialog
15211  * @extends Roo.BasicDialog
15212  * Dialog which provides adjustments for working with a layout in a Dialog.
15213  * Add your necessary layout config options to the dialog's config.<br>
15214  * Example usage (including a nested layout):
15215  * <pre><code>
15216 if(!dialog){
15217     dialog = new Roo.LayoutDialog("download-dlg", {
15218         modal: true,
15219         width:600,
15220         height:450,
15221         shadow:true,
15222         minWidth:500,
15223         minHeight:350,
15224         autoTabs:true,
15225         proxyDrag:true,
15226         // layout config merges with the dialog config
15227         center:{
15228             tabPosition: "top",
15229             alwaysShowTabs: true
15230         }
15231     });
15232     dialog.addKeyListener(27, dialog.hide, dialog);
15233     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15234     dialog.addButton("Build It!", this.getDownload, this);
15235
15236     // we can even add nested layouts
15237     var innerLayout = new Roo.BorderLayout("dl-inner", {
15238         east: {
15239             initialSize: 200,
15240             autoScroll:true,
15241             split:true
15242         },
15243         center: {
15244             autoScroll:true
15245         }
15246     });
15247     innerLayout.beginUpdate();
15248     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15249     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15250     innerLayout.endUpdate(true);
15251
15252     var layout = dialog.getLayout();
15253     layout.beginUpdate();
15254     layout.add("center", new Roo.ContentPanel("standard-panel",
15255                         {title: "Download the Source", fitToFrame:true}));
15256     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15257                {title: "Build your own roo.js"}));
15258     layout.getRegion("center").showPanel(sp);
15259     layout.endUpdate();
15260 }
15261 </code></pre>
15262     * @constructor
15263     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15264     * @param {Object} config configuration options
15265   */
15266 Roo.LayoutDialog = function(el, cfg){
15267     
15268     var config=  cfg;
15269     if (typeof(cfg) == 'undefined') {
15270         config = Roo.apply({}, el);
15271         // not sure why we use documentElement here.. - it should always be body.
15272         // IE7 borks horribly if we use documentElement.
15273         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15274         //config.autoCreate = true;
15275     }
15276     
15277     
15278     config.autoTabs = false;
15279     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15280     this.body.setStyle({overflow:"hidden", position:"relative"});
15281     this.layout = new Roo.BorderLayout(this.body.dom, config);
15282     this.layout.monitorWindowResize = false;
15283     this.el.addClass("x-dlg-auto-layout");
15284     // fix case when center region overwrites center function
15285     this.center = Roo.BasicDialog.prototype.center;
15286     this.on("show", this.layout.layout, this.layout, true);
15287     if (config.items) {
15288         var xitems = config.items;
15289         delete config.items;
15290         Roo.each(xitems, this.addxtype, this);
15291     }
15292     
15293     
15294 };
15295 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15296     /**
15297      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15298      * @deprecated
15299      */
15300     endUpdate : function(){
15301         this.layout.endUpdate();
15302     },
15303
15304     /**
15305      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15306      *  @deprecated
15307      */
15308     beginUpdate : function(){
15309         this.layout.beginUpdate();
15310     },
15311
15312     /**
15313      * Get the BorderLayout for this dialog
15314      * @return {Roo.BorderLayout}
15315      */
15316     getLayout : function(){
15317         return this.layout;
15318     },
15319
15320     showEl : function(){
15321         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15322         if(Roo.isIE7){
15323             this.layout.layout();
15324         }
15325     },
15326
15327     // private
15328     // Use the syncHeightBeforeShow config option to control this automatically
15329     syncBodyHeight : function(){
15330         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15331         if(this.layout){this.layout.layout();}
15332     },
15333     
15334       /**
15335      * Add an xtype element (actually adds to the layout.)
15336      * @return {Object} xdata xtype object data.
15337      */
15338     
15339     addxtype : function(c) {
15340         return this.layout.addxtype(c);
15341     }
15342 });/*
15343  * Based on:
15344  * Ext JS Library 1.1.1
15345  * Copyright(c) 2006-2007, Ext JS, LLC.
15346  *
15347  * Originally Released Under LGPL - original licence link has changed is not relivant.
15348  *
15349  * Fork - LGPL
15350  * <script type="text/javascript">
15351  */
15352  
15353 /**
15354  * @class Roo.MessageBox
15355  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15356  * Example usage:
15357  *<pre><code>
15358 // Basic alert:
15359 Roo.Msg.alert('Status', 'Changes saved successfully.');
15360
15361 // Prompt for user data:
15362 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15363     if (btn == 'ok'){
15364         // process text value...
15365     }
15366 });
15367
15368 // Show a dialog using config options:
15369 Roo.Msg.show({
15370    title:'Save Changes?',
15371    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15372    buttons: Roo.Msg.YESNOCANCEL,
15373    fn: processResult,
15374    animEl: 'elId'
15375 });
15376 </code></pre>
15377  * @singleton
15378  */
15379 Roo.MessageBox = function(){
15380     var dlg, opt, mask, waitTimer;
15381     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15382     var buttons, activeTextEl, bwidth;
15383
15384     // private
15385     var handleButton = function(button){
15386         dlg.hide();
15387         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15388     };
15389
15390     // private
15391     var handleHide = function(){
15392         if(opt && opt.cls){
15393             dlg.el.removeClass(opt.cls);
15394         }
15395         if(waitTimer){
15396             Roo.TaskMgr.stop(waitTimer);
15397             waitTimer = null;
15398         }
15399     };
15400
15401     // private
15402     var updateButtons = function(b){
15403         var width = 0;
15404         if(!b){
15405             buttons["ok"].hide();
15406             buttons["cancel"].hide();
15407             buttons["yes"].hide();
15408             buttons["no"].hide();
15409             dlg.footer.dom.style.display = 'none';
15410             return width;
15411         }
15412         dlg.footer.dom.style.display = '';
15413         for(var k in buttons){
15414             if(typeof buttons[k] != "function"){
15415                 if(b[k]){
15416                     buttons[k].show();
15417                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15418                     width += buttons[k].el.getWidth()+15;
15419                 }else{
15420                     buttons[k].hide();
15421                 }
15422             }
15423         }
15424         return width;
15425     };
15426
15427     // private
15428     var handleEsc = function(d, k, e){
15429         if(opt && opt.closable !== false){
15430             dlg.hide();
15431         }
15432         if(e){
15433             e.stopEvent();
15434         }
15435     };
15436
15437     return {
15438         /**
15439          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15440          * @return {Roo.BasicDialog} The BasicDialog element
15441          */
15442         getDialog : function(){
15443            if(!dlg){
15444                 dlg = new Roo.BasicDialog("x-msg-box", {
15445                     autoCreate : true,
15446                     shadow: true,
15447                     draggable: true,
15448                     resizable:false,
15449                     constraintoviewport:false,
15450                     fixedcenter:true,
15451                     collapsible : false,
15452                     shim:true,
15453                     modal: true,
15454                     width:400, height:100,
15455                     buttonAlign:"center",
15456                     closeClick : function(){
15457                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15458                             handleButton("no");
15459                         }else{
15460                             handleButton("cancel");
15461                         }
15462                     }
15463                 });
15464                 dlg.on("hide", handleHide);
15465                 mask = dlg.mask;
15466                 dlg.addKeyListener(27, handleEsc);
15467                 buttons = {};
15468                 var bt = this.buttonText;
15469                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15470                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15471                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15472                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15473                 bodyEl = dlg.body.createChild({
15474
15475                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15476                 });
15477                 msgEl = bodyEl.dom.firstChild;
15478                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15479                 textboxEl.enableDisplayMode();
15480                 textboxEl.addKeyListener([10,13], function(){
15481                     if(dlg.isVisible() && opt && opt.buttons){
15482                         if(opt.buttons.ok){
15483                             handleButton("ok");
15484                         }else if(opt.buttons.yes){
15485                             handleButton("yes");
15486                         }
15487                     }
15488                 });
15489                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15490                 textareaEl.enableDisplayMode();
15491                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15492                 progressEl.enableDisplayMode();
15493                 var pf = progressEl.dom.firstChild;
15494                 if (pf) {
15495                     pp = Roo.get(pf.firstChild);
15496                     pp.setHeight(pf.offsetHeight);
15497                 }
15498                 
15499             }
15500             return dlg;
15501         },
15502
15503         /**
15504          * Updates the message box body text
15505          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15506          * the XHTML-compliant non-breaking space character '&amp;#160;')
15507          * @return {Roo.MessageBox} This message box
15508          */
15509         updateText : function(text){
15510             if(!dlg.isVisible() && !opt.width){
15511                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15512             }
15513             msgEl.innerHTML = text || '&#160;';
15514             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15515                         Math.max(opt.minWidth || this.minWidth, bwidth));
15516             if(opt.prompt){
15517                 activeTextEl.setWidth(w);
15518             }
15519             if(dlg.isVisible()){
15520                 dlg.fixedcenter = false;
15521             }
15522             dlg.setContentSize(w, bodyEl.getHeight());
15523             if(dlg.isVisible()){
15524                 dlg.fixedcenter = true;
15525             }
15526             return this;
15527         },
15528
15529         /**
15530          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15531          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15532          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15533          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15534          * @return {Roo.MessageBox} This message box
15535          */
15536         updateProgress : function(value, text){
15537             if(text){
15538                 this.updateText(text);
15539             }
15540             if (pp) { // weird bug on my firefox - for some reason this is not defined
15541                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15542             }
15543             return this;
15544         },        
15545
15546         /**
15547          * Returns true if the message box is currently displayed
15548          * @return {Boolean} True if the message box is visible, else false
15549          */
15550         isVisible : function(){
15551             return dlg && dlg.isVisible();  
15552         },
15553
15554         /**
15555          * Hides the message box if it is displayed
15556          */
15557         hide : function(){
15558             if(this.isVisible()){
15559                 dlg.hide();
15560             }  
15561         },
15562
15563         /**
15564          * Displays a new message box, or reinitializes an existing message box, based on the config options
15565          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15566          * The following config object properties are supported:
15567          * <pre>
15568 Property    Type             Description
15569 ----------  ---------------  ------------------------------------------------------------------------------------
15570 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15571                                    closes (defaults to undefined)
15572 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15573                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15574 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15575                                    progress and wait dialogs will ignore this property and always hide the
15576                                    close button as they can only be closed programmatically.
15577 cls               String           A custom CSS class to apply to the message box element
15578 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15579                                    displayed (defaults to 75)
15580 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15581                                    function will be btn (the name of the button that was clicked, if applicable,
15582                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15583                                    Progress and wait dialogs will ignore this option since they do not respond to
15584                                    user actions and can only be closed programmatically, so any required function
15585                                    should be called by the same code after it closes the dialog.
15586 icon              String           A CSS class that provides a background image to be used as an icon for
15587                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15588 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15589 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15590 modal             Boolean          False to allow user interaction with the page while the message box is
15591                                    displayed (defaults to true)
15592 msg               String           A string that will replace the existing message box body text (defaults
15593                                    to the XHTML-compliant non-breaking space character '&#160;')
15594 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15595 progress          Boolean          True to display a progress bar (defaults to false)
15596 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15597 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15598 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15599 title             String           The title text
15600 value             String           The string value to set into the active textbox element if displayed
15601 wait              Boolean          True to display a progress bar (defaults to false)
15602 width             Number           The width of the dialog in pixels
15603 </pre>
15604          *
15605          * Example usage:
15606          * <pre><code>
15607 Roo.Msg.show({
15608    title: 'Address',
15609    msg: 'Please enter your address:',
15610    width: 300,
15611    buttons: Roo.MessageBox.OKCANCEL,
15612    multiline: true,
15613    fn: saveAddress,
15614    animEl: 'addAddressBtn'
15615 });
15616 </code></pre>
15617          * @param {Object} config Configuration options
15618          * @return {Roo.MessageBox} This message box
15619          */
15620         show : function(options){
15621             if(this.isVisible()){
15622                 this.hide();
15623             }
15624             var d = this.getDialog();
15625             opt = options;
15626             d.setTitle(opt.title || "&#160;");
15627             d.close.setDisplayed(opt.closable !== false);
15628             activeTextEl = textboxEl;
15629             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15630             if(opt.prompt){
15631                 if(opt.multiline){
15632                     textboxEl.hide();
15633                     textareaEl.show();
15634                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15635                         opt.multiline : this.defaultTextHeight);
15636                     activeTextEl = textareaEl;
15637                 }else{
15638                     textboxEl.show();
15639                     textareaEl.hide();
15640                 }
15641             }else{
15642                 textboxEl.hide();
15643                 textareaEl.hide();
15644             }
15645             progressEl.setDisplayed(opt.progress === true);
15646             this.updateProgress(0);
15647             activeTextEl.dom.value = opt.value || "";
15648             if(opt.prompt){
15649                 dlg.setDefaultButton(activeTextEl);
15650             }else{
15651                 var bs = opt.buttons;
15652                 var db = null;
15653                 if(bs && bs.ok){
15654                     db = buttons["ok"];
15655                 }else if(bs && bs.yes){
15656                     db = buttons["yes"];
15657                 }
15658                 dlg.setDefaultButton(db);
15659             }
15660             bwidth = updateButtons(opt.buttons);
15661             this.updateText(opt.msg);
15662             if(opt.cls){
15663                 d.el.addClass(opt.cls);
15664             }
15665             d.proxyDrag = opt.proxyDrag === true;
15666             d.modal = opt.modal !== false;
15667             d.mask = opt.modal !== false ? mask : false;
15668             if(!d.isVisible()){
15669                 // force it to the end of the z-index stack so it gets a cursor in FF
15670                 document.body.appendChild(dlg.el.dom);
15671                 d.animateTarget = null;
15672                 d.show(options.animEl);
15673             }
15674             return this;
15675         },
15676
15677         /**
15678          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15679          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15680          * and closing the message box when the process is complete.
15681          * @param {String} title The title bar text
15682          * @param {String} msg The message box body text
15683          * @return {Roo.MessageBox} This message box
15684          */
15685         progress : function(title, msg){
15686             this.show({
15687                 title : title,
15688                 msg : msg,
15689                 buttons: false,
15690                 progress:true,
15691                 closable:false,
15692                 minWidth: this.minProgressWidth,
15693                 modal : true
15694             });
15695             return this;
15696         },
15697
15698         /**
15699          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15700          * If a callback function is passed it will be called after the user clicks the button, and the
15701          * id of the button that was clicked will be passed as the only parameter to the callback
15702          * (could also be the top-right close button).
15703          * @param {String} title The title bar text
15704          * @param {String} msg The message box body text
15705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15706          * @param {Object} scope (optional) The scope of the callback function
15707          * @return {Roo.MessageBox} This message box
15708          */
15709         alert : function(title, msg, fn, scope){
15710             this.show({
15711                 title : title,
15712                 msg : msg,
15713                 buttons: this.OK,
15714                 fn: fn,
15715                 scope : scope,
15716                 modal : true
15717             });
15718             return this;
15719         },
15720
15721         /**
15722          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15723          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15724          * You are responsible for closing the message box when the process is complete.
15725          * @param {String} msg The message box body text
15726          * @param {String} title (optional) The title bar text
15727          * @return {Roo.MessageBox} This message box
15728          */
15729         wait : function(msg, title){
15730             this.show({
15731                 title : title,
15732                 msg : msg,
15733                 buttons: false,
15734                 closable:false,
15735                 progress:true,
15736                 modal:true,
15737                 width:300,
15738                 wait:true
15739             });
15740             waitTimer = Roo.TaskMgr.start({
15741                 run: function(i){
15742                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15743                 },
15744                 interval: 1000
15745             });
15746             return this;
15747         },
15748
15749         /**
15750          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15751          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15752          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15753          * @param {String} title The title bar text
15754          * @param {String} msg The message box body text
15755          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15756          * @param {Object} scope (optional) The scope of the callback function
15757          * @return {Roo.MessageBox} This message box
15758          */
15759         confirm : function(title, msg, fn, scope){
15760             this.show({
15761                 title : title,
15762                 msg : msg,
15763                 buttons: this.YESNO,
15764                 fn: fn,
15765                 scope : scope,
15766                 modal : true
15767             });
15768             return this;
15769         },
15770
15771         /**
15772          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15773          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15774          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15775          * (could also be the top-right close button) and the text that was entered will be passed as the two
15776          * parameters to the callback.
15777          * @param {String} title The title bar text
15778          * @param {String} msg The message box body text
15779          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15780          * @param {Object} scope (optional) The scope of the callback function
15781          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15782          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15783          * @return {Roo.MessageBox} This message box
15784          */
15785         prompt : function(title, msg, fn, scope, multiline){
15786             this.show({
15787                 title : title,
15788                 msg : msg,
15789                 buttons: this.OKCANCEL,
15790                 fn: fn,
15791                 minWidth:250,
15792                 scope : scope,
15793                 prompt:true,
15794                 multiline: multiline,
15795                 modal : true
15796             });
15797             return this;
15798         },
15799
15800         /**
15801          * Button config that displays a single OK button
15802          * @type Object
15803          */
15804         OK : {ok:true},
15805         /**
15806          * Button config that displays Yes and No buttons
15807          * @type Object
15808          */
15809         YESNO : {yes:true, no:true},
15810         /**
15811          * Button config that displays OK and Cancel buttons
15812          * @type Object
15813          */
15814         OKCANCEL : {ok:true, cancel:true},
15815         /**
15816          * Button config that displays Yes, No and Cancel buttons
15817          * @type Object
15818          */
15819         YESNOCANCEL : {yes:true, no:true, cancel:true},
15820
15821         /**
15822          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15823          * @type Number
15824          */
15825         defaultTextHeight : 75,
15826         /**
15827          * The maximum width in pixels of the message box (defaults to 600)
15828          * @type Number
15829          */
15830         maxWidth : 600,
15831         /**
15832          * The minimum width in pixels of the message box (defaults to 100)
15833          * @type Number
15834          */
15835         minWidth : 100,
15836         /**
15837          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15838          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15839          * @type Number
15840          */
15841         minProgressWidth : 250,
15842         /**
15843          * An object containing the default button text strings that can be overriden for localized language support.
15844          * Supported properties are: ok, cancel, yes and no.
15845          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15846          * @type Object
15847          */
15848         buttonText : {
15849             ok : "OK",
15850             cancel : "Cancel",
15851             yes : "Yes",
15852             no : "No"
15853         }
15854     };
15855 }();
15856
15857 /**
15858  * Shorthand for {@link Roo.MessageBox}
15859  */
15860 Roo.Msg = Roo.MessageBox;/*
15861  * Based on:
15862  * Ext JS Library 1.1.1
15863  * Copyright(c) 2006-2007, Ext JS, LLC.
15864  *
15865  * Originally Released Under LGPL - original licence link has changed is not relivant.
15866  *
15867  * Fork - LGPL
15868  * <script type="text/javascript">
15869  */
15870 /**
15871  * @class Roo.QuickTips
15872  * Provides attractive and customizable tooltips for any element.
15873  * @singleton
15874  */
15875 Roo.QuickTips = function(){
15876     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15877     var ce, bd, xy, dd;
15878     var visible = false, disabled = true, inited = false;
15879     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15880     
15881     var onOver = function(e){
15882         if(disabled){
15883             return;
15884         }
15885         var t = e.getTarget();
15886         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15887             return;
15888         }
15889         if(ce && t == ce.el){
15890             clearTimeout(hideProc);
15891             return;
15892         }
15893         if(t && tagEls[t.id]){
15894             tagEls[t.id].el = t;
15895             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15896             return;
15897         }
15898         var ttp, et = Roo.fly(t);
15899         var ns = cfg.namespace;
15900         if(tm.interceptTitles && t.title){
15901             ttp = t.title;
15902             t.qtip = ttp;
15903             t.removeAttribute("title");
15904             e.preventDefault();
15905         }else{
15906             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15907         }
15908         if(ttp){
15909             showProc = show.defer(tm.showDelay, tm, [{
15910                 el: t, 
15911                 text: ttp, 
15912                 width: et.getAttributeNS(ns, cfg.width),
15913                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15914                 title: et.getAttributeNS(ns, cfg.title),
15915                     cls: et.getAttributeNS(ns, cfg.cls)
15916             }]);
15917         }
15918     };
15919     
15920     var onOut = function(e){
15921         clearTimeout(showProc);
15922         var t = e.getTarget();
15923         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15924             hideProc = setTimeout(hide, tm.hideDelay);
15925         }
15926     };
15927     
15928     var onMove = function(e){
15929         if(disabled){
15930             return;
15931         }
15932         xy = e.getXY();
15933         xy[1] += 18;
15934         if(tm.trackMouse && ce){
15935             el.setXY(xy);
15936         }
15937     };
15938     
15939     var onDown = function(e){
15940         clearTimeout(showProc);
15941         clearTimeout(hideProc);
15942         if(!e.within(el)){
15943             if(tm.hideOnClick){
15944                 hide();
15945                 tm.disable();
15946                 tm.enable.defer(100, tm);
15947             }
15948         }
15949     };
15950     
15951     var getPad = function(){
15952         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15953     };
15954
15955     var show = function(o){
15956         if(disabled){
15957             return;
15958         }
15959         clearTimeout(dismissProc);
15960         ce = o;
15961         if(removeCls){ // in case manually hidden
15962             el.removeClass(removeCls);
15963             removeCls = null;
15964         }
15965         if(ce.cls){
15966             el.addClass(ce.cls);
15967             removeCls = ce.cls;
15968         }
15969         if(ce.title){
15970             tipTitle.update(ce.title);
15971             tipTitle.show();
15972         }else{
15973             tipTitle.update('');
15974             tipTitle.hide();
15975         }
15976         el.dom.style.width  = tm.maxWidth+'px';
15977         //tipBody.dom.style.width = '';
15978         tipBodyText.update(o.text);
15979         var p = getPad(), w = ce.width;
15980         if(!w){
15981             var td = tipBodyText.dom;
15982             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15983             if(aw > tm.maxWidth){
15984                 w = tm.maxWidth;
15985             }else if(aw < tm.minWidth){
15986                 w = tm.minWidth;
15987             }else{
15988                 w = aw;
15989             }
15990         }
15991         //tipBody.setWidth(w);
15992         el.setWidth(parseInt(w, 10) + p);
15993         if(ce.autoHide === false){
15994             close.setDisplayed(true);
15995             if(dd){
15996                 dd.unlock();
15997             }
15998         }else{
15999             close.setDisplayed(false);
16000             if(dd){
16001                 dd.lock();
16002             }
16003         }
16004         if(xy){
16005             el.avoidY = xy[1]-18;
16006             el.setXY(xy);
16007         }
16008         if(tm.animate){
16009             el.setOpacity(.1);
16010             el.setStyle("visibility", "visible");
16011             el.fadeIn({callback: afterShow});
16012         }else{
16013             afterShow();
16014         }
16015     };
16016     
16017     var afterShow = function(){
16018         if(ce){
16019             el.show();
16020             esc.enable();
16021             if(tm.autoDismiss && ce.autoHide !== false){
16022                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16023             }
16024         }
16025     };
16026     
16027     var hide = function(noanim){
16028         clearTimeout(dismissProc);
16029         clearTimeout(hideProc);
16030         ce = null;
16031         if(el.isVisible()){
16032             esc.disable();
16033             if(noanim !== true && tm.animate){
16034                 el.fadeOut({callback: afterHide});
16035             }else{
16036                 afterHide();
16037             } 
16038         }
16039     };
16040     
16041     var afterHide = function(){
16042         el.hide();
16043         if(removeCls){
16044             el.removeClass(removeCls);
16045             removeCls = null;
16046         }
16047     };
16048     
16049     return {
16050         /**
16051         * @cfg {Number} minWidth
16052         * The minimum width of the quick tip (defaults to 40)
16053         */
16054        minWidth : 40,
16055         /**
16056         * @cfg {Number} maxWidth
16057         * The maximum width of the quick tip (defaults to 300)
16058         */
16059        maxWidth : 300,
16060         /**
16061         * @cfg {Boolean} interceptTitles
16062         * True to automatically use the element's DOM title value if available (defaults to false)
16063         */
16064        interceptTitles : false,
16065         /**
16066         * @cfg {Boolean} trackMouse
16067         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16068         */
16069        trackMouse : false,
16070         /**
16071         * @cfg {Boolean} hideOnClick
16072         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16073         */
16074        hideOnClick : true,
16075         /**
16076         * @cfg {Number} showDelay
16077         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16078         */
16079        showDelay : 500,
16080         /**
16081         * @cfg {Number} hideDelay
16082         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16083         */
16084        hideDelay : 200,
16085         /**
16086         * @cfg {Boolean} autoHide
16087         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16088         * Used in conjunction with hideDelay.
16089         */
16090        autoHide : true,
16091         /**
16092         * @cfg {Boolean}
16093         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16094         * (defaults to true).  Used in conjunction with autoDismissDelay.
16095         */
16096        autoDismiss : true,
16097         /**
16098         * @cfg {Number}
16099         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16100         */
16101        autoDismissDelay : 5000,
16102        /**
16103         * @cfg {Boolean} animate
16104         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16105         */
16106        animate : false,
16107
16108        /**
16109         * @cfg {String} title
16110         * Title text to display (defaults to '').  This can be any valid HTML markup.
16111         */
16112         title: '',
16113        /**
16114         * @cfg {String} text
16115         * Body text to display (defaults to '').  This can be any valid HTML markup.
16116         */
16117         text : '',
16118        /**
16119         * @cfg {String} cls
16120         * A CSS class to apply to the base quick tip element (defaults to '').
16121         */
16122         cls : '',
16123        /**
16124         * @cfg {Number} width
16125         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16126         * minWidth or maxWidth.
16127         */
16128         width : null,
16129
16130     /**
16131      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16132      * or display QuickTips in a page.
16133      */
16134        init : function(){
16135           tm = Roo.QuickTips;
16136           cfg = tm.tagConfig;
16137           if(!inited){
16138               if(!Roo.isReady){ // allow calling of init() before onReady
16139                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16140                   return;
16141               }
16142               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16143               el.fxDefaults = {stopFx: true};
16144               // maximum custom styling
16145               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16146               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16147               tipTitle = el.child('h3');
16148               tipTitle.enableDisplayMode("block");
16149               tipBody = el.child('div.x-tip-bd');
16150               tipBodyText = el.child('div.x-tip-bd-inner');
16151               //bdLeft = el.child('div.x-tip-bd-left');
16152               //bdRight = el.child('div.x-tip-bd-right');
16153               close = el.child('div.x-tip-close');
16154               close.enableDisplayMode("block");
16155               close.on("click", hide);
16156               var d = Roo.get(document);
16157               d.on("mousedown", onDown);
16158               d.on("mouseover", onOver);
16159               d.on("mouseout", onOut);
16160               d.on("mousemove", onMove);
16161               esc = d.addKeyListener(27, hide);
16162               esc.disable();
16163               if(Roo.dd.DD){
16164                   dd = el.initDD("default", null, {
16165                       onDrag : function(){
16166                           el.sync();  
16167                       }
16168                   });
16169                   dd.setHandleElId(tipTitle.id);
16170                   dd.lock();
16171               }
16172               inited = true;
16173           }
16174           this.enable(); 
16175        },
16176
16177     /**
16178      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16179      * are supported:
16180      * <pre>
16181 Property    Type                   Description
16182 ----------  ---------------------  ------------------------------------------------------------------------
16183 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16184      * </ul>
16185      * @param {Object} config The config object
16186      */
16187        register : function(config){
16188            var cs = config instanceof Array ? config : arguments;
16189            for(var i = 0, len = cs.length; i < len; i++) {
16190                var c = cs[i];
16191                var target = c.target;
16192                if(target){
16193                    if(target instanceof Array){
16194                        for(var j = 0, jlen = target.length; j < jlen; j++){
16195                            tagEls[target[j]] = c;
16196                        }
16197                    }else{
16198                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16199                    }
16200                }
16201            }
16202        },
16203
16204     /**
16205      * Removes this quick tip from its element and destroys it.
16206      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16207      */
16208        unregister : function(el){
16209            delete tagEls[Roo.id(el)];
16210        },
16211
16212     /**
16213      * Enable this quick tip.
16214      */
16215        enable : function(){
16216            if(inited && disabled){
16217                locks.pop();
16218                if(locks.length < 1){
16219                    disabled = false;
16220                }
16221            }
16222        },
16223
16224     /**
16225      * Disable this quick tip.
16226      */
16227        disable : function(){
16228           disabled = true;
16229           clearTimeout(showProc);
16230           clearTimeout(hideProc);
16231           clearTimeout(dismissProc);
16232           if(ce){
16233               hide(true);
16234           }
16235           locks.push(1);
16236        },
16237
16238     /**
16239      * Returns true if the quick tip is enabled, else false.
16240      */
16241        isEnabled : function(){
16242             return !disabled;
16243        },
16244
16245         // private
16246        tagConfig : {
16247            namespace : "ext",
16248            attribute : "qtip",
16249            width : "width",
16250            target : "target",
16251            title : "qtitle",
16252            hide : "hide",
16253            cls : "qclass"
16254        }
16255    };
16256 }();
16257
16258 // backwards compat
16259 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.tree.TreePanel
16273  * @extends Roo.data.Tree
16274
16275  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16276  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16277  * @cfg {Boolean} enableDD true to enable drag and drop
16278  * @cfg {Boolean} enableDrag true to enable just drag
16279  * @cfg {Boolean} enableDrop true to enable just drop
16280  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16281  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16282  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16283  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16284  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16285  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16286  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16287  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16288  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16289  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16290  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16291  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16292  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16293  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16294  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16295  * 
16296  * @constructor
16297  * @param {String/HTMLElement/Element} el The container element
16298  * @param {Object} config
16299  */
16300 Roo.tree.TreePanel = function(el, config){
16301     var root = false;
16302     var loader = false;
16303     if (config.root) {
16304         root = config.root;
16305         delete config.root;
16306     }
16307     if (config.loader) {
16308         loader = config.loader;
16309         delete config.loader;
16310     }
16311     
16312     Roo.apply(this, config);
16313     Roo.tree.TreePanel.superclass.constructor.call(this);
16314     this.el = Roo.get(el);
16315     this.el.addClass('x-tree');
16316     //console.log(root);
16317     if (root) {
16318         this.setRootNode( Roo.factory(root, Roo.tree));
16319     }
16320     if (loader) {
16321         this.loader = Roo.factory(loader, Roo.tree);
16322     }
16323    /**
16324     * Read-only. The id of the container element becomes this TreePanel's id.
16325     */
16326    this.id = this.el.id;
16327    this.addEvents({
16328         /**
16329         * @event beforeload
16330         * Fires before a node is loaded, return false to cancel
16331         * @param {Node} node The node being loaded
16332         */
16333         "beforeload" : true,
16334         /**
16335         * @event load
16336         * Fires when a node is loaded
16337         * @param {Node} node The node that was loaded
16338         */
16339         "load" : true,
16340         /**
16341         * @event textchange
16342         * Fires when the text for a node is changed
16343         * @param {Node} node The node
16344         * @param {String} text The new text
16345         * @param {String} oldText The old text
16346         */
16347         "textchange" : true,
16348         /**
16349         * @event beforeexpand
16350         * Fires before a node is expanded, return false to cancel.
16351         * @param {Node} node The node
16352         * @param {Boolean} deep
16353         * @param {Boolean} anim
16354         */
16355         "beforeexpand" : true,
16356         /**
16357         * @event beforecollapse
16358         * Fires before a node is collapsed, return false to cancel.
16359         * @param {Node} node The node
16360         * @param {Boolean} deep
16361         * @param {Boolean} anim
16362         */
16363         "beforecollapse" : true,
16364         /**
16365         * @event expand
16366         * Fires when a node is expanded
16367         * @param {Node} node The node
16368         */
16369         "expand" : true,
16370         /**
16371         * @event disabledchange
16372         * Fires when the disabled status of a node changes
16373         * @param {Node} node The node
16374         * @param {Boolean} disabled
16375         */
16376         "disabledchange" : true,
16377         /**
16378         * @event collapse
16379         * Fires when a node is collapsed
16380         * @param {Node} node The node
16381         */
16382         "collapse" : true,
16383         /**
16384         * @event beforeclick
16385         * Fires before click processing on a node. Return false to cancel the default action.
16386         * @param {Node} node The node
16387         * @param {Roo.EventObject} e The event object
16388         */
16389         "beforeclick":true,
16390         /**
16391         * @event checkchange
16392         * Fires when a node with a checkbox's checked property changes
16393         * @param {Node} this This node
16394         * @param {Boolean} checked
16395         */
16396         "checkchange":true,
16397         /**
16398         * @event click
16399         * Fires when a node is clicked
16400         * @param {Node} node The node
16401         * @param {Roo.EventObject} e The event object
16402         */
16403         "click":true,
16404         /**
16405         * @event dblclick
16406         * Fires when a node is double clicked
16407         * @param {Node} node The node
16408         * @param {Roo.EventObject} e The event object
16409         */
16410         "dblclick":true,
16411         /**
16412         * @event contextmenu
16413         * Fires when a node is right clicked
16414         * @param {Node} node The node
16415         * @param {Roo.EventObject} e The event object
16416         */
16417         "contextmenu":true,
16418         /**
16419         * @event beforechildrenrendered
16420         * Fires right before the child nodes for a node are rendered
16421         * @param {Node} node The node
16422         */
16423         "beforechildrenrendered":true,
16424        /**
16425              * @event startdrag
16426              * Fires when a node starts being dragged
16427              * @param {Roo.tree.TreePanel} this
16428              * @param {Roo.tree.TreeNode} node
16429              * @param {event} e The raw browser event
16430              */ 
16431             "startdrag" : true,
16432             /**
16433              * @event enddrag
16434              * Fires when a drag operation is complete
16435              * @param {Roo.tree.TreePanel} this
16436              * @param {Roo.tree.TreeNode} node
16437              * @param {event} e The raw browser event
16438              */
16439             "enddrag" : true,
16440             /**
16441              * @event dragdrop
16442              * Fires when a dragged node is dropped on a valid DD target
16443              * @param {Roo.tree.TreePanel} this
16444              * @param {Roo.tree.TreeNode} node
16445              * @param {DD} dd The dd it was dropped on
16446              * @param {event} e The raw browser event
16447              */
16448             "dragdrop" : true,
16449             /**
16450              * @event beforenodedrop
16451              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16452              * passed to handlers has the following properties:<br />
16453              * <ul style="padding:5px;padding-left:16px;">
16454              * <li>tree - The TreePanel</li>
16455              * <li>target - The node being targeted for the drop</li>
16456              * <li>data - The drag data from the drag source</li>
16457              * <li>point - The point of the drop - append, above or below</li>
16458              * <li>source - The drag source</li>
16459              * <li>rawEvent - Raw mouse event</li>
16460              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16461              * to be inserted by setting them on this object.</li>
16462              * <li>cancel - Set this to true to cancel the drop.</li>
16463              * </ul>
16464              * @param {Object} dropEvent
16465              */
16466             "beforenodedrop" : true,
16467             /**
16468              * @event nodedrop
16469              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16470              * passed to handlers has the following properties:<br />
16471              * <ul style="padding:5px;padding-left:16px;">
16472              * <li>tree - The TreePanel</li>
16473              * <li>target - The node being targeted for the drop</li>
16474              * <li>data - The drag data from the drag source</li>
16475              * <li>point - The point of the drop - append, above or below</li>
16476              * <li>source - The drag source</li>
16477              * <li>rawEvent - Raw mouse event</li>
16478              * <li>dropNode - Dropped node(s).</li>
16479              * </ul>
16480              * @param {Object} dropEvent
16481              */
16482             "nodedrop" : true,
16483              /**
16484              * @event nodedragover
16485              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16486              * passed to handlers has the following properties:<br />
16487              * <ul style="padding:5px;padding-left:16px;">
16488              * <li>tree - The TreePanel</li>
16489              * <li>target - The node being targeted for the drop</li>
16490              * <li>data - The drag data from the drag source</li>
16491              * <li>point - The point of the drop - append, above or below</li>
16492              * <li>source - The drag source</li>
16493              * <li>rawEvent - Raw mouse event</li>
16494              * <li>dropNode - Drop node(s) provided by the source.</li>
16495              * <li>cancel - Set this to true to signal drop not allowed.</li>
16496              * </ul>
16497              * @param {Object} dragOverEvent
16498              */
16499             "nodedragover" : true
16500         
16501    });
16502    if(this.singleExpand){
16503        this.on("beforeexpand", this.restrictExpand, this);
16504    }
16505 };
16506 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16507     rootVisible : true,
16508     animate: Roo.enableFx,
16509     lines : true,
16510     enableDD : false,
16511     hlDrop : Roo.enableFx,
16512   
16513     renderer: false,
16514     
16515     rendererTip: false,
16516     // private
16517     restrictExpand : function(node){
16518         var p = node.parentNode;
16519         if(p){
16520             if(p.expandedChild && p.expandedChild.parentNode == p){
16521                 p.expandedChild.collapse();
16522             }
16523             p.expandedChild = node;
16524         }
16525     },
16526
16527     // private override
16528     setRootNode : function(node){
16529         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16530         if(!this.rootVisible){
16531             node.ui = new Roo.tree.RootTreeNodeUI(node);
16532         }
16533         return node;
16534     },
16535
16536     /**
16537      * Returns the container element for this TreePanel
16538      */
16539     getEl : function(){
16540         return this.el;
16541     },
16542
16543     /**
16544      * Returns the default TreeLoader for this TreePanel
16545      */
16546     getLoader : function(){
16547         return this.loader;
16548     },
16549
16550     /**
16551      * Expand all nodes
16552      */
16553     expandAll : function(){
16554         this.root.expand(true);
16555     },
16556
16557     /**
16558      * Collapse all nodes
16559      */
16560     collapseAll : function(){
16561         this.root.collapse(true);
16562     },
16563
16564     /**
16565      * Returns the selection model used by this TreePanel
16566      */
16567     getSelectionModel : function(){
16568         if(!this.selModel){
16569             this.selModel = new Roo.tree.DefaultSelectionModel();
16570         }
16571         return this.selModel;
16572     },
16573
16574     /**
16575      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16576      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16577      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16578      * @return {Array}
16579      */
16580     getChecked : function(a, startNode){
16581         startNode = startNode || this.root;
16582         var r = [];
16583         var f = function(){
16584             if(this.attributes.checked){
16585                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16586             }
16587         }
16588         startNode.cascade(f);
16589         return r;
16590     },
16591
16592     /**
16593      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16594      * @param {String} path
16595      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16596      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16597      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16598      */
16599     expandPath : function(path, attr, callback){
16600         attr = attr || "id";
16601         var keys = path.split(this.pathSeparator);
16602         var curNode = this.root;
16603         if(curNode.attributes[attr] != keys[1]){ // invalid root
16604             if(callback){
16605                 callback(false, null);
16606             }
16607             return;
16608         }
16609         var index = 1;
16610         var f = function(){
16611             if(++index == keys.length){
16612                 if(callback){
16613                     callback(true, curNode);
16614                 }
16615                 return;
16616             }
16617             var c = curNode.findChild(attr, keys[index]);
16618             if(!c){
16619                 if(callback){
16620                     callback(false, curNode);
16621                 }
16622                 return;
16623             }
16624             curNode = c;
16625             c.expand(false, false, f);
16626         };
16627         curNode.expand(false, false, f);
16628     },
16629
16630     /**
16631      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16632      * @param {String} path
16633      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16634      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16635      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16636      */
16637     selectPath : function(path, attr, callback){
16638         attr = attr || "id";
16639         var keys = path.split(this.pathSeparator);
16640         var v = keys.pop();
16641         if(keys.length > 0){
16642             var f = function(success, node){
16643                 if(success && node){
16644                     var n = node.findChild(attr, v);
16645                     if(n){
16646                         n.select();
16647                         if(callback){
16648                             callback(true, n);
16649                         }
16650                     }else if(callback){
16651                         callback(false, n);
16652                     }
16653                 }else{
16654                     if(callback){
16655                         callback(false, n);
16656                     }
16657                 }
16658             };
16659             this.expandPath(keys.join(this.pathSeparator), attr, f);
16660         }else{
16661             this.root.select();
16662             if(callback){
16663                 callback(true, this.root);
16664             }
16665         }
16666     },
16667
16668     getTreeEl : function(){
16669         return this.el;
16670     },
16671
16672     /**
16673      * Trigger rendering of this TreePanel
16674      */
16675     render : function(){
16676         if (this.innerCt) {
16677             return this; // stop it rendering more than once!!
16678         }
16679         
16680         this.innerCt = this.el.createChild({tag:"ul",
16681                cls:"x-tree-root-ct " +
16682                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16683
16684         if(this.containerScroll){
16685             Roo.dd.ScrollManager.register(this.el);
16686         }
16687         if((this.enableDD || this.enableDrop) && !this.dropZone){
16688            /**
16689             * The dropZone used by this tree if drop is enabled
16690             * @type Roo.tree.TreeDropZone
16691             */
16692              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16693                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16694            });
16695         }
16696         if((this.enableDD || this.enableDrag) && !this.dragZone){
16697            /**
16698             * The dragZone used by this tree if drag is enabled
16699             * @type Roo.tree.TreeDragZone
16700             */
16701             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16702                ddGroup: this.ddGroup || "TreeDD",
16703                scroll: this.ddScroll
16704            });
16705         }
16706         this.getSelectionModel().init(this);
16707         if (!this.root) {
16708             console.log("ROOT not set in tree");
16709             return;
16710         }
16711         this.root.render();
16712         if(!this.rootVisible){
16713             this.root.renderChildren();
16714         }
16715         return this;
16716     }
16717 });/*
16718  * Based on:
16719  * Ext JS Library 1.1.1
16720  * Copyright(c) 2006-2007, Ext JS, LLC.
16721  *
16722  * Originally Released Under LGPL - original licence link has changed is not relivant.
16723  *
16724  * Fork - LGPL
16725  * <script type="text/javascript">
16726  */
16727  
16728
16729 /**
16730  * @class Roo.tree.DefaultSelectionModel
16731  * @extends Roo.util.Observable
16732  * The default single selection for a TreePanel.
16733  */
16734 Roo.tree.DefaultSelectionModel = function(){
16735    this.selNode = null;
16736    
16737    this.addEvents({
16738        /**
16739         * @event selectionchange
16740         * Fires when the selected node changes
16741         * @param {DefaultSelectionModel} this
16742         * @param {TreeNode} node the new selection
16743         */
16744        "selectionchange" : true,
16745
16746        /**
16747         * @event beforeselect
16748         * Fires before the selected node changes, return false to cancel the change
16749         * @param {DefaultSelectionModel} this
16750         * @param {TreeNode} node the new selection
16751         * @param {TreeNode} node the old selection
16752         */
16753        "beforeselect" : true
16754    });
16755 };
16756
16757 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16758     init : function(tree){
16759         this.tree = tree;
16760         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16761         tree.on("click", this.onNodeClick, this);
16762     },
16763     
16764     onNodeClick : function(node, e){
16765         if (e.ctrlKey && this.selNode == node)  {
16766             this.unselect(node);
16767             return;
16768         }
16769         this.select(node);
16770     },
16771     
16772     /**
16773      * Select a node.
16774      * @param {TreeNode} node The node to select
16775      * @return {TreeNode} The selected node
16776      */
16777     select : function(node){
16778         var last = this.selNode;
16779         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16780             if(last){
16781                 last.ui.onSelectedChange(false);
16782             }
16783             this.selNode = node;
16784             node.ui.onSelectedChange(true);
16785             this.fireEvent("selectionchange", this, node, last);
16786         }
16787         return node;
16788     },
16789     
16790     /**
16791      * Deselect a node.
16792      * @param {TreeNode} node The node to unselect
16793      */
16794     unselect : function(node){
16795         if(this.selNode == node){
16796             this.clearSelections();
16797         }    
16798     },
16799     
16800     /**
16801      * Clear all selections
16802      */
16803     clearSelections : function(){
16804         var n = this.selNode;
16805         if(n){
16806             n.ui.onSelectedChange(false);
16807             this.selNode = null;
16808             this.fireEvent("selectionchange", this, null);
16809         }
16810         return n;
16811     },
16812     
16813     /**
16814      * Get the selected node
16815      * @return {TreeNode} The selected node
16816      */
16817     getSelectedNode : function(){
16818         return this.selNode;    
16819     },
16820     
16821     /**
16822      * Returns true if the node is selected
16823      * @param {TreeNode} node The node to check
16824      * @return {Boolean}
16825      */
16826     isSelected : function(node){
16827         return this.selNode == node;  
16828     },
16829
16830     /**
16831      * Selects the node above the selected node in the tree, intelligently walking the nodes
16832      * @return TreeNode The new selection
16833      */
16834     selectPrevious : function(){
16835         var s = this.selNode || this.lastSelNode;
16836         if(!s){
16837             return null;
16838         }
16839         var ps = s.previousSibling;
16840         if(ps){
16841             if(!ps.isExpanded() || ps.childNodes.length < 1){
16842                 return this.select(ps);
16843             } else{
16844                 var lc = ps.lastChild;
16845                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16846                     lc = lc.lastChild;
16847                 }
16848                 return this.select(lc);
16849             }
16850         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16851             return this.select(s.parentNode);
16852         }
16853         return null;
16854     },
16855
16856     /**
16857      * Selects the node above the selected node in the tree, intelligently walking the nodes
16858      * @return TreeNode The new selection
16859      */
16860     selectNext : function(){
16861         var s = this.selNode || this.lastSelNode;
16862         if(!s){
16863             return null;
16864         }
16865         if(s.firstChild && s.isExpanded()){
16866              return this.select(s.firstChild);
16867          }else if(s.nextSibling){
16868              return this.select(s.nextSibling);
16869          }else if(s.parentNode){
16870             var newS = null;
16871             s.parentNode.bubble(function(){
16872                 if(this.nextSibling){
16873                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16874                     return false;
16875                 }
16876             });
16877             return newS;
16878          }
16879         return null;
16880     },
16881
16882     onKeyDown : function(e){
16883         var s = this.selNode || this.lastSelNode;
16884         // undesirable, but required
16885         var sm = this;
16886         if(!s){
16887             return;
16888         }
16889         var k = e.getKey();
16890         switch(k){
16891              case e.DOWN:
16892                  e.stopEvent();
16893                  this.selectNext();
16894              break;
16895              case e.UP:
16896                  e.stopEvent();
16897                  this.selectPrevious();
16898              break;
16899              case e.RIGHT:
16900                  e.preventDefault();
16901                  if(s.hasChildNodes()){
16902                      if(!s.isExpanded()){
16903                          s.expand();
16904                      }else if(s.firstChild){
16905                          this.select(s.firstChild, e);
16906                      }
16907                  }
16908              break;
16909              case e.LEFT:
16910                  e.preventDefault();
16911                  if(s.hasChildNodes() && s.isExpanded()){
16912                      s.collapse();
16913                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16914                      this.select(s.parentNode, e);
16915                  }
16916              break;
16917         };
16918     }
16919 });
16920
16921 /**
16922  * @class Roo.tree.MultiSelectionModel
16923  * @extends Roo.util.Observable
16924  * Multi selection for a TreePanel.
16925  */
16926 Roo.tree.MultiSelectionModel = function(){
16927    this.selNodes = [];
16928    this.selMap = {};
16929    this.addEvents({
16930        /**
16931         * @event selectionchange
16932         * Fires when the selected nodes change
16933         * @param {MultiSelectionModel} this
16934         * @param {Array} nodes Array of the selected nodes
16935         */
16936        "selectionchange" : true
16937    });
16938 };
16939
16940 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16941     init : function(tree){
16942         this.tree = tree;
16943         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16944         tree.on("click", this.onNodeClick, this);
16945     },
16946     
16947     onNodeClick : function(node, e){
16948         this.select(node, e, e.ctrlKey);
16949     },
16950     
16951     /**
16952      * Select a node.
16953      * @param {TreeNode} node The node to select
16954      * @param {EventObject} e (optional) An event associated with the selection
16955      * @param {Boolean} keepExisting True to retain existing selections
16956      * @return {TreeNode} The selected node
16957      */
16958     select : function(node, e, keepExisting){
16959         if(keepExisting !== true){
16960             this.clearSelections(true);
16961         }
16962         if(this.isSelected(node)){
16963             this.lastSelNode = node;
16964             return node;
16965         }
16966         this.selNodes.push(node);
16967         this.selMap[node.id] = node;
16968         this.lastSelNode = node;
16969         node.ui.onSelectedChange(true);
16970         this.fireEvent("selectionchange", this, this.selNodes);
16971         return node;
16972     },
16973     
16974     /**
16975      * Deselect a node.
16976      * @param {TreeNode} node The node to unselect
16977      */
16978     unselect : function(node){
16979         if(this.selMap[node.id]){
16980             node.ui.onSelectedChange(false);
16981             var sn = this.selNodes;
16982             var index = -1;
16983             if(sn.indexOf){
16984                 index = sn.indexOf(node);
16985             }else{
16986                 for(var i = 0, len = sn.length; i < len; i++){
16987                     if(sn[i] == node){
16988                         index = i;
16989                         break;
16990                     }
16991                 }
16992             }
16993             if(index != -1){
16994                 this.selNodes.splice(index, 1);
16995             }
16996             delete this.selMap[node.id];
16997             this.fireEvent("selectionchange", this, this.selNodes);
16998         }
16999     },
17000     
17001     /**
17002      * Clear all selections
17003      */
17004     clearSelections : function(suppressEvent){
17005         var sn = this.selNodes;
17006         if(sn.length > 0){
17007             for(var i = 0, len = sn.length; i < len; i++){
17008                 sn[i].ui.onSelectedChange(false);
17009             }
17010             this.selNodes = [];
17011             this.selMap = {};
17012             if(suppressEvent !== true){
17013                 this.fireEvent("selectionchange", this, this.selNodes);
17014             }
17015         }
17016     },
17017     
17018     /**
17019      * Returns true if the node is selected
17020      * @param {TreeNode} node The node to check
17021      * @return {Boolean}
17022      */
17023     isSelected : function(node){
17024         return this.selMap[node.id] ? true : false;  
17025     },
17026     
17027     /**
17028      * Returns an array of the selected nodes
17029      * @return {Array}
17030      */
17031     getSelectedNodes : function(){
17032         return this.selNodes;    
17033     },
17034
17035     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17036
17037     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17038
17039     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17040 });/*
17041  * Based on:
17042  * Ext JS Library 1.1.1
17043  * Copyright(c) 2006-2007, Ext JS, LLC.
17044  *
17045  * Originally Released Under LGPL - original licence link has changed is not relivant.
17046  *
17047  * Fork - LGPL
17048  * <script type="text/javascript">
17049  */
17050  
17051 /**
17052  * @class Roo.tree.TreeNode
17053  * @extends Roo.data.Node
17054  * @cfg {String} text The text for this node
17055  * @cfg {Boolean} expanded true to start the node expanded
17056  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17057  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17058  * @cfg {Boolean} disabled true to start the node disabled
17059  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17060  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17061  * @cfg {String} cls A css class to be added to the node
17062  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17063  * @cfg {String} href URL of the link used for the node (defaults to #)
17064  * @cfg {String} hrefTarget target frame for the link
17065  * @cfg {String} qtip An Ext QuickTip for the node
17066  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17067  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17068  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17069  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17070  * (defaults to undefined with no checkbox rendered)
17071  * @constructor
17072  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17073  */
17074 Roo.tree.TreeNode = function(attributes){
17075     attributes = attributes || {};
17076     if(typeof attributes == "string"){
17077         attributes = {text: attributes};
17078     }
17079     this.childrenRendered = false;
17080     this.rendered = false;
17081     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17082     this.expanded = attributes.expanded === true;
17083     this.isTarget = attributes.isTarget !== false;
17084     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17085     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17086
17087     /**
17088      * Read-only. The text for this node. To change it use setText().
17089      * @type String
17090      */
17091     this.text = attributes.text;
17092     /**
17093      * True if this node is disabled.
17094      * @type Boolean
17095      */
17096     this.disabled = attributes.disabled === true;
17097
17098     this.addEvents({
17099         /**
17100         * @event textchange
17101         * Fires when the text for this node is changed
17102         * @param {Node} this This node
17103         * @param {String} text The new text
17104         * @param {String} oldText The old text
17105         */
17106         "textchange" : true,
17107         /**
17108         * @event beforeexpand
17109         * Fires before this node is expanded, return false to cancel.
17110         * @param {Node} this This node
17111         * @param {Boolean} deep
17112         * @param {Boolean} anim
17113         */
17114         "beforeexpand" : true,
17115         /**
17116         * @event beforecollapse
17117         * Fires before this node is collapsed, return false to cancel.
17118         * @param {Node} this This node
17119         * @param {Boolean} deep
17120         * @param {Boolean} anim
17121         */
17122         "beforecollapse" : true,
17123         /**
17124         * @event expand
17125         * Fires when this node is expanded
17126         * @param {Node} this This node
17127         */
17128         "expand" : true,
17129         /**
17130         * @event disabledchange
17131         * Fires when the disabled status of this node changes
17132         * @param {Node} this This node
17133         * @param {Boolean} disabled
17134         */
17135         "disabledchange" : true,
17136         /**
17137         * @event collapse
17138         * Fires when this node is collapsed
17139         * @param {Node} this This node
17140         */
17141         "collapse" : true,
17142         /**
17143         * @event beforeclick
17144         * Fires before click processing. Return false to cancel the default action.
17145         * @param {Node} this This node
17146         * @param {Roo.EventObject} e The event object
17147         */
17148         "beforeclick":true,
17149         /**
17150         * @event checkchange
17151         * Fires when a node with a checkbox's checked property changes
17152         * @param {Node} this This node
17153         * @param {Boolean} checked
17154         */
17155         "checkchange":true,
17156         /**
17157         * @event click
17158         * Fires when this node is clicked
17159         * @param {Node} this This node
17160         * @param {Roo.EventObject} e The event object
17161         */
17162         "click":true,
17163         /**
17164         * @event dblclick
17165         * Fires when this node is double clicked
17166         * @param {Node} this This node
17167         * @param {Roo.EventObject} e The event object
17168         */
17169         "dblclick":true,
17170         /**
17171         * @event contextmenu
17172         * Fires when this node is right clicked
17173         * @param {Node} this This node
17174         * @param {Roo.EventObject} e The event object
17175         */
17176         "contextmenu":true,
17177         /**
17178         * @event beforechildrenrendered
17179         * Fires right before the child nodes for this node are rendered
17180         * @param {Node} this This node
17181         */
17182         "beforechildrenrendered":true
17183     });
17184
17185     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17186
17187     /**
17188      * Read-only. The UI for this node
17189      * @type TreeNodeUI
17190      */
17191     this.ui = new uiClass(this);
17192 };
17193 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17194     preventHScroll: true,
17195     /**
17196      * Returns true if this node is expanded
17197      * @return {Boolean}
17198      */
17199     isExpanded : function(){
17200         return this.expanded;
17201     },
17202
17203     /**
17204      * Returns the UI object for this node
17205      * @return {TreeNodeUI}
17206      */
17207     getUI : function(){
17208         return this.ui;
17209     },
17210
17211     // private override
17212     setFirstChild : function(node){
17213         var of = this.firstChild;
17214         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17215         if(this.childrenRendered && of && node != of){
17216             of.renderIndent(true, true);
17217         }
17218         if(this.rendered){
17219             this.renderIndent(true, true);
17220         }
17221     },
17222
17223     // private override
17224     setLastChild : function(node){
17225         var ol = this.lastChild;
17226         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17227         if(this.childrenRendered && ol && node != ol){
17228             ol.renderIndent(true, true);
17229         }
17230         if(this.rendered){
17231             this.renderIndent(true, true);
17232         }
17233     },
17234
17235     // these methods are overridden to provide lazy rendering support
17236     // private override
17237     appendChild : function(){
17238         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17239         if(node && this.childrenRendered){
17240             node.render();
17241         }
17242         this.ui.updateExpandIcon();
17243         return node;
17244     },
17245
17246     // private override
17247     removeChild : function(node){
17248         this.ownerTree.getSelectionModel().unselect(node);
17249         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17250         // if it's been rendered remove dom node
17251         if(this.childrenRendered){
17252             node.ui.remove();
17253         }
17254         if(this.childNodes.length < 1){
17255             this.collapse(false, false);
17256         }else{
17257             this.ui.updateExpandIcon();
17258         }
17259         if(!this.firstChild) {
17260             this.childrenRendered = false;
17261         }
17262         return node;
17263     },
17264
17265     // private override
17266     insertBefore : function(node, refNode){
17267         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17268         if(newNode && refNode && this.childrenRendered){
17269             node.render();
17270         }
17271         this.ui.updateExpandIcon();
17272         return newNode;
17273     },
17274
17275     /**
17276      * Sets the text for this node
17277      * @param {String} text
17278      */
17279     setText : function(text){
17280         var oldText = this.text;
17281         this.text = text;
17282         this.attributes.text = text;
17283         if(this.rendered){ // event without subscribing
17284             this.ui.onTextChange(this, text, oldText);
17285         }
17286         this.fireEvent("textchange", this, text, oldText);
17287     },
17288
17289     /**
17290      * Triggers selection of this node
17291      */
17292     select : function(){
17293         this.getOwnerTree().getSelectionModel().select(this);
17294     },
17295
17296     /**
17297      * Triggers deselection of this node
17298      */
17299     unselect : function(){
17300         this.getOwnerTree().getSelectionModel().unselect(this);
17301     },
17302
17303     /**
17304      * Returns true if this node is selected
17305      * @return {Boolean}
17306      */
17307     isSelected : function(){
17308         return this.getOwnerTree().getSelectionModel().isSelected(this);
17309     },
17310
17311     /**
17312      * Expand this node.
17313      * @param {Boolean} deep (optional) True to expand all children as well
17314      * @param {Boolean} anim (optional) false to cancel the default animation
17315      * @param {Function} callback (optional) A callback to be called when
17316      * expanding this node completes (does not wait for deep expand to complete).
17317      * Called with 1 parameter, this node.
17318      */
17319     expand : function(deep, anim, callback){
17320         if(!this.expanded){
17321             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17322                 return;
17323             }
17324             if(!this.childrenRendered){
17325                 this.renderChildren();
17326             }
17327             this.expanded = true;
17328             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17329                 this.ui.animExpand(function(){
17330                     this.fireEvent("expand", this);
17331                     if(typeof callback == "function"){
17332                         callback(this);
17333                     }
17334                     if(deep === true){
17335                         this.expandChildNodes(true);
17336                     }
17337                 }.createDelegate(this));
17338                 return;
17339             }else{
17340                 this.ui.expand();
17341                 this.fireEvent("expand", this);
17342                 if(typeof callback == "function"){
17343                     callback(this);
17344                 }
17345             }
17346         }else{
17347            if(typeof callback == "function"){
17348                callback(this);
17349            }
17350         }
17351         if(deep === true){
17352             this.expandChildNodes(true);
17353         }
17354     },
17355
17356     isHiddenRoot : function(){
17357         return this.isRoot && !this.getOwnerTree().rootVisible;
17358     },
17359
17360     /**
17361      * Collapse this node.
17362      * @param {Boolean} deep (optional) True to collapse all children as well
17363      * @param {Boolean} anim (optional) false to cancel the default animation
17364      */
17365     collapse : function(deep, anim){
17366         if(this.expanded && !this.isHiddenRoot()){
17367             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17368                 return;
17369             }
17370             this.expanded = false;
17371             if((this.getOwnerTree().animate && anim !== false) || anim){
17372                 this.ui.animCollapse(function(){
17373                     this.fireEvent("collapse", this);
17374                     if(deep === true){
17375                         this.collapseChildNodes(true);
17376                     }
17377                 }.createDelegate(this));
17378                 return;
17379             }else{
17380                 this.ui.collapse();
17381                 this.fireEvent("collapse", this);
17382             }
17383         }
17384         if(deep === true){
17385             var cs = this.childNodes;
17386             for(var i = 0, len = cs.length; i < len; i++) {
17387                 cs[i].collapse(true, false);
17388             }
17389         }
17390     },
17391
17392     // private
17393     delayedExpand : function(delay){
17394         if(!this.expandProcId){
17395             this.expandProcId = this.expand.defer(delay, this);
17396         }
17397     },
17398
17399     // private
17400     cancelExpand : function(){
17401         if(this.expandProcId){
17402             clearTimeout(this.expandProcId);
17403         }
17404         this.expandProcId = false;
17405     },
17406
17407     /**
17408      * Toggles expanded/collapsed state of the node
17409      */
17410     toggle : function(){
17411         if(this.expanded){
17412             this.collapse();
17413         }else{
17414             this.expand();
17415         }
17416     },
17417
17418     /**
17419      * Ensures all parent nodes are expanded
17420      */
17421     ensureVisible : function(callback){
17422         var tree = this.getOwnerTree();
17423         tree.expandPath(this.parentNode.getPath(), false, function(){
17424             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17425             Roo.callback(callback);
17426         }.createDelegate(this));
17427     },
17428
17429     /**
17430      * Expand all child nodes
17431      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17432      */
17433     expandChildNodes : function(deep){
17434         var cs = this.childNodes;
17435         for(var i = 0, len = cs.length; i < len; i++) {
17436                 cs[i].expand(deep);
17437         }
17438     },
17439
17440     /**
17441      * Collapse all child nodes
17442      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17443      */
17444     collapseChildNodes : function(deep){
17445         var cs = this.childNodes;
17446         for(var i = 0, len = cs.length; i < len; i++) {
17447                 cs[i].collapse(deep);
17448         }
17449     },
17450
17451     /**
17452      * Disables this node
17453      */
17454     disable : function(){
17455         this.disabled = true;
17456         this.unselect();
17457         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17458             this.ui.onDisableChange(this, true);
17459         }
17460         this.fireEvent("disabledchange", this, true);
17461     },
17462
17463     /**
17464      * Enables this node
17465      */
17466     enable : function(){
17467         this.disabled = false;
17468         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17469             this.ui.onDisableChange(this, false);
17470         }
17471         this.fireEvent("disabledchange", this, false);
17472     },
17473
17474     // private
17475     renderChildren : function(suppressEvent){
17476         if(suppressEvent !== false){
17477             this.fireEvent("beforechildrenrendered", this);
17478         }
17479         var cs = this.childNodes;
17480         for(var i = 0, len = cs.length; i < len; i++){
17481             cs[i].render(true);
17482         }
17483         this.childrenRendered = true;
17484     },
17485
17486     // private
17487     sort : function(fn, scope){
17488         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17489         if(this.childrenRendered){
17490             var cs = this.childNodes;
17491             for(var i = 0, len = cs.length; i < len; i++){
17492                 cs[i].render(true);
17493             }
17494         }
17495     },
17496
17497     // private
17498     render : function(bulkRender){
17499         this.ui.render(bulkRender);
17500         if(!this.rendered){
17501             this.rendered = true;
17502             if(this.expanded){
17503                 this.expanded = false;
17504                 this.expand(false, false);
17505             }
17506         }
17507     },
17508
17509     // private
17510     renderIndent : function(deep, refresh){
17511         if(refresh){
17512             this.ui.childIndent = null;
17513         }
17514         this.ui.renderIndent();
17515         if(deep === true && this.childrenRendered){
17516             var cs = this.childNodes;
17517             for(var i = 0, len = cs.length; i < len; i++){
17518                 cs[i].renderIndent(true, refresh);
17519             }
17520         }
17521     }
17522 });/*
17523  * Based on:
17524  * Ext JS Library 1.1.1
17525  * Copyright(c) 2006-2007, Ext JS, LLC.
17526  *
17527  * Originally Released Under LGPL - original licence link has changed is not relivant.
17528  *
17529  * Fork - LGPL
17530  * <script type="text/javascript">
17531  */
17532  
17533 /**
17534  * @class Roo.tree.AsyncTreeNode
17535  * @extends Roo.tree.TreeNode
17536  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17537  * @constructor
17538  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17539  */
17540  Roo.tree.AsyncTreeNode = function(config){
17541     this.loaded = false;
17542     this.loading = false;
17543     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17544     /**
17545     * @event beforeload
17546     * Fires before this node is loaded, return false to cancel
17547     * @param {Node} this This node
17548     */
17549     this.addEvents({'beforeload':true, 'load': true});
17550     /**
17551     * @event load
17552     * Fires when this node is loaded
17553     * @param {Node} this This node
17554     */
17555     /**
17556      * The loader used by this node (defaults to using the tree's defined loader)
17557      * @type TreeLoader
17558      * @property loader
17559      */
17560 };
17561 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17562     expand : function(deep, anim, callback){
17563         if(this.loading){ // if an async load is already running, waiting til it's done
17564             var timer;
17565             var f = function(){
17566                 if(!this.loading){ // done loading
17567                     clearInterval(timer);
17568                     this.expand(deep, anim, callback);
17569                 }
17570             }.createDelegate(this);
17571             timer = setInterval(f, 200);
17572             return;
17573         }
17574         if(!this.loaded){
17575             if(this.fireEvent("beforeload", this) === false){
17576                 return;
17577             }
17578             this.loading = true;
17579             this.ui.beforeLoad(this);
17580             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17581             if(loader){
17582                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17583                 return;
17584             }
17585         }
17586         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17587     },
17588     
17589     /**
17590      * Returns true if this node is currently loading
17591      * @return {Boolean}
17592      */
17593     isLoading : function(){
17594         return this.loading;  
17595     },
17596     
17597     loadComplete : function(deep, anim, callback){
17598         this.loading = false;
17599         this.loaded = true;
17600         this.ui.afterLoad(this);
17601         this.fireEvent("load", this);
17602         this.expand(deep, anim, callback);
17603     },
17604     
17605     /**
17606      * Returns true if this node has been loaded
17607      * @return {Boolean}
17608      */
17609     isLoaded : function(){
17610         return this.loaded;
17611     },
17612     
17613     hasChildNodes : function(){
17614         if(!this.isLeaf() && !this.loaded){
17615             return true;
17616         }else{
17617             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17618         }
17619     },
17620
17621     /**
17622      * Trigger a reload for this node
17623      * @param {Function} callback
17624      */
17625     reload : function(callback){
17626         this.collapse(false, false);
17627         while(this.firstChild){
17628             this.removeChild(this.firstChild);
17629         }
17630         this.childrenRendered = false;
17631         this.loaded = false;
17632         if(this.isHiddenRoot()){
17633             this.expanded = false;
17634         }
17635         this.expand(false, false, callback);
17636     }
17637 });/*
17638  * Based on:
17639  * Ext JS Library 1.1.1
17640  * Copyright(c) 2006-2007, Ext JS, LLC.
17641  *
17642  * Originally Released Under LGPL - original licence link has changed is not relivant.
17643  *
17644  * Fork - LGPL
17645  * <script type="text/javascript">
17646  */
17647  
17648 /**
17649  * @class Roo.tree.TreeNodeUI
17650  * @constructor
17651  * @param {Object} node The node to render
17652  * The TreeNode UI implementation is separate from the
17653  * tree implementation. Unless you are customizing the tree UI,
17654  * you should never have to use this directly.
17655  */
17656 Roo.tree.TreeNodeUI = function(node){
17657     this.node = node;
17658     this.rendered = false;
17659     this.animating = false;
17660     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17661 };
17662
17663 Roo.tree.TreeNodeUI.prototype = {
17664     removeChild : function(node){
17665         if(this.rendered){
17666             this.ctNode.removeChild(node.ui.getEl());
17667         }
17668     },
17669
17670     beforeLoad : function(){
17671          this.addClass("x-tree-node-loading");
17672     },
17673
17674     afterLoad : function(){
17675          this.removeClass("x-tree-node-loading");
17676     },
17677
17678     onTextChange : function(node, text, oldText){
17679         if(this.rendered){
17680             this.textNode.innerHTML = text;
17681         }
17682     },
17683
17684     onDisableChange : function(node, state){
17685         this.disabled = state;
17686         if(state){
17687             this.addClass("x-tree-node-disabled");
17688         }else{
17689             this.removeClass("x-tree-node-disabled");
17690         }
17691     },
17692
17693     onSelectedChange : function(state){
17694         if(state){
17695             this.focus();
17696             this.addClass("x-tree-selected");
17697         }else{
17698             //this.blur();
17699             this.removeClass("x-tree-selected");
17700         }
17701     },
17702
17703     onMove : function(tree, node, oldParent, newParent, index, refNode){
17704         this.childIndent = null;
17705         if(this.rendered){
17706             var targetNode = newParent.ui.getContainer();
17707             if(!targetNode){//target not rendered
17708                 this.holder = document.createElement("div");
17709                 this.holder.appendChild(this.wrap);
17710                 return;
17711             }
17712             var insertBefore = refNode ? refNode.ui.getEl() : null;
17713             if(insertBefore){
17714                 targetNode.insertBefore(this.wrap, insertBefore);
17715             }else{
17716                 targetNode.appendChild(this.wrap);
17717             }
17718             this.node.renderIndent(true);
17719         }
17720     },
17721
17722     addClass : function(cls){
17723         if(this.elNode){
17724             Roo.fly(this.elNode).addClass(cls);
17725         }
17726     },
17727
17728     removeClass : function(cls){
17729         if(this.elNode){
17730             Roo.fly(this.elNode).removeClass(cls);
17731         }
17732     },
17733
17734     remove : function(){
17735         if(this.rendered){
17736             this.holder = document.createElement("div");
17737             this.holder.appendChild(this.wrap);
17738         }
17739     },
17740
17741     fireEvent : function(){
17742         return this.node.fireEvent.apply(this.node, arguments);
17743     },
17744
17745     initEvents : function(){
17746         this.node.on("move", this.onMove, this);
17747         var E = Roo.EventManager;
17748         var a = this.anchor;
17749
17750         var el = Roo.fly(a, '_treeui');
17751
17752         if(Roo.isOpera){ // opera render bug ignores the CSS
17753             el.setStyle("text-decoration", "none");
17754         }
17755
17756         el.on("click", this.onClick, this);
17757         el.on("dblclick", this.onDblClick, this);
17758
17759         if(this.checkbox){
17760             Roo.EventManager.on(this.checkbox,
17761                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17762         }
17763
17764         el.on("contextmenu", this.onContextMenu, this);
17765
17766         var icon = Roo.fly(this.iconNode);
17767         icon.on("click", this.onClick, this);
17768         icon.on("dblclick", this.onDblClick, this);
17769         icon.on("contextmenu", this.onContextMenu, this);
17770         E.on(this.ecNode, "click", this.ecClick, this, true);
17771
17772         if(this.node.disabled){
17773             this.addClass("x-tree-node-disabled");
17774         }
17775         if(this.node.hidden){
17776             this.addClass("x-tree-node-disabled");
17777         }
17778         var ot = this.node.getOwnerTree();
17779         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17780         if(dd && (!this.node.isRoot || ot.rootVisible)){
17781             Roo.dd.Registry.register(this.elNode, {
17782                 node: this.node,
17783                 handles: this.getDDHandles(),
17784                 isHandle: false
17785             });
17786         }
17787     },
17788
17789     getDDHandles : function(){
17790         return [this.iconNode, this.textNode];
17791     },
17792
17793     hide : function(){
17794         if(this.rendered){
17795             this.wrap.style.display = "none";
17796         }
17797     },
17798
17799     show : function(){
17800         if(this.rendered){
17801             this.wrap.style.display = "";
17802         }
17803     },
17804
17805     onContextMenu : function(e){
17806         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17807             e.preventDefault();
17808             this.focus();
17809             this.fireEvent("contextmenu", this.node, e);
17810         }
17811     },
17812
17813     onClick : function(e){
17814         if(this.dropping){
17815             e.stopEvent();
17816             return;
17817         }
17818         if(this.fireEvent("beforeclick", this.node, e) !== false){
17819             if(!this.disabled && this.node.attributes.href){
17820                 this.fireEvent("click", this.node, e);
17821                 return;
17822             }
17823             e.preventDefault();
17824             if(this.disabled){
17825                 return;
17826             }
17827
17828             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17829                 this.node.toggle();
17830             }
17831
17832             this.fireEvent("click", this.node, e);
17833         }else{
17834             e.stopEvent();
17835         }
17836     },
17837
17838     onDblClick : function(e){
17839         e.preventDefault();
17840         if(this.disabled){
17841             return;
17842         }
17843         if(this.checkbox){
17844             this.toggleCheck();
17845         }
17846         if(!this.animating && this.node.hasChildNodes()){
17847             this.node.toggle();
17848         }
17849         this.fireEvent("dblclick", this.node, e);
17850     },
17851
17852     onCheckChange : function(){
17853         var checked = this.checkbox.checked;
17854         this.node.attributes.checked = checked;
17855         this.fireEvent('checkchange', this.node, checked);
17856     },
17857
17858     ecClick : function(e){
17859         if(!this.animating && this.node.hasChildNodes()){
17860             this.node.toggle();
17861         }
17862     },
17863
17864     startDrop : function(){
17865         this.dropping = true;
17866     },
17867
17868     // delayed drop so the click event doesn't get fired on a drop
17869     endDrop : function(){
17870        setTimeout(function(){
17871            this.dropping = false;
17872        }.createDelegate(this), 50);
17873     },
17874
17875     expand : function(){
17876         this.updateExpandIcon();
17877         this.ctNode.style.display = "";
17878     },
17879
17880     focus : function(){
17881         if(!this.node.preventHScroll){
17882             try{this.anchor.focus();
17883             }catch(e){}
17884         }else if(!Roo.isIE){
17885             try{
17886                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17887                 var l = noscroll.scrollLeft;
17888                 this.anchor.focus();
17889                 noscroll.scrollLeft = l;
17890             }catch(e){}
17891         }
17892     },
17893
17894     toggleCheck : function(value){
17895         var cb = this.checkbox;
17896         if(cb){
17897             cb.checked = (value === undefined ? !cb.checked : value);
17898         }
17899     },
17900
17901     blur : function(){
17902         try{
17903             this.anchor.blur();
17904         }catch(e){}
17905     },
17906
17907     animExpand : function(callback){
17908         var ct = Roo.get(this.ctNode);
17909         ct.stopFx();
17910         if(!this.node.hasChildNodes()){
17911             this.updateExpandIcon();
17912             this.ctNode.style.display = "";
17913             Roo.callback(callback);
17914             return;
17915         }
17916         this.animating = true;
17917         this.updateExpandIcon();
17918
17919         ct.slideIn('t', {
17920            callback : function(){
17921                this.animating = false;
17922                Roo.callback(callback);
17923             },
17924             scope: this,
17925             duration: this.node.ownerTree.duration || .25
17926         });
17927     },
17928
17929     highlight : function(){
17930         var tree = this.node.getOwnerTree();
17931         Roo.fly(this.wrap).highlight(
17932             tree.hlColor || "C3DAF9",
17933             {endColor: tree.hlBaseColor}
17934         );
17935     },
17936
17937     collapse : function(){
17938         this.updateExpandIcon();
17939         this.ctNode.style.display = "none";
17940     },
17941
17942     animCollapse : function(callback){
17943         var ct = Roo.get(this.ctNode);
17944         ct.enableDisplayMode('block');
17945         ct.stopFx();
17946
17947         this.animating = true;
17948         this.updateExpandIcon();
17949
17950         ct.slideOut('t', {
17951             callback : function(){
17952                this.animating = false;
17953                Roo.callback(callback);
17954             },
17955             scope: this,
17956             duration: this.node.ownerTree.duration || .25
17957         });
17958     },
17959
17960     getContainer : function(){
17961         return this.ctNode;
17962     },
17963
17964     getEl : function(){
17965         return this.wrap;
17966     },
17967
17968     appendDDGhost : function(ghostNode){
17969         ghostNode.appendChild(this.elNode.cloneNode(true));
17970     },
17971
17972     getDDRepairXY : function(){
17973         return Roo.lib.Dom.getXY(this.iconNode);
17974     },
17975
17976     onRender : function(){
17977         this.render();
17978     },
17979
17980     render : function(bulkRender){
17981         var n = this.node, a = n.attributes;
17982         var targetNode = n.parentNode ?
17983               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17984
17985         if(!this.rendered){
17986             this.rendered = true;
17987
17988             this.renderElements(n, a, targetNode, bulkRender);
17989
17990             if(a.qtip){
17991                if(this.textNode.setAttributeNS){
17992                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17993                    if(a.qtipTitle){
17994                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17995                    }
17996                }else{
17997                    this.textNode.setAttribute("ext:qtip", a.qtip);
17998                    if(a.qtipTitle){
17999                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18000                    }
18001                }
18002             }else if(a.qtipCfg){
18003                 a.qtipCfg.target = Roo.id(this.textNode);
18004                 Roo.QuickTips.register(a.qtipCfg);
18005             }
18006             this.initEvents();
18007             if(!this.node.expanded){
18008                 this.updateExpandIcon();
18009             }
18010         }else{
18011             if(bulkRender === true) {
18012                 targetNode.appendChild(this.wrap);
18013             }
18014         }
18015     },
18016
18017     renderElements : function(n, a, targetNode, bulkRender){
18018         // add some indent caching, this helps performance when rendering a large tree
18019         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18020         var t = n.getOwnerTree();
18021         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18022         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18023         var cb = typeof a.checked == 'boolean';
18024         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18025         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18026             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18027             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18028             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18029             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18030             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18031              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18032                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18033             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18034             "</li>"];
18035
18036         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18037             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18038                                 n.nextSibling.ui.getEl(), buf.join(""));
18039         }else{
18040             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18041         }
18042
18043         this.elNode = this.wrap.childNodes[0];
18044         this.ctNode = this.wrap.childNodes[1];
18045         var cs = this.elNode.childNodes;
18046         this.indentNode = cs[0];
18047         this.ecNode = cs[1];
18048         this.iconNode = cs[2];
18049         var index = 3;
18050         if(cb){
18051             this.checkbox = cs[3];
18052             index++;
18053         }
18054         this.anchor = cs[index];
18055         this.textNode = cs[index].firstChild;
18056     },
18057
18058     getAnchor : function(){
18059         return this.anchor;
18060     },
18061
18062     getTextEl : function(){
18063         return this.textNode;
18064     },
18065
18066     getIconEl : function(){
18067         return this.iconNode;
18068     },
18069
18070     isChecked : function(){
18071         return this.checkbox ? this.checkbox.checked : false;
18072     },
18073
18074     updateExpandIcon : function(){
18075         if(this.rendered){
18076             var n = this.node, c1, c2;
18077             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18078             var hasChild = n.hasChildNodes();
18079             if(hasChild){
18080                 if(n.expanded){
18081                     cls += "-minus";
18082                     c1 = "x-tree-node-collapsed";
18083                     c2 = "x-tree-node-expanded";
18084                 }else{
18085                     cls += "-plus";
18086                     c1 = "x-tree-node-expanded";
18087                     c2 = "x-tree-node-collapsed";
18088                 }
18089                 if(this.wasLeaf){
18090                     this.removeClass("x-tree-node-leaf");
18091                     this.wasLeaf = false;
18092                 }
18093                 if(this.c1 != c1 || this.c2 != c2){
18094                     Roo.fly(this.elNode).replaceClass(c1, c2);
18095                     this.c1 = c1; this.c2 = c2;
18096                 }
18097             }else{
18098                 if(!this.wasLeaf){
18099                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18100                     delete this.c1;
18101                     delete this.c2;
18102                     this.wasLeaf = true;
18103                 }
18104             }
18105             var ecc = "x-tree-ec-icon "+cls;
18106             if(this.ecc != ecc){
18107                 this.ecNode.className = ecc;
18108                 this.ecc = ecc;
18109             }
18110         }
18111     },
18112
18113     getChildIndent : function(){
18114         if(!this.childIndent){
18115             var buf = [];
18116             var p = this.node;
18117             while(p){
18118                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18119                     if(!p.isLast()) {
18120                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18121                     } else {
18122                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18123                     }
18124                 }
18125                 p = p.parentNode;
18126             }
18127             this.childIndent = buf.join("");
18128         }
18129         return this.childIndent;
18130     },
18131
18132     renderIndent : function(){
18133         if(this.rendered){
18134             var indent = "";
18135             var p = this.node.parentNode;
18136             if(p){
18137                 indent = p.ui.getChildIndent();
18138             }
18139             if(this.indentMarkup != indent){ // don't rerender if not required
18140                 this.indentNode.innerHTML = indent;
18141                 this.indentMarkup = indent;
18142             }
18143             this.updateExpandIcon();
18144         }
18145     }
18146 };
18147
18148 Roo.tree.RootTreeNodeUI = function(){
18149     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18150 };
18151 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18152     render : function(){
18153         if(!this.rendered){
18154             var targetNode = this.node.ownerTree.innerCt.dom;
18155             this.node.expanded = true;
18156             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18157             this.wrap = this.ctNode = targetNode.firstChild;
18158         }
18159     },
18160     collapse : function(){
18161     },
18162     expand : function(){
18163     }
18164 });/*
18165  * Based on:
18166  * Ext JS Library 1.1.1
18167  * Copyright(c) 2006-2007, Ext JS, LLC.
18168  *
18169  * Originally Released Under LGPL - original licence link has changed is not relivant.
18170  *
18171  * Fork - LGPL
18172  * <script type="text/javascript">
18173  */
18174 /**
18175  * @class Roo.tree.TreeLoader
18176  * @extends Roo.util.Observable
18177  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18178  * nodes from a specified URL. The response must be a javascript Array definition
18179  * who's elements are node definition objects. eg:
18180  * <pre><code>
18181    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18182     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18183 </code></pre>
18184  * <br><br>
18185  * A server request is sent, and child nodes are loaded only when a node is expanded.
18186  * The loading node's id is passed to the server under the parameter name "node" to
18187  * enable the server to produce the correct child nodes.
18188  * <br><br>
18189  * To pass extra parameters, an event handler may be attached to the "beforeload"
18190  * event, and the parameters specified in the TreeLoader's baseParams property:
18191  * <pre><code>
18192     myTreeLoader.on("beforeload", function(treeLoader, node) {
18193         this.baseParams.category = node.attributes.category;
18194     }, this);
18195 </code></pre><
18196  * This would pass an HTTP parameter called "category" to the server containing
18197  * the value of the Node's "category" attribute.
18198  * @constructor
18199  * Creates a new Treeloader.
18200  * @param {Object} config A config object containing config properties.
18201  */
18202 Roo.tree.TreeLoader = function(config){
18203     this.baseParams = {};
18204     this.requestMethod = "POST";
18205     Roo.apply(this, config);
18206
18207     this.addEvents({
18208     
18209         /**
18210          * @event beforeload
18211          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18212          * @param {Object} This TreeLoader object.
18213          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18214          * @param {Object} callback The callback function specified in the {@link #load} call.
18215          */
18216         beforeload : true,
18217         /**
18218          * @event load
18219          * Fires when the node has been successfuly loaded.
18220          * @param {Object} This TreeLoader object.
18221          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18222          * @param {Object} response The response object containing the data from the server.
18223          */
18224         load : true,
18225         /**
18226          * @event loadexception
18227          * Fires if the network request failed.
18228          * @param {Object} This TreeLoader object.
18229          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18230          * @param {Object} response The response object containing the data from the server.
18231          */
18232         loadexception : true,
18233         /**
18234          * @event create
18235          * Fires before a node is created, enabling you to return custom Node types 
18236          * @param {Object} This TreeLoader object.
18237          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18238          */
18239         create : true
18240     });
18241
18242     Roo.tree.TreeLoader.superclass.constructor.call(this);
18243 };
18244
18245 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18246     /**
18247     * @cfg {String} dataUrl The URL from which to request a Json string which
18248     * specifies an array of node definition object representing the child nodes
18249     * to be loaded.
18250     */
18251     /**
18252     * @cfg {Object} baseParams (optional) An object containing properties which
18253     * specify HTTP parameters to be passed to each request for child nodes.
18254     */
18255     /**
18256     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18257     * created by this loader. If the attributes sent by the server have an attribute in this object,
18258     * they take priority.
18259     */
18260     /**
18261     * @cfg {Object} uiProviders (optional) An object containing properties which
18262     * 
18263     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18264     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18265     * <i>uiProvider</i> attribute of a returned child node is a string rather
18266     * than a reference to a TreeNodeUI implementation, this that string value
18267     * is used as a property name in the uiProviders object. You can define the provider named
18268     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18269     */
18270     uiProviders : {},
18271
18272     /**
18273     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18274     * child nodes before loading.
18275     */
18276     clearOnLoad : true,
18277
18278     /**
18279     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18280     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18281     * Grid query { data : [ .....] }
18282     */
18283     
18284     root : false,
18285      /**
18286     * @cfg {String} queryParam (optional) 
18287     * Name of the query as it will be passed on the querystring (defaults to 'node')
18288     * eg. the request will be ?node=[id]
18289     */
18290     
18291     
18292     queryParam: false,
18293     
18294     /**
18295      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18296      * This is called automatically when a node is expanded, but may be used to reload
18297      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18298      * @param {Roo.tree.TreeNode} node
18299      * @param {Function} callback
18300      */
18301     load : function(node, callback){
18302         if(this.clearOnLoad){
18303             while(node.firstChild){
18304                 node.removeChild(node.firstChild);
18305             }
18306         }
18307         if(node.attributes.children){ // preloaded json children
18308             var cs = node.attributes.children;
18309             for(var i = 0, len = cs.length; i < len; i++){
18310                 node.appendChild(this.createNode(cs[i]));
18311             }
18312             if(typeof callback == "function"){
18313                 callback();
18314             }
18315         }else if(this.dataUrl){
18316             this.requestData(node, callback);
18317         }
18318     },
18319
18320     getParams: function(node){
18321         var buf = [], bp = this.baseParams;
18322         for(var key in bp){
18323             if(typeof bp[key] != "function"){
18324                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18325             }
18326         }
18327         var n = this.queryParam === false ? 'node' : this.queryParam;
18328         buf.push(n + "=", encodeURIComponent(node.id));
18329         return buf.join("");
18330     },
18331
18332     requestData : function(node, callback){
18333         if(this.fireEvent("beforeload", this, node, callback) !== false){
18334             this.transId = Roo.Ajax.request({
18335                 method:this.requestMethod,
18336                 url: this.dataUrl||this.url,
18337                 success: this.handleResponse,
18338                 failure: this.handleFailure,
18339                 scope: this,
18340                 argument: {callback: callback, node: node},
18341                 params: this.getParams(node)
18342             });
18343         }else{
18344             // if the load is cancelled, make sure we notify
18345             // the node that we are done
18346             if(typeof callback == "function"){
18347                 callback();
18348             }
18349         }
18350     },
18351
18352     isLoading : function(){
18353         return this.transId ? true : false;
18354     },
18355
18356     abort : function(){
18357         if(this.isLoading()){
18358             Roo.Ajax.abort(this.transId);
18359         }
18360     },
18361
18362     // private
18363     createNode : function(attr){
18364         // apply baseAttrs, nice idea Corey!
18365         if(this.baseAttrs){
18366             Roo.applyIf(attr, this.baseAttrs);
18367         }
18368         if(this.applyLoader !== false){
18369             attr.loader = this;
18370         }
18371         // uiProvider = depreciated..
18372         
18373         if(typeof(attr.uiProvider) == 'string'){
18374            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18375                 /**  eval:var:attr */ eval(attr.uiProvider);
18376         }
18377         if(typeof(this.uiProviders['default']) != 'undefined') {
18378             attr.uiProvider = this.uiProviders['default'];
18379         }
18380         
18381         this.fireEvent('create', this, attr);
18382         
18383         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18384         return(attr.leaf ?
18385                         new Roo.tree.TreeNode(attr) :
18386                         new Roo.tree.AsyncTreeNode(attr));
18387     },
18388
18389     processResponse : function(response, node, callback){
18390         var json = response.responseText;
18391         try {
18392             
18393             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18394             if (this.root !== false) {
18395                 o = o[this.root];
18396             }
18397             
18398             for(var i = 0, len = o.length; i < len; i++){
18399                 var n = this.createNode(o[i]);
18400                 if(n){
18401                     node.appendChild(n);
18402                 }
18403             }
18404             if(typeof callback == "function"){
18405                 callback(this, node);
18406             }
18407         }catch(e){
18408             this.handleFailure(response);
18409         }
18410     },
18411
18412     handleResponse : function(response){
18413         this.transId = false;
18414         var a = response.argument;
18415         this.processResponse(response, a.node, a.callback);
18416         this.fireEvent("load", this, a.node, response);
18417     },
18418
18419     handleFailure : function(response){
18420         this.transId = false;
18421         var a = response.argument;
18422         this.fireEvent("loadexception", this, a.node, response);
18423         if(typeof a.callback == "function"){
18424             a.callback(this, a.node);
18425         }
18426     }
18427 });/*
18428  * Based on:
18429  * Ext JS Library 1.1.1
18430  * Copyright(c) 2006-2007, Ext JS, LLC.
18431  *
18432  * Originally Released Under LGPL - original licence link has changed is not relivant.
18433  *
18434  * Fork - LGPL
18435  * <script type="text/javascript">
18436  */
18437
18438 /**
18439 * @class Roo.tree.TreeFilter
18440 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18441 * @param {TreePanel} tree
18442 * @param {Object} config (optional)
18443  */
18444 Roo.tree.TreeFilter = function(tree, config){
18445     this.tree = tree;
18446     this.filtered = {};
18447     Roo.apply(this, config);
18448 };
18449
18450 Roo.tree.TreeFilter.prototype = {
18451     clearBlank:false,
18452     reverse:false,
18453     autoClear:false,
18454     remove:false,
18455
18456      /**
18457      * Filter the data by a specific attribute.
18458      * @param {String/RegExp} value Either string that the attribute value
18459      * should start with or a RegExp to test against the attribute
18460      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18461      * @param {TreeNode} startNode (optional) The node to start the filter at.
18462      */
18463     filter : function(value, attr, startNode){
18464         attr = attr || "text";
18465         var f;
18466         if(typeof value == "string"){
18467             var vlen = value.length;
18468             // auto clear empty filter
18469             if(vlen == 0 && this.clearBlank){
18470                 this.clear();
18471                 return;
18472             }
18473             value = value.toLowerCase();
18474             f = function(n){
18475                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18476             };
18477         }else if(value.exec){ // regex?
18478             f = function(n){
18479                 return value.test(n.attributes[attr]);
18480             };
18481         }else{
18482             throw 'Illegal filter type, must be string or regex';
18483         }
18484         this.filterBy(f, null, startNode);
18485         },
18486
18487     /**
18488      * Filter by a function. The passed function will be called with each
18489      * node in the tree (or from the startNode). If the function returns true, the node is kept
18490      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18491      * @param {Function} fn The filter function
18492      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18493      */
18494     filterBy : function(fn, scope, startNode){
18495         startNode = startNode || this.tree.root;
18496         if(this.autoClear){
18497             this.clear();
18498         }
18499         var af = this.filtered, rv = this.reverse;
18500         var f = function(n){
18501             if(n == startNode){
18502                 return true;
18503             }
18504             if(af[n.id]){
18505                 return false;
18506             }
18507             var m = fn.call(scope || n, n);
18508             if(!m || rv){
18509                 af[n.id] = n;
18510                 n.ui.hide();
18511                 return false;
18512             }
18513             return true;
18514         };
18515         startNode.cascade(f);
18516         if(this.remove){
18517            for(var id in af){
18518                if(typeof id != "function"){
18519                    var n = af[id];
18520                    if(n && n.parentNode){
18521                        n.parentNode.removeChild(n);
18522                    }
18523                }
18524            }
18525         }
18526     },
18527
18528     /**
18529      * Clears the current filter. Note: with the "remove" option
18530      * set a filter cannot be cleared.
18531      */
18532     clear : function(){
18533         var t = this.tree;
18534         var af = this.filtered;
18535         for(var id in af){
18536             if(typeof id != "function"){
18537                 var n = af[id];
18538                 if(n){
18539                     n.ui.show();
18540                 }
18541             }
18542         }
18543         this.filtered = {};
18544     }
18545 };
18546 /*
18547  * Based on:
18548  * Ext JS Library 1.1.1
18549  * Copyright(c) 2006-2007, Ext JS, LLC.
18550  *
18551  * Originally Released Under LGPL - original licence link has changed is not relivant.
18552  *
18553  * Fork - LGPL
18554  * <script type="text/javascript">
18555  */
18556  
18557
18558 /**
18559  * @class Roo.tree.TreeSorter
18560  * Provides sorting of nodes in a TreePanel
18561  * 
18562  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18563  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18564  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18565  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18566  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18567  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18568  * @constructor
18569  * @param {TreePanel} tree
18570  * @param {Object} config
18571  */
18572 Roo.tree.TreeSorter = function(tree, config){
18573     Roo.apply(this, config);
18574     tree.on("beforechildrenrendered", this.doSort, this);
18575     tree.on("append", this.updateSort, this);
18576     tree.on("insert", this.updateSort, this);
18577     
18578     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18579     var p = this.property || "text";
18580     var sortType = this.sortType;
18581     var fs = this.folderSort;
18582     var cs = this.caseSensitive === true;
18583     var leafAttr = this.leafAttr || 'leaf';
18584
18585     this.sortFn = function(n1, n2){
18586         if(fs){
18587             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18588                 return 1;
18589             }
18590             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18591                 return -1;
18592             }
18593         }
18594         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18595         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18596         if(v1 < v2){
18597                         return dsc ? +1 : -1;
18598                 }else if(v1 > v2){
18599                         return dsc ? -1 : +1;
18600         }else{
18601                 return 0;
18602         }
18603     };
18604 };
18605
18606 Roo.tree.TreeSorter.prototype = {
18607     doSort : function(node){
18608         node.sort(this.sortFn);
18609     },
18610     
18611     compareNodes : function(n1, n2){
18612         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18613     },
18614     
18615     updateSort : function(tree, node){
18616         if(node.childrenRendered){
18617             this.doSort.defer(1, this, [node]);
18618         }
18619     }
18620 };/*
18621  * Based on:
18622  * Ext JS Library 1.1.1
18623  * Copyright(c) 2006-2007, Ext JS, LLC.
18624  *
18625  * Originally Released Under LGPL - original licence link has changed is not relivant.
18626  *
18627  * Fork - LGPL
18628  * <script type="text/javascript">
18629  */
18630
18631 if(Roo.dd.DropZone){
18632     
18633 Roo.tree.TreeDropZone = function(tree, config){
18634     this.allowParentInsert = false;
18635     this.allowContainerDrop = false;
18636     this.appendOnly = false;
18637     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18638     this.tree = tree;
18639     this.lastInsertClass = "x-tree-no-status";
18640     this.dragOverData = {};
18641 };
18642
18643 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18644     ddGroup : "TreeDD",
18645     
18646     expandDelay : 1000,
18647     
18648     expandNode : function(node){
18649         if(node.hasChildNodes() && !node.isExpanded()){
18650             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18651         }
18652     },
18653     
18654     queueExpand : function(node){
18655         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18656     },
18657     
18658     cancelExpand : function(){
18659         if(this.expandProcId){
18660             clearTimeout(this.expandProcId);
18661             this.expandProcId = false;
18662         }
18663     },
18664     
18665     isValidDropPoint : function(n, pt, dd, e, data){
18666         if(!n || !data){ return false; }
18667         var targetNode = n.node;
18668         var dropNode = data.node;
18669         // default drop rules
18670         if(!(targetNode && targetNode.isTarget && pt)){
18671             return false;
18672         }
18673         if(pt == "append" && targetNode.allowChildren === false){
18674             return false;
18675         }
18676         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18677             return false;
18678         }
18679         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18680             return false;
18681         }
18682         // reuse the object
18683         var overEvent = this.dragOverData;
18684         overEvent.tree = this.tree;
18685         overEvent.target = targetNode;
18686         overEvent.data = data;
18687         overEvent.point = pt;
18688         overEvent.source = dd;
18689         overEvent.rawEvent = e;
18690         overEvent.dropNode = dropNode;
18691         overEvent.cancel = false;  
18692         var result = this.tree.fireEvent("nodedragover", overEvent);
18693         return overEvent.cancel === false && result !== false;
18694     },
18695     
18696     getDropPoint : function(e, n, dd){
18697         var tn = n.node;
18698         if(tn.isRoot){
18699             return tn.allowChildren !== false ? "append" : false; // always append for root
18700         }
18701         var dragEl = n.ddel;
18702         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18703         var y = Roo.lib.Event.getPageY(e);
18704         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18705         
18706         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18707         var noAppend = tn.allowChildren === false;
18708         if(this.appendOnly || tn.parentNode.allowChildren === false){
18709             return noAppend ? false : "append";
18710         }
18711         var noBelow = false;
18712         if(!this.allowParentInsert){
18713             noBelow = tn.hasChildNodes() && tn.isExpanded();
18714         }
18715         var q = (b - t) / (noAppend ? 2 : 3);
18716         if(y >= t && y < (t + q)){
18717             return "above";
18718         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18719             return "below";
18720         }else{
18721             return "append";
18722         }
18723     },
18724     
18725     onNodeEnter : function(n, dd, e, data){
18726         this.cancelExpand();
18727     },
18728     
18729     onNodeOver : function(n, dd, e, data){
18730         var pt = this.getDropPoint(e, n, dd);
18731         var node = n.node;
18732         
18733         // auto node expand check
18734         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18735             this.queueExpand(node);
18736         }else if(pt != "append"){
18737             this.cancelExpand();
18738         }
18739         
18740         // set the insert point style on the target node
18741         var returnCls = this.dropNotAllowed;
18742         if(this.isValidDropPoint(n, pt, dd, e, data)){
18743            if(pt){
18744                var el = n.ddel;
18745                var cls;
18746                if(pt == "above"){
18747                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18748                    cls = "x-tree-drag-insert-above";
18749                }else if(pt == "below"){
18750                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18751                    cls = "x-tree-drag-insert-below";
18752                }else{
18753                    returnCls = "x-tree-drop-ok-append";
18754                    cls = "x-tree-drag-append";
18755                }
18756                if(this.lastInsertClass != cls){
18757                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18758                    this.lastInsertClass = cls;
18759                }
18760            }
18761        }
18762        return returnCls;
18763     },
18764     
18765     onNodeOut : function(n, dd, e, data){
18766         this.cancelExpand();
18767         this.removeDropIndicators(n);
18768     },
18769     
18770     onNodeDrop : function(n, dd, e, data){
18771         var point = this.getDropPoint(e, n, dd);
18772         var targetNode = n.node;
18773         targetNode.ui.startDrop();
18774         if(!this.isValidDropPoint(n, point, dd, e, data)){
18775             targetNode.ui.endDrop();
18776             return false;
18777         }
18778         // first try to find the drop node
18779         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18780         var dropEvent = {
18781             tree : this.tree,
18782             target: targetNode,
18783             data: data,
18784             point: point,
18785             source: dd,
18786             rawEvent: e,
18787             dropNode: dropNode,
18788             cancel: !dropNode   
18789         };
18790         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18791         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18792             targetNode.ui.endDrop();
18793             return false;
18794         }
18795         // allow target changing
18796         targetNode = dropEvent.target;
18797         if(point == "append" && !targetNode.isExpanded()){
18798             targetNode.expand(false, null, function(){
18799                 this.completeDrop(dropEvent);
18800             }.createDelegate(this));
18801         }else{
18802             this.completeDrop(dropEvent);
18803         }
18804         return true;
18805     },
18806     
18807     completeDrop : function(de){
18808         var ns = de.dropNode, p = de.point, t = de.target;
18809         if(!(ns instanceof Array)){
18810             ns = [ns];
18811         }
18812         var n;
18813         for(var i = 0, len = ns.length; i < len; i++){
18814             n = ns[i];
18815             if(p == "above"){
18816                 t.parentNode.insertBefore(n, t);
18817             }else if(p == "below"){
18818                 t.parentNode.insertBefore(n, t.nextSibling);
18819             }else{
18820                 t.appendChild(n);
18821             }
18822         }
18823         n.ui.focus();
18824         if(this.tree.hlDrop){
18825             n.ui.highlight();
18826         }
18827         t.ui.endDrop();
18828         this.tree.fireEvent("nodedrop", de);
18829     },
18830     
18831     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18832         if(this.tree.hlDrop){
18833             dropNode.ui.focus();
18834             dropNode.ui.highlight();
18835         }
18836         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18837     },
18838     
18839     getTree : function(){
18840         return this.tree;
18841     },
18842     
18843     removeDropIndicators : function(n){
18844         if(n && n.ddel){
18845             var el = n.ddel;
18846             Roo.fly(el).removeClass([
18847                     "x-tree-drag-insert-above",
18848                     "x-tree-drag-insert-below",
18849                     "x-tree-drag-append"]);
18850             this.lastInsertClass = "_noclass";
18851         }
18852     },
18853     
18854     beforeDragDrop : function(target, e, id){
18855         this.cancelExpand();
18856         return true;
18857     },
18858     
18859     afterRepair : function(data){
18860         if(data && Roo.enableFx){
18861             data.node.ui.highlight();
18862         }
18863         this.hideProxy();
18864     }    
18865 });
18866
18867 }
18868 /*
18869  * Based on:
18870  * Ext JS Library 1.1.1
18871  * Copyright(c) 2006-2007, Ext JS, LLC.
18872  *
18873  * Originally Released Under LGPL - original licence link has changed is not relivant.
18874  *
18875  * Fork - LGPL
18876  * <script type="text/javascript">
18877  */
18878  
18879
18880 if(Roo.dd.DragZone){
18881 Roo.tree.TreeDragZone = function(tree, config){
18882     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18883     this.tree = tree;
18884 };
18885
18886 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18887     ddGroup : "TreeDD",
18888     
18889     onBeforeDrag : function(data, e){
18890         var n = data.node;
18891         return n && n.draggable && !n.disabled;
18892     },
18893     
18894     onInitDrag : function(e){
18895         var data = this.dragData;
18896         this.tree.getSelectionModel().select(data.node);
18897         this.proxy.update("");
18898         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18899         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18900     },
18901     
18902     getRepairXY : function(e, data){
18903         return data.node.ui.getDDRepairXY();
18904     },
18905     
18906     onEndDrag : function(data, e){
18907         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18908     },
18909     
18910     onValidDrop : function(dd, e, id){
18911         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18912         this.hideProxy();
18913     },
18914     
18915     beforeInvalidDrop : function(e, id){
18916         // this scrolls the original position back into view
18917         var sm = this.tree.getSelectionModel();
18918         sm.clearSelections();
18919         sm.select(this.dragData.node);
18920     }
18921 });
18922 }/*
18923  * Based on:
18924  * Ext JS Library 1.1.1
18925  * Copyright(c) 2006-2007, Ext JS, LLC.
18926  *
18927  * Originally Released Under LGPL - original licence link has changed is not relivant.
18928  *
18929  * Fork - LGPL
18930  * <script type="text/javascript">
18931  */
18932 /**
18933  * @class Roo.tree.TreeEditor
18934  * @extends Roo.Editor
18935  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18936  * as the editor field.
18937  * @constructor
18938  * @param {TreePanel} tree
18939  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18940  */
18941 Roo.tree.TreeEditor = function(tree, config){
18942     config = config || {};
18943     var field = config.events ? config : new Roo.form.TextField(config);
18944     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18945
18946     this.tree = tree;
18947
18948     tree.on('beforeclick', this.beforeNodeClick, this);
18949     tree.getTreeEl().on('mousedown', this.hide, this);
18950     this.on('complete', this.updateNode, this);
18951     this.on('beforestartedit', this.fitToTree, this);
18952     this.on('startedit', this.bindScroll, this, {delay:10});
18953     this.on('specialkey', this.onSpecialKey, this);
18954 };
18955
18956 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18957     /**
18958      * @cfg {String} alignment
18959      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18960      */
18961     alignment: "l-l",
18962     // inherit
18963     autoSize: false,
18964     /**
18965      * @cfg {Boolean} hideEl
18966      * True to hide the bound element while the editor is displayed (defaults to false)
18967      */
18968     hideEl : false,
18969     /**
18970      * @cfg {String} cls
18971      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18972      */
18973     cls: "x-small-editor x-tree-editor",
18974     /**
18975      * @cfg {Boolean} shim
18976      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18977      */
18978     shim:false,
18979     // inherit
18980     shadow:"frame",
18981     /**
18982      * @cfg {Number} maxWidth
18983      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18984      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18985      * scroll and client offsets into account prior to each edit.
18986      */
18987     maxWidth: 250,
18988
18989     editDelay : 350,
18990
18991     // private
18992     fitToTree : function(ed, el){
18993         var td = this.tree.getTreeEl().dom, nd = el.dom;
18994         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18995             td.scrollLeft = nd.offsetLeft;
18996         }
18997         var w = Math.min(
18998                 this.maxWidth,
18999                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19000         this.setSize(w, '');
19001     },
19002
19003     // private
19004     triggerEdit : function(node){
19005         this.completeEdit();
19006         this.editNode = node;
19007         this.startEdit(node.ui.textNode, node.text);
19008     },
19009
19010     // private
19011     bindScroll : function(){
19012         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19013     },
19014
19015     // private
19016     beforeNodeClick : function(node, e){
19017         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19018         this.lastClick = new Date();
19019         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19020             e.stopEvent();
19021             this.triggerEdit(node);
19022             return false;
19023         }
19024     },
19025
19026     // private
19027     updateNode : function(ed, value){
19028         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19029         this.editNode.setText(value);
19030     },
19031
19032     // private
19033     onHide : function(){
19034         Roo.tree.TreeEditor.superclass.onHide.call(this);
19035         if(this.editNode){
19036             this.editNode.ui.focus();
19037         }
19038     },
19039
19040     // private
19041     onSpecialKey : function(field, e){
19042         var k = e.getKey();
19043         if(k == e.ESC){
19044             e.stopEvent();
19045             this.cancelEdit();
19046         }else if(k == e.ENTER && !e.hasModifier()){
19047             e.stopEvent();
19048             this.completeEdit();
19049         }
19050     }
19051 });//<Script type="text/javascript">
19052 /*
19053  * Based on:
19054  * Ext JS Library 1.1.1
19055  * Copyright(c) 2006-2007, Ext JS, LLC.
19056  *
19057  * Originally Released Under LGPL - original licence link has changed is not relivant.
19058  *
19059  * Fork - LGPL
19060  * <script type="text/javascript">
19061  */
19062  
19063 /**
19064  * Not documented??? - probably should be...
19065  */
19066
19067 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19068     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19069     
19070     renderElements : function(n, a, targetNode, bulkRender){
19071         //consel.log("renderElements?");
19072         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19073
19074         var t = n.getOwnerTree();
19075         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19076         
19077         var cols = t.columns;
19078         var bw = t.borderWidth;
19079         var c = cols[0];
19080         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19081          var cb = typeof a.checked == "boolean";
19082         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19083         var colcls = 'x-t-' + tid + '-c0';
19084         var buf = [
19085             '<li class="x-tree-node">',
19086             
19087                 
19088                 '<div class="x-tree-node-el ', a.cls,'">',
19089                     // extran...
19090                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19091                 
19092                 
19093                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19094                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19095                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19096                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19097                            (a.iconCls ? ' '+a.iconCls : ''),
19098                            '" unselectable="on" />',
19099                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19100                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19101                              
19102                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19103                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19104                             '<span unselectable="on" qtip="' + tx + '">',
19105                              tx,
19106                              '</span></a>' ,
19107                     '</div>',
19108                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19109                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19110                  ];
19111         for(var i = 1, len = cols.length; i < len; i++){
19112             c = cols[i];
19113             colcls = 'x-t-' + tid + '-c' +i;
19114             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19115             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19116                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19117                       "</div>");
19118          }
19119          
19120          buf.push(
19121             '</a>',
19122             '<div class="x-clear"></div></div>',
19123             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19124             "</li>");
19125         
19126         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19127             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19128                                 n.nextSibling.ui.getEl(), buf.join(""));
19129         }else{
19130             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19131         }
19132         var el = this.wrap.firstChild;
19133         this.elRow = el;
19134         this.elNode = el.firstChild;
19135         this.ranchor = el.childNodes[1];
19136         this.ctNode = this.wrap.childNodes[1];
19137         var cs = el.firstChild.childNodes;
19138         this.indentNode = cs[0];
19139         this.ecNode = cs[1];
19140         this.iconNode = cs[2];
19141         var index = 3;
19142         if(cb){
19143             this.checkbox = cs[3];
19144             index++;
19145         }
19146         this.anchor = cs[index];
19147         
19148         this.textNode = cs[index].firstChild;
19149         
19150         //el.on("click", this.onClick, this);
19151         //el.on("dblclick", this.onDblClick, this);
19152         
19153         
19154        // console.log(this);
19155     },
19156     initEvents : function(){
19157         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19158         
19159             
19160         var a = this.ranchor;
19161
19162         var el = Roo.get(a);
19163
19164         if(Roo.isOpera){ // opera render bug ignores the CSS
19165             el.setStyle("text-decoration", "none");
19166         }
19167
19168         el.on("click", this.onClick, this);
19169         el.on("dblclick", this.onDblClick, this);
19170         el.on("contextmenu", this.onContextMenu, this);
19171         
19172     },
19173     
19174     /*onSelectedChange : function(state){
19175         if(state){
19176             this.focus();
19177             this.addClass("x-tree-selected");
19178         }else{
19179             //this.blur();
19180             this.removeClass("x-tree-selected");
19181         }
19182     },*/
19183     addClass : function(cls){
19184         if(this.elRow){
19185             Roo.fly(this.elRow).addClass(cls);
19186         }
19187         
19188     },
19189     
19190     
19191     removeClass : function(cls){
19192         if(this.elRow){
19193             Roo.fly(this.elRow).removeClass(cls);
19194         }
19195     }
19196
19197     
19198     
19199 });//<Script type="text/javascript">
19200
19201 /*
19202  * Based on:
19203  * Ext JS Library 1.1.1
19204  * Copyright(c) 2006-2007, Ext JS, LLC.
19205  *
19206  * Originally Released Under LGPL - original licence link has changed is not relivant.
19207  *
19208  * Fork - LGPL
19209  * <script type="text/javascript">
19210  */
19211  
19212
19213 /**
19214  * @class Roo.tree.ColumnTree
19215  * @extends Roo.data.TreePanel
19216  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19217  * @cfg {int} borderWidth  compined right/left border allowance
19218  * @constructor
19219  * @param {String/HTMLElement/Element} el The container element
19220  * @param {Object} config
19221  */
19222 Roo.tree.ColumnTree =  function(el, config)
19223 {
19224    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19225    this.addEvents({
19226         /**
19227         * @event resize
19228         * Fire this event on a container when it resizes
19229         * @param {int} w Width
19230         * @param {int} h Height
19231         */
19232        "resize" : true
19233     });
19234     this.on('resize', this.onResize, this);
19235 };
19236
19237 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19238     //lines:false,
19239     
19240     
19241     borderWidth: Roo.isBorderBox ? 0 : 2, 
19242     headEls : false,
19243     
19244     render : function(){
19245         // add the header.....
19246        
19247         Roo.tree.ColumnTree.superclass.render.apply(this);
19248         
19249         this.el.addClass('x-column-tree');
19250         
19251         this.headers = this.el.createChild(
19252             {cls:'x-tree-headers'},this.innerCt.dom);
19253    
19254         var cols = this.columns, c;
19255         var totalWidth = 0;
19256         this.headEls = [];
19257         var  len = cols.length;
19258         for(var i = 0; i < len; i++){
19259              c = cols[i];
19260              totalWidth += c.width;
19261             this.headEls.push(this.headers.createChild({
19262                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19263                  cn: {
19264                      cls:'x-tree-hd-text',
19265                      html: c.header
19266                  },
19267                  style:'width:'+(c.width-this.borderWidth)+'px;'
19268              }));
19269         }
19270         this.headers.createChild({cls:'x-clear'});
19271         // prevent floats from wrapping when clipped
19272         this.headers.setWidth(totalWidth);
19273         //this.innerCt.setWidth(totalWidth);
19274         this.innerCt.setStyle({ overflow: 'auto' });
19275         this.onResize(this.width, this.height);
19276              
19277         
19278     },
19279     onResize : function(w,h)
19280     {
19281         this.height = h;
19282         this.width = w;
19283         // resize cols..
19284         this.innerCt.setWidth(this.width);
19285         this.innerCt.setHeight(this.height-20);
19286         
19287         // headers...
19288         var cols = this.columns, c;
19289         var totalWidth = 0;
19290         var expEl = false;
19291         var len = cols.length;
19292         for(var i = 0; i < len; i++){
19293             c = cols[i];
19294             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19295                 // it's the expander..
19296                 expEl  = this.headEls[i];
19297                 continue;
19298             }
19299             totalWidth += c.width;
19300             
19301         }
19302         if (expEl) {
19303             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19304         }
19305         this.headers.setWidth(w-20);
19306
19307         
19308         
19309         
19310     }
19311 });
19312 /*
19313  * Based on:
19314  * Ext JS Library 1.1.1
19315  * Copyright(c) 2006-2007, Ext JS, LLC.
19316  *
19317  * Originally Released Under LGPL - original licence link has changed is not relivant.
19318  *
19319  * Fork - LGPL
19320  * <script type="text/javascript">
19321  */
19322  
19323 /**
19324  * @class Roo.menu.Menu
19325  * @extends Roo.util.Observable
19326  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19327  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19328  * @constructor
19329  * Creates a new Menu
19330  * @param {Object} config Configuration options
19331  */
19332 Roo.menu.Menu = function(config){
19333     Roo.apply(this, config);
19334     this.id = this.id || Roo.id();
19335     this.addEvents({
19336         /**
19337          * @event beforeshow
19338          * Fires before this menu is displayed
19339          * @param {Roo.menu.Menu} this
19340          */
19341         beforeshow : true,
19342         /**
19343          * @event beforehide
19344          * Fires before this menu is hidden
19345          * @param {Roo.menu.Menu} this
19346          */
19347         beforehide : true,
19348         /**
19349          * @event show
19350          * Fires after this menu is displayed
19351          * @param {Roo.menu.Menu} this
19352          */
19353         show : true,
19354         /**
19355          * @event hide
19356          * Fires after this menu is hidden
19357          * @param {Roo.menu.Menu} this
19358          */
19359         hide : true,
19360         /**
19361          * @event click
19362          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19363          * @param {Roo.menu.Menu} this
19364          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19365          * @param {Roo.EventObject} e
19366          */
19367         click : true,
19368         /**
19369          * @event mouseover
19370          * Fires when the mouse is hovering over this menu
19371          * @param {Roo.menu.Menu} this
19372          * @param {Roo.EventObject} e
19373          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19374          */
19375         mouseover : true,
19376         /**
19377          * @event mouseout
19378          * Fires when the mouse exits this menu
19379          * @param {Roo.menu.Menu} this
19380          * @param {Roo.EventObject} e
19381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19382          */
19383         mouseout : true,
19384         /**
19385          * @event itemclick
19386          * Fires when a menu item contained in this menu is clicked
19387          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19388          * @param {Roo.EventObject} e
19389          */
19390         itemclick: true
19391     });
19392     if (this.registerMenu) {
19393         Roo.menu.MenuMgr.register(this);
19394     }
19395     
19396     var mis = this.items;
19397     this.items = new Roo.util.MixedCollection();
19398     if(mis){
19399         this.add.apply(this, mis);
19400     }
19401 };
19402
19403 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19404     /**
19405      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19406      */
19407     minWidth : 120,
19408     /**
19409      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19410      * for bottom-right shadow (defaults to "sides")
19411      */
19412     shadow : "sides",
19413     /**
19414      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19415      * this menu (defaults to "tl-tr?")
19416      */
19417     subMenuAlign : "tl-tr?",
19418     /**
19419      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19420      * relative to its element of origin (defaults to "tl-bl?")
19421      */
19422     defaultAlign : "tl-bl?",
19423     /**
19424      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19425      */
19426     allowOtherMenus : false,
19427     /**
19428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19429      */
19430     registerMenu : true,
19431
19432     hidden:true,
19433
19434     // private
19435     render : function(){
19436         if(this.el){
19437             return;
19438         }
19439         var el = this.el = new Roo.Layer({
19440             cls: "x-menu",
19441             shadow:this.shadow,
19442             constrain: false,
19443             parentEl: this.parentEl || document.body,
19444             zindex:15000
19445         });
19446
19447         this.keyNav = new Roo.menu.MenuNav(this);
19448
19449         if(this.plain){
19450             el.addClass("x-menu-plain");
19451         }
19452         if(this.cls){
19453             el.addClass(this.cls);
19454         }
19455         // generic focus element
19456         this.focusEl = el.createChild({
19457             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19458         });
19459         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19460         ul.on("click", this.onClick, this);
19461         ul.on("mouseover", this.onMouseOver, this);
19462         ul.on("mouseout", this.onMouseOut, this);
19463         this.items.each(function(item){
19464             var li = document.createElement("li");
19465             li.className = "x-menu-list-item";
19466             ul.dom.appendChild(li);
19467             item.render(li, this);
19468         }, this);
19469         this.ul = ul;
19470         this.autoWidth();
19471     },
19472
19473     // private
19474     autoWidth : function(){
19475         var el = this.el, ul = this.ul;
19476         if(!el){
19477             return;
19478         }
19479         var w = this.width;
19480         if(w){
19481             el.setWidth(w);
19482         }else if(Roo.isIE){
19483             el.setWidth(this.minWidth);
19484             var t = el.dom.offsetWidth; // force recalc
19485             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19486         }
19487     },
19488
19489     // private
19490     delayAutoWidth : function(){
19491         if(this.rendered){
19492             if(!this.awTask){
19493                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19494             }
19495             this.awTask.delay(20);
19496         }
19497     },
19498
19499     // private
19500     findTargetItem : function(e){
19501         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19502         if(t && t.menuItemId){
19503             return this.items.get(t.menuItemId);
19504         }
19505     },
19506
19507     // private
19508     onClick : function(e){
19509         var t;
19510         if(t = this.findTargetItem(e)){
19511             t.onClick(e);
19512             this.fireEvent("click", this, t, e);
19513         }
19514     },
19515
19516     // private
19517     setActiveItem : function(item, autoExpand){
19518         if(item != this.activeItem){
19519             if(this.activeItem){
19520                 this.activeItem.deactivate();
19521             }
19522             this.activeItem = item;
19523             item.activate(autoExpand);
19524         }else if(autoExpand){
19525             item.expandMenu();
19526         }
19527     },
19528
19529     // private
19530     tryActivate : function(start, step){
19531         var items = this.items;
19532         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19533             var item = items.get(i);
19534             if(!item.disabled && item.canActivate){
19535                 this.setActiveItem(item, false);
19536                 return item;
19537             }
19538         }
19539         return false;
19540     },
19541
19542     // private
19543     onMouseOver : function(e){
19544         var t;
19545         if(t = this.findTargetItem(e)){
19546             if(t.canActivate && !t.disabled){
19547                 this.setActiveItem(t, true);
19548             }
19549         }
19550         this.fireEvent("mouseover", this, e, t);
19551     },
19552
19553     // private
19554     onMouseOut : function(e){
19555         var t;
19556         if(t = this.findTargetItem(e)){
19557             if(t == this.activeItem && t.shouldDeactivate(e)){
19558                 this.activeItem.deactivate();
19559                 delete this.activeItem;
19560             }
19561         }
19562         this.fireEvent("mouseout", this, e, t);
19563     },
19564
19565     /**
19566      * Read-only.  Returns true if the menu is currently displayed, else false.
19567      * @type Boolean
19568      */
19569     isVisible : function(){
19570         return this.el && !this.hidden;
19571     },
19572
19573     /**
19574      * Displays this menu relative to another element
19575      * @param {String/HTMLElement/Roo.Element} element The element to align to
19576      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19577      * the element (defaults to this.defaultAlign)
19578      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19579      */
19580     show : function(el, pos, parentMenu){
19581         this.parentMenu = parentMenu;
19582         if(!this.el){
19583             this.render();
19584         }
19585         this.fireEvent("beforeshow", this);
19586         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19587     },
19588
19589     /**
19590      * Displays this menu at a specific xy position
19591      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19592      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19593      */
19594     showAt : function(xy, parentMenu, /* private: */_e){
19595         this.parentMenu = parentMenu;
19596         if(!this.el){
19597             this.render();
19598         }
19599         if(_e !== false){
19600             this.fireEvent("beforeshow", this);
19601             xy = this.el.adjustForConstraints(xy);
19602         }
19603         this.el.setXY(xy);
19604         this.el.show();
19605         this.hidden = false;
19606         this.focus();
19607         this.fireEvent("show", this);
19608     },
19609
19610     focus : function(){
19611         if(!this.hidden){
19612             this.doFocus.defer(50, this);
19613         }
19614     },
19615
19616     doFocus : function(){
19617         if(!this.hidden){
19618             this.focusEl.focus();
19619         }
19620     },
19621
19622     /**
19623      * Hides this menu and optionally all parent menus
19624      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19625      */
19626     hide : function(deep){
19627         if(this.el && this.isVisible()){
19628             this.fireEvent("beforehide", this);
19629             if(this.activeItem){
19630                 this.activeItem.deactivate();
19631                 this.activeItem = null;
19632             }
19633             this.el.hide();
19634             this.hidden = true;
19635             this.fireEvent("hide", this);
19636         }
19637         if(deep === true && this.parentMenu){
19638             this.parentMenu.hide(true);
19639         }
19640     },
19641
19642     /**
19643      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19644      * Any of the following are valid:
19645      * <ul>
19646      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19647      * <li>An HTMLElement object which will be converted to a menu item</li>
19648      * <li>A menu item config object that will be created as a new menu item</li>
19649      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19650      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19651      * </ul>
19652      * Usage:
19653      * <pre><code>
19654 // Create the menu
19655 var menu = new Roo.menu.Menu();
19656
19657 // Create a menu item to add by reference
19658 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19659
19660 // Add a bunch of items at once using different methods.
19661 // Only the last item added will be returned.
19662 var item = menu.add(
19663     menuItem,                // add existing item by ref
19664     'Dynamic Item',          // new TextItem
19665     '-',                     // new separator
19666     { text: 'Config Item' }  // new item by config
19667 );
19668 </code></pre>
19669      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19670      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19671      */
19672     add : function(){
19673         var a = arguments, l = a.length, item;
19674         for(var i = 0; i < l; i++){
19675             var el = a[i];
19676             if ((typeof(el) == "object") && el.xtype && el.xns) {
19677                 el = Roo.factory(el, Roo.menu);
19678             }
19679             
19680             if(el.render){ // some kind of Item
19681                 item = this.addItem(el);
19682             }else if(typeof el == "string"){ // string
19683                 if(el == "separator" || el == "-"){
19684                     item = this.addSeparator();
19685                 }else{
19686                     item = this.addText(el);
19687                 }
19688             }else if(el.tagName || el.el){ // element
19689                 item = this.addElement(el);
19690             }else if(typeof el == "object"){ // must be menu item config?
19691                 item = this.addMenuItem(el);
19692             }
19693         }
19694         return item;
19695     },
19696
19697     /**
19698      * Returns this menu's underlying {@link Roo.Element} object
19699      * @return {Roo.Element} The element
19700      */
19701     getEl : function(){
19702         if(!this.el){
19703             this.render();
19704         }
19705         return this.el;
19706     },
19707
19708     /**
19709      * Adds a separator bar to the menu
19710      * @return {Roo.menu.Item} The menu item that was added
19711      */
19712     addSeparator : function(){
19713         return this.addItem(new Roo.menu.Separator());
19714     },
19715
19716     /**
19717      * Adds an {@link Roo.Element} object to the menu
19718      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19719      * @return {Roo.menu.Item} The menu item that was added
19720      */
19721     addElement : function(el){
19722         return this.addItem(new Roo.menu.BaseItem(el));
19723     },
19724
19725     /**
19726      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19727      * @param {Roo.menu.Item} item The menu item to add
19728      * @return {Roo.menu.Item} The menu item that was added
19729      */
19730     addItem : function(item){
19731         this.items.add(item);
19732         if(this.ul){
19733             var li = document.createElement("li");
19734             li.className = "x-menu-list-item";
19735             this.ul.dom.appendChild(li);
19736             item.render(li, this);
19737             this.delayAutoWidth();
19738         }
19739         return item;
19740     },
19741
19742     /**
19743      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19744      * @param {Object} config A MenuItem config object
19745      * @return {Roo.menu.Item} The menu item that was added
19746      */
19747     addMenuItem : function(config){
19748         if(!(config instanceof Roo.menu.Item)){
19749             if(typeof config.checked == "boolean"){ // must be check menu item config?
19750                 config = new Roo.menu.CheckItem(config);
19751             }else{
19752                 config = new Roo.menu.Item(config);
19753             }
19754         }
19755         return this.addItem(config);
19756     },
19757
19758     /**
19759      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19760      * @param {String} text The text to display in the menu item
19761      * @return {Roo.menu.Item} The menu item that was added
19762      */
19763     addText : function(text){
19764         return this.addItem(new Roo.menu.TextItem({ text : text }));
19765     },
19766
19767     /**
19768      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19769      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19770      * @param {Roo.menu.Item} item The menu item to add
19771      * @return {Roo.menu.Item} The menu item that was added
19772      */
19773     insert : function(index, item){
19774         this.items.insert(index, item);
19775         if(this.ul){
19776             var li = document.createElement("li");
19777             li.className = "x-menu-list-item";
19778             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19779             item.render(li, this);
19780             this.delayAutoWidth();
19781         }
19782         return item;
19783     },
19784
19785     /**
19786      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19787      * @param {Roo.menu.Item} item The menu item to remove
19788      */
19789     remove : function(item){
19790         this.items.removeKey(item.id);
19791         item.destroy();
19792     },
19793
19794     /**
19795      * Removes and destroys all items in the menu
19796      */
19797     removeAll : function(){
19798         var f;
19799         while(f = this.items.first()){
19800             this.remove(f);
19801         }
19802     }
19803 });
19804
19805 // MenuNav is a private utility class used internally by the Menu
19806 Roo.menu.MenuNav = function(menu){
19807     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19808     this.scope = this.menu = menu;
19809 };
19810
19811 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19812     doRelay : function(e, h){
19813         var k = e.getKey();
19814         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19815             this.menu.tryActivate(0, 1);
19816             return false;
19817         }
19818         return h.call(this.scope || this, e, this.menu);
19819     },
19820
19821     up : function(e, m){
19822         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19823             m.tryActivate(m.items.length-1, -1);
19824         }
19825     },
19826
19827     down : function(e, m){
19828         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19829             m.tryActivate(0, 1);
19830         }
19831     },
19832
19833     right : function(e, m){
19834         if(m.activeItem){
19835             m.activeItem.expandMenu(true);
19836         }
19837     },
19838
19839     left : function(e, m){
19840         m.hide();
19841         if(m.parentMenu && m.parentMenu.activeItem){
19842             m.parentMenu.activeItem.activate();
19843         }
19844     },
19845
19846     enter : function(e, m){
19847         if(m.activeItem){
19848             e.stopPropagation();
19849             m.activeItem.onClick(e);
19850             m.fireEvent("click", this, m.activeItem);
19851             return true;
19852         }
19853     }
19854 });/*
19855  * Based on:
19856  * Ext JS Library 1.1.1
19857  * Copyright(c) 2006-2007, Ext JS, LLC.
19858  *
19859  * Originally Released Under LGPL - original licence link has changed is not relivant.
19860  *
19861  * Fork - LGPL
19862  * <script type="text/javascript">
19863  */
19864  
19865 /**
19866  * @class Roo.menu.MenuMgr
19867  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19868  * @singleton
19869  */
19870 Roo.menu.MenuMgr = function(){
19871    var menus, active, groups = {}, attached = false, lastShow = new Date();
19872
19873    // private - called when first menu is created
19874    function init(){
19875        menus = {};
19876        active = new Roo.util.MixedCollection();
19877        Roo.get(document).addKeyListener(27, function(){
19878            if(active.length > 0){
19879                hideAll();
19880            }
19881        });
19882    }
19883
19884    // private
19885    function hideAll(){
19886        if(active && active.length > 0){
19887            var c = active.clone();
19888            c.each(function(m){
19889                m.hide();
19890            });
19891        }
19892    }
19893
19894    // private
19895    function onHide(m){
19896        active.remove(m);
19897        if(active.length < 1){
19898            Roo.get(document).un("mousedown", onMouseDown);
19899            attached = false;
19900        }
19901    }
19902
19903    // private
19904    function onShow(m){
19905        var last = active.last();
19906        lastShow = new Date();
19907        active.add(m);
19908        if(!attached){
19909            Roo.get(document).on("mousedown", onMouseDown);
19910            attached = true;
19911        }
19912        if(m.parentMenu){
19913           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19914           m.parentMenu.activeChild = m;
19915        }else if(last && last.isVisible()){
19916           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19917        }
19918    }
19919
19920    // private
19921    function onBeforeHide(m){
19922        if(m.activeChild){
19923            m.activeChild.hide();
19924        }
19925        if(m.autoHideTimer){
19926            clearTimeout(m.autoHideTimer);
19927            delete m.autoHideTimer;
19928        }
19929    }
19930
19931    // private
19932    function onBeforeShow(m){
19933        var pm = m.parentMenu;
19934        if(!pm && !m.allowOtherMenus){
19935            hideAll();
19936        }else if(pm && pm.activeChild && active != m){
19937            pm.activeChild.hide();
19938        }
19939    }
19940
19941    // private
19942    function onMouseDown(e){
19943        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19944            hideAll();
19945        }
19946    }
19947
19948    // private
19949    function onBeforeCheck(mi, state){
19950        if(state){
19951            var g = groups[mi.group];
19952            for(var i = 0, l = g.length; i < l; i++){
19953                if(g[i] != mi){
19954                    g[i].setChecked(false);
19955                }
19956            }
19957        }
19958    }
19959
19960    return {
19961
19962        /**
19963         * Hides all menus that are currently visible
19964         */
19965        hideAll : function(){
19966             hideAll();  
19967        },
19968
19969        // private
19970        register : function(menu){
19971            if(!menus){
19972                init();
19973            }
19974            menus[menu.id] = menu;
19975            menu.on("beforehide", onBeforeHide);
19976            menu.on("hide", onHide);
19977            menu.on("beforeshow", onBeforeShow);
19978            menu.on("show", onShow);
19979            var g = menu.group;
19980            if(g && menu.events["checkchange"]){
19981                if(!groups[g]){
19982                    groups[g] = [];
19983                }
19984                groups[g].push(menu);
19985                menu.on("checkchange", onCheck);
19986            }
19987        },
19988
19989         /**
19990          * Returns a {@link Roo.menu.Menu} object
19991          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19992          * be used to generate and return a new Menu instance.
19993          */
19994        get : function(menu){
19995            if(typeof menu == "string"){ // menu id
19996                return menus[menu];
19997            }else if(menu.events){  // menu instance
19998                return menu;
19999            }else if(typeof menu.length == 'number'){ // array of menu items?
20000                return new Roo.menu.Menu({items:menu});
20001            }else{ // otherwise, must be a config
20002                return new Roo.menu.Menu(menu);
20003            }
20004        },
20005
20006        // private
20007        unregister : function(menu){
20008            delete menus[menu.id];
20009            menu.un("beforehide", onBeforeHide);
20010            menu.un("hide", onHide);
20011            menu.un("beforeshow", onBeforeShow);
20012            menu.un("show", onShow);
20013            var g = menu.group;
20014            if(g && menu.events["checkchange"]){
20015                groups[g].remove(menu);
20016                menu.un("checkchange", onCheck);
20017            }
20018        },
20019
20020        // private
20021        registerCheckable : function(menuItem){
20022            var g = menuItem.group;
20023            if(g){
20024                if(!groups[g]){
20025                    groups[g] = [];
20026                }
20027                groups[g].push(menuItem);
20028                menuItem.on("beforecheckchange", onBeforeCheck);
20029            }
20030        },
20031
20032        // private
20033        unregisterCheckable : function(menuItem){
20034            var g = menuItem.group;
20035            if(g){
20036                groups[g].remove(menuItem);
20037                menuItem.un("beforecheckchange", onBeforeCheck);
20038            }
20039        }
20040    };
20041 }();/*
20042  * Based on:
20043  * Ext JS Library 1.1.1
20044  * Copyright(c) 2006-2007, Ext JS, LLC.
20045  *
20046  * Originally Released Under LGPL - original licence link has changed is not relivant.
20047  *
20048  * Fork - LGPL
20049  * <script type="text/javascript">
20050  */
20051  
20052
20053 /**
20054  * @class Roo.menu.BaseItem
20055  * @extends Roo.Component
20056  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20057  * management and base configuration options shared by all menu components.
20058  * @constructor
20059  * Creates a new BaseItem
20060  * @param {Object} config Configuration options
20061  */
20062 Roo.menu.BaseItem = function(config){
20063     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20064
20065     this.addEvents({
20066         /**
20067          * @event click
20068          * Fires when this item is clicked
20069          * @param {Roo.menu.BaseItem} this
20070          * @param {Roo.EventObject} e
20071          */
20072         click: true,
20073         /**
20074          * @event activate
20075          * Fires when this item is activated
20076          * @param {Roo.menu.BaseItem} this
20077          */
20078         activate : true,
20079         /**
20080          * @event deactivate
20081          * Fires when this item is deactivated
20082          * @param {Roo.menu.BaseItem} this
20083          */
20084         deactivate : true
20085     });
20086
20087     if(this.handler){
20088         this.on("click", this.handler, this.scope, true);
20089     }
20090 };
20091
20092 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20093     /**
20094      * @cfg {Function} handler
20095      * A function that will handle the click event of this menu item (defaults to undefined)
20096      */
20097     /**
20098      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20099      */
20100     canActivate : false,
20101     /**
20102      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20103      */
20104     activeClass : "x-menu-item-active",
20105     /**
20106      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20107      */
20108     hideOnClick : true,
20109     /**
20110      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20111      */
20112     hideDelay : 100,
20113
20114     // private
20115     ctype: "Roo.menu.BaseItem",
20116
20117     // private
20118     actionMode : "container",
20119
20120     // private
20121     render : function(container, parentMenu){
20122         this.parentMenu = parentMenu;
20123         Roo.menu.BaseItem.superclass.render.call(this, container);
20124         this.container.menuItemId = this.id;
20125     },
20126
20127     // private
20128     onRender : function(container, position){
20129         this.el = Roo.get(this.el);
20130         container.dom.appendChild(this.el.dom);
20131     },
20132
20133     // private
20134     onClick : function(e){
20135         if(!this.disabled && this.fireEvent("click", this, e) !== false
20136                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20137             this.handleClick(e);
20138         }else{
20139             e.stopEvent();
20140         }
20141     },
20142
20143     // private
20144     activate : function(){
20145         if(this.disabled){
20146             return false;
20147         }
20148         var li = this.container;
20149         li.addClass(this.activeClass);
20150         this.region = li.getRegion().adjust(2, 2, -2, -2);
20151         this.fireEvent("activate", this);
20152         return true;
20153     },
20154
20155     // private
20156     deactivate : function(){
20157         this.container.removeClass(this.activeClass);
20158         this.fireEvent("deactivate", this);
20159     },
20160
20161     // private
20162     shouldDeactivate : function(e){
20163         return !this.region || !this.region.contains(e.getPoint());
20164     },
20165
20166     // private
20167     handleClick : function(e){
20168         if(this.hideOnClick){
20169             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20170         }
20171     },
20172
20173     // private
20174     expandMenu : function(autoActivate){
20175         // do nothing
20176     },
20177
20178     // private
20179     hideMenu : function(){
20180         // do nothing
20181     }
20182 });/*
20183  * Based on:
20184  * Ext JS Library 1.1.1
20185  * Copyright(c) 2006-2007, Ext JS, LLC.
20186  *
20187  * Originally Released Under LGPL - original licence link has changed is not relivant.
20188  *
20189  * Fork - LGPL
20190  * <script type="text/javascript">
20191  */
20192  
20193 /**
20194  * @class Roo.menu.Adapter
20195  * @extends Roo.menu.BaseItem
20196  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20197  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20198  * @constructor
20199  * Creates a new Adapter
20200  * @param {Object} config Configuration options
20201  */
20202 Roo.menu.Adapter = function(component, config){
20203     Roo.menu.Adapter.superclass.constructor.call(this, config);
20204     this.component = component;
20205 };
20206 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20207     // private
20208     canActivate : true,
20209
20210     // private
20211     onRender : function(container, position){
20212         this.component.render(container);
20213         this.el = this.component.getEl();
20214     },
20215
20216     // private
20217     activate : function(){
20218         if(this.disabled){
20219             return false;
20220         }
20221         this.component.focus();
20222         this.fireEvent("activate", this);
20223         return true;
20224     },
20225
20226     // private
20227     deactivate : function(){
20228         this.fireEvent("deactivate", this);
20229     },
20230
20231     // private
20232     disable : function(){
20233         this.component.disable();
20234         Roo.menu.Adapter.superclass.disable.call(this);
20235     },
20236
20237     // private
20238     enable : function(){
20239         this.component.enable();
20240         Roo.menu.Adapter.superclass.enable.call(this);
20241     }
20242 });/*
20243  * Based on:
20244  * Ext JS Library 1.1.1
20245  * Copyright(c) 2006-2007, Ext JS, LLC.
20246  *
20247  * Originally Released Under LGPL - original licence link has changed is not relivant.
20248  *
20249  * Fork - LGPL
20250  * <script type="text/javascript">
20251  */
20252
20253 /**
20254  * @class Roo.menu.TextItem
20255  * @extends Roo.menu.BaseItem
20256  * Adds a static text string to a menu, usually used as either a heading or group separator.
20257  * Note: old style constructor with text is still supported.
20258  * 
20259  * @constructor
20260  * Creates a new TextItem
20261  * @param {Object} cfg Configuration
20262  */
20263 Roo.menu.TextItem = function(cfg){
20264     if (typeof(cfg) == 'string') {
20265         this.text = cfg;
20266     } else {
20267         Roo.apply(this,cfg);
20268     }
20269     
20270     Roo.menu.TextItem.superclass.constructor.call(this);
20271 };
20272
20273 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20274     /**
20275      * @cfg {Boolean} text Text to show on item.
20276      */
20277     text : '',
20278     
20279     /**
20280      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20281      */
20282     hideOnClick : false,
20283     /**
20284      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20285      */
20286     itemCls : "x-menu-text",
20287
20288     // private
20289     onRender : function(){
20290         var s = document.createElement("span");
20291         s.className = this.itemCls;
20292         s.innerHTML = this.text;
20293         this.el = s;
20294         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20295     }
20296 });/*
20297  * Based on:
20298  * Ext JS Library 1.1.1
20299  * Copyright(c) 2006-2007, Ext JS, LLC.
20300  *
20301  * Originally Released Under LGPL - original licence link has changed is not relivant.
20302  *
20303  * Fork - LGPL
20304  * <script type="text/javascript">
20305  */
20306
20307 /**
20308  * @class Roo.menu.Separator
20309  * @extends Roo.menu.BaseItem
20310  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20311  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20312  * @constructor
20313  * @param {Object} config Configuration options
20314  */
20315 Roo.menu.Separator = function(config){
20316     Roo.menu.Separator.superclass.constructor.call(this, config);
20317 };
20318
20319 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20320     /**
20321      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20322      */
20323     itemCls : "x-menu-sep",
20324     /**
20325      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20326      */
20327     hideOnClick : false,
20328
20329     // private
20330     onRender : function(li){
20331         var s = document.createElement("span");
20332         s.className = this.itemCls;
20333         s.innerHTML = "&#160;";
20334         this.el = s;
20335         li.addClass("x-menu-sep-li");
20336         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20337     }
20338 });/*
20339  * Based on:
20340  * Ext JS Library 1.1.1
20341  * Copyright(c) 2006-2007, Ext JS, LLC.
20342  *
20343  * Originally Released Under LGPL - original licence link has changed is not relivant.
20344  *
20345  * Fork - LGPL
20346  * <script type="text/javascript">
20347  */
20348 /**
20349  * @class Roo.menu.Item
20350  * @extends Roo.menu.BaseItem
20351  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20352  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20353  * activation and click handling.
20354  * @constructor
20355  * Creates a new Item
20356  * @param {Object} config Configuration options
20357  */
20358 Roo.menu.Item = function(config){
20359     Roo.menu.Item.superclass.constructor.call(this, config);
20360     if(this.menu){
20361         this.menu = Roo.menu.MenuMgr.get(this.menu);
20362     }
20363 };
20364 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20365     
20366     /**
20367      * @cfg {String} text
20368      * The text to show on the menu item.
20369      */
20370     text: '',
20371      /**
20372      * @cfg {String} HTML to render in menu
20373      * The text to show on the menu item (HTML version).
20374      */
20375     html: '',
20376     /**
20377      * @cfg {String} icon
20378      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20379      */
20380     icon: undefined,
20381     /**
20382      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20383      */
20384     itemCls : "x-menu-item",
20385     /**
20386      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20387      */
20388     canActivate : true,
20389     /**
20390      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20391      */
20392     showDelay: 200,
20393     // doc'd in BaseItem
20394     hideDelay: 200,
20395
20396     // private
20397     ctype: "Roo.menu.Item",
20398     
20399     // private
20400     onRender : function(container, position){
20401         var el = document.createElement("a");
20402         el.hideFocus = true;
20403         el.unselectable = "on";
20404         el.href = this.href || "#";
20405         if(this.hrefTarget){
20406             el.target = this.hrefTarget;
20407         }
20408         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20409         
20410         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20411         
20412         el.innerHTML = String.format(
20413                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20414                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20415         this.el = el;
20416         Roo.menu.Item.superclass.onRender.call(this, container, position);
20417     },
20418
20419     /**
20420      * Sets the text to display in this menu item
20421      * @param {String} text The text to display
20422      * @param {Boolean} isHTML true to indicate text is pure html.
20423      */
20424     setText : function(text, isHTML){
20425         if (isHTML) {
20426             this.html = text;
20427         } else {
20428             this.text = text;
20429             this.html = '';
20430         }
20431         if(this.rendered){
20432             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20433      
20434             this.el.update(String.format(
20435                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20436                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20437             this.parentMenu.autoWidth();
20438         }
20439     },
20440
20441     // private
20442     handleClick : function(e){
20443         if(!this.href){ // if no link defined, stop the event automatically
20444             e.stopEvent();
20445         }
20446         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20447     },
20448
20449     // private
20450     activate : function(autoExpand){
20451         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20452             this.focus();
20453             if(autoExpand){
20454                 this.expandMenu();
20455             }
20456         }
20457         return true;
20458     },
20459
20460     // private
20461     shouldDeactivate : function(e){
20462         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20463             if(this.menu && this.menu.isVisible()){
20464                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20465             }
20466             return true;
20467         }
20468         return false;
20469     },
20470
20471     // private
20472     deactivate : function(){
20473         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20474         this.hideMenu();
20475     },
20476
20477     // private
20478     expandMenu : function(autoActivate){
20479         if(!this.disabled && this.menu){
20480             clearTimeout(this.hideTimer);
20481             delete this.hideTimer;
20482             if(!this.menu.isVisible() && !this.showTimer){
20483                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20484             }else if (this.menu.isVisible() && autoActivate){
20485                 this.menu.tryActivate(0, 1);
20486             }
20487         }
20488     },
20489
20490     // private
20491     deferExpand : function(autoActivate){
20492         delete this.showTimer;
20493         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20494         if(autoActivate){
20495             this.menu.tryActivate(0, 1);
20496         }
20497     },
20498
20499     // private
20500     hideMenu : function(){
20501         clearTimeout(this.showTimer);
20502         delete this.showTimer;
20503         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20504             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20505         }
20506     },
20507
20508     // private
20509     deferHide : function(){
20510         delete this.hideTimer;
20511         this.menu.hide();
20512     }
20513 });/*
20514  * Based on:
20515  * Ext JS Library 1.1.1
20516  * Copyright(c) 2006-2007, Ext JS, LLC.
20517  *
20518  * Originally Released Under LGPL - original licence link has changed is not relivant.
20519  *
20520  * Fork - LGPL
20521  * <script type="text/javascript">
20522  */
20523  
20524 /**
20525  * @class Roo.menu.CheckItem
20526  * @extends Roo.menu.Item
20527  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20528  * @constructor
20529  * Creates a new CheckItem
20530  * @param {Object} config Configuration options
20531  */
20532 Roo.menu.CheckItem = function(config){
20533     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20534     this.addEvents({
20535         /**
20536          * @event beforecheckchange
20537          * Fires before the checked value is set, providing an opportunity to cancel if needed
20538          * @param {Roo.menu.CheckItem} this
20539          * @param {Boolean} checked The new checked value that will be set
20540          */
20541         "beforecheckchange" : true,
20542         /**
20543          * @event checkchange
20544          * Fires after the checked value has been set
20545          * @param {Roo.menu.CheckItem} this
20546          * @param {Boolean} checked The checked value that was set
20547          */
20548         "checkchange" : true
20549     });
20550     if(this.checkHandler){
20551         this.on('checkchange', this.checkHandler, this.scope);
20552     }
20553 };
20554 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20555     /**
20556      * @cfg {String} group
20557      * All check items with the same group name will automatically be grouped into a single-select
20558      * radio button group (defaults to '')
20559      */
20560     /**
20561      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20562      */
20563     itemCls : "x-menu-item x-menu-check-item",
20564     /**
20565      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20566      */
20567     groupClass : "x-menu-group-item",
20568
20569     /**
20570      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20571      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20572      * initialized with checked = true will be rendered as checked.
20573      */
20574     checked: false,
20575
20576     // private
20577     ctype: "Roo.menu.CheckItem",
20578
20579     // private
20580     onRender : function(c){
20581         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20582         if(this.group){
20583             this.el.addClass(this.groupClass);
20584         }
20585         Roo.menu.MenuMgr.registerCheckable(this);
20586         if(this.checked){
20587             this.checked = false;
20588             this.setChecked(true, true);
20589         }
20590     },
20591
20592     // private
20593     destroy : function(){
20594         if(this.rendered){
20595             Roo.menu.MenuMgr.unregisterCheckable(this);
20596         }
20597         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20598     },
20599
20600     /**
20601      * Set the checked state of this item
20602      * @param {Boolean} checked The new checked value
20603      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20604      */
20605     setChecked : function(state, suppressEvent){
20606         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20607             if(this.container){
20608                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20609             }
20610             this.checked = state;
20611             if(suppressEvent !== true){
20612                 this.fireEvent("checkchange", this, state);
20613             }
20614         }
20615     },
20616
20617     // private
20618     handleClick : function(e){
20619        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20620            this.setChecked(!this.checked);
20621        }
20622        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20623     }
20624 });/*
20625  * Based on:
20626  * Ext JS Library 1.1.1
20627  * Copyright(c) 2006-2007, Ext JS, LLC.
20628  *
20629  * Originally Released Under LGPL - original licence link has changed is not relivant.
20630  *
20631  * Fork - LGPL
20632  * <script type="text/javascript">
20633  */
20634  
20635 /**
20636  * @class Roo.menu.DateItem
20637  * @extends Roo.menu.Adapter
20638  * A menu item that wraps the {@link Roo.DatPicker} component.
20639  * @constructor
20640  * Creates a new DateItem
20641  * @param {Object} config Configuration options
20642  */
20643 Roo.menu.DateItem = function(config){
20644     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20645     /** The Roo.DatePicker object @type Roo.DatePicker */
20646     this.picker = this.component;
20647     this.addEvents({select: true});
20648     
20649     this.picker.on("render", function(picker){
20650         picker.getEl().swallowEvent("click");
20651         picker.container.addClass("x-menu-date-item");
20652     });
20653
20654     this.picker.on("select", this.onSelect, this);
20655 };
20656
20657 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20658     // private
20659     onSelect : function(picker, date){
20660         this.fireEvent("select", this, date, picker);
20661         Roo.menu.DateItem.superclass.handleClick.call(this);
20662     }
20663 });/*
20664  * Based on:
20665  * Ext JS Library 1.1.1
20666  * Copyright(c) 2006-2007, Ext JS, LLC.
20667  *
20668  * Originally Released Under LGPL - original licence link has changed is not relivant.
20669  *
20670  * Fork - LGPL
20671  * <script type="text/javascript">
20672  */
20673  
20674 /**
20675  * @class Roo.menu.ColorItem
20676  * @extends Roo.menu.Adapter
20677  * A menu item that wraps the {@link Roo.ColorPalette} component.
20678  * @constructor
20679  * Creates a new ColorItem
20680  * @param {Object} config Configuration options
20681  */
20682 Roo.menu.ColorItem = function(config){
20683     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20684     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20685     this.palette = this.component;
20686     this.relayEvents(this.palette, ["select"]);
20687     if(this.selectHandler){
20688         this.on('select', this.selectHandler, this.scope);
20689     }
20690 };
20691 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20692  * Based on:
20693  * Ext JS Library 1.1.1
20694  * Copyright(c) 2006-2007, Ext JS, LLC.
20695  *
20696  * Originally Released Under LGPL - original licence link has changed is not relivant.
20697  *
20698  * Fork - LGPL
20699  * <script type="text/javascript">
20700  */
20701  
20702
20703 /**
20704  * @class Roo.menu.DateMenu
20705  * @extends Roo.menu.Menu
20706  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20707  * @constructor
20708  * Creates a new DateMenu
20709  * @param {Object} config Configuration options
20710  */
20711 Roo.menu.DateMenu = function(config){
20712     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20713     this.plain = true;
20714     var di = new Roo.menu.DateItem(config);
20715     this.add(di);
20716     /**
20717      * The {@link Roo.DatePicker} instance for this DateMenu
20718      * @type DatePicker
20719      */
20720     this.picker = di.picker;
20721     /**
20722      * @event select
20723      * @param {DatePicker} picker
20724      * @param {Date} date
20725      */
20726     this.relayEvents(di, ["select"]);
20727
20728     this.on('beforeshow', function(){
20729         if(this.picker){
20730             this.picker.hideMonthPicker(true);
20731         }
20732     }, this);
20733 };
20734 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20735     cls:'x-date-menu'
20736 });/*
20737  * Based on:
20738  * Ext JS Library 1.1.1
20739  * Copyright(c) 2006-2007, Ext JS, LLC.
20740  *
20741  * Originally Released Under LGPL - original licence link has changed is not relivant.
20742  *
20743  * Fork - LGPL
20744  * <script type="text/javascript">
20745  */
20746  
20747
20748 /**
20749  * @class Roo.menu.ColorMenu
20750  * @extends Roo.menu.Menu
20751  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20752  * @constructor
20753  * Creates a new ColorMenu
20754  * @param {Object} config Configuration options
20755  */
20756 Roo.menu.ColorMenu = function(config){
20757     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20758     this.plain = true;
20759     var ci = new Roo.menu.ColorItem(config);
20760     this.add(ci);
20761     /**
20762      * The {@link Roo.ColorPalette} instance for this ColorMenu
20763      * @type ColorPalette
20764      */
20765     this.palette = ci.palette;
20766     /**
20767      * @event select
20768      * @param {ColorPalette} palette
20769      * @param {String} color
20770      */
20771     this.relayEvents(ci, ["select"]);
20772 };
20773 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20774  * Based on:
20775  * Ext JS Library 1.1.1
20776  * Copyright(c) 2006-2007, Ext JS, LLC.
20777  *
20778  * Originally Released Under LGPL - original licence link has changed is not relivant.
20779  *
20780  * Fork - LGPL
20781  * <script type="text/javascript">
20782  */
20783  
20784 /**
20785  * @class Roo.form.Field
20786  * @extends Roo.BoxComponent
20787  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20788  * @constructor
20789  * Creates a new Field
20790  * @param {Object} config Configuration options
20791  */
20792 Roo.form.Field = function(config){
20793     Roo.form.Field.superclass.constructor.call(this, config);
20794 };
20795
20796 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20797     /**
20798      * @cfg {String} fieldLabel Label to use when rendering a form.
20799      */
20800        /**
20801      * @cfg {String} qtip Mouse over tip
20802      */
20803      
20804     /**
20805      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20806      */
20807     invalidClass : "x-form-invalid",
20808     /**
20809      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20810      */
20811     invalidText : "The value in this field is invalid",
20812     /**
20813      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20814      */
20815     focusClass : "x-form-focus",
20816     /**
20817      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20818       automatic validation (defaults to "keyup").
20819      */
20820     validationEvent : "keyup",
20821     /**
20822      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20823      */
20824     validateOnBlur : true,
20825     /**
20826      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20827      */
20828     validationDelay : 250,
20829     /**
20830      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20831      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20832      */
20833     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20834     /**
20835      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20836      */
20837     fieldClass : "x-form-field",
20838     /**
20839      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20840      *<pre>
20841 Value         Description
20842 -----------   ----------------------------------------------------------------------
20843 qtip          Display a quick tip when the user hovers over the field
20844 title         Display a default browser title attribute popup
20845 under         Add a block div beneath the field containing the error text
20846 side          Add an error icon to the right of the field with a popup on hover
20847 [element id]  Add the error text directly to the innerHTML of the specified element
20848 </pre>
20849      */
20850     msgTarget : 'qtip',
20851     /**
20852      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20853      */
20854     msgFx : 'normal',
20855
20856     /**
20857      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20858      */
20859     readOnly : false,
20860
20861     /**
20862      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20863      */
20864     disabled : false,
20865
20866     /**
20867      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20868      */
20869     inputType : undefined,
20870     
20871     /**
20872      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20873          */
20874         tabIndex : undefined,
20875         
20876     // private
20877     isFormField : true,
20878
20879     // private
20880     hasFocus : false,
20881     /**
20882      * @property {Roo.Element} fieldEl
20883      * Element Containing the rendered Field (with label etc.)
20884      */
20885     /**
20886      * @cfg {Mixed} value A value to initialize this field with.
20887      */
20888     value : undefined,
20889
20890     /**
20891      * @cfg {String} name The field's HTML name attribute.
20892      */
20893     /**
20894      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20895      */
20896
20897         // private ??
20898         initComponent : function(){
20899         Roo.form.Field.superclass.initComponent.call(this);
20900         this.addEvents({
20901             /**
20902              * @event focus
20903              * Fires when this field receives input focus.
20904              * @param {Roo.form.Field} this
20905              */
20906             focus : true,
20907             /**
20908              * @event blur
20909              * Fires when this field loses input focus.
20910              * @param {Roo.form.Field} this
20911              */
20912             blur : true,
20913             /**
20914              * @event specialkey
20915              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20916              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20917              * @param {Roo.form.Field} this
20918              * @param {Roo.EventObject} e The event object
20919              */
20920             specialkey : true,
20921             /**
20922              * @event change
20923              * Fires just before the field blurs if the field value has changed.
20924              * @param {Roo.form.Field} this
20925              * @param {Mixed} newValue The new value
20926              * @param {Mixed} oldValue The original value
20927              */
20928             change : true,
20929             /**
20930              * @event invalid
20931              * Fires after the field has been marked as invalid.
20932              * @param {Roo.form.Field} this
20933              * @param {String} msg The validation message
20934              */
20935             invalid : true,
20936             /**
20937              * @event valid
20938              * Fires after the field has been validated with no errors.
20939              * @param {Roo.form.Field} this
20940              */
20941             valid : true,
20942              /**
20943              * @event keyup
20944              * Fires after the key up
20945              * @param {Roo.form.Field} this
20946              * @param {Roo.EventObject}  e The event Object
20947              */
20948             keyup : true
20949         });
20950     },
20951
20952     /**
20953      * Returns the name attribute of the field if available
20954      * @return {String} name The field name
20955      */
20956     getName: function(){
20957          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20958     },
20959
20960     // private
20961     onRender : function(ct, position){
20962         Roo.form.Field.superclass.onRender.call(this, ct, position);
20963         if(!this.el){
20964             var cfg = this.getAutoCreate();
20965             if(!cfg.name){
20966                 cfg.name = this.name || this.id;
20967             }
20968             if(this.inputType){
20969                 cfg.type = this.inputType;
20970             }
20971             this.el = ct.createChild(cfg, position);
20972         }
20973         var type = this.el.dom.type;
20974         if(type){
20975             if(type == 'password'){
20976                 type = 'text';
20977             }
20978             this.el.addClass('x-form-'+type);
20979         }
20980         if(this.readOnly){
20981             this.el.dom.readOnly = true;
20982         }
20983         if(this.tabIndex !== undefined){
20984             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20985         }
20986
20987         this.el.addClass([this.fieldClass, this.cls]);
20988         this.initValue();
20989     },
20990
20991     /**
20992      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20993      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20994      * @return {Roo.form.Field} this
20995      */
20996     applyTo : function(target){
20997         this.allowDomMove = false;
20998         this.el = Roo.get(target);
20999         this.render(this.el.dom.parentNode);
21000         return this;
21001     },
21002
21003     // private
21004     initValue : function(){
21005         if(this.value !== undefined){
21006             this.setValue(this.value);
21007         }else if(this.el.dom.value.length > 0){
21008             this.setValue(this.el.dom.value);
21009         }
21010     },
21011
21012     /**
21013      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21014      */
21015     isDirty : function() {
21016         if(this.disabled) {
21017             return false;
21018         }
21019         return String(this.getValue()) !== String(this.originalValue);
21020     },
21021
21022     // private
21023     afterRender : function(){
21024         Roo.form.Field.superclass.afterRender.call(this);
21025         this.initEvents();
21026     },
21027
21028     // private
21029     fireKey : function(e){
21030         //Roo.log('field ' + e.getKey());
21031         if(e.isNavKeyPress()){
21032             this.fireEvent("specialkey", this, e);
21033         }
21034     },
21035
21036     /**
21037      * Resets the current field value to the originally loaded value and clears any validation messages
21038      */
21039     reset : function(){
21040         this.setValue(this.originalValue);
21041         this.clearInvalid();
21042     },
21043
21044     // private
21045     initEvents : function(){
21046         // safari killled keypress - so keydown is now used..
21047         this.el.on("keydown" , this.fireKey,  this);
21048         this.el.on("focus", this.onFocus,  this);
21049         this.el.on("blur", this.onBlur,  this);
21050         this.el.relayEvent('keyup', this);
21051
21052         // reference to original value for reset
21053         this.originalValue = this.getValue();
21054     },
21055
21056     // private
21057     onFocus : function(){
21058         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21059             this.el.addClass(this.focusClass);
21060         }
21061         if(!this.hasFocus){
21062             this.hasFocus = true;
21063             this.startValue = this.getValue();
21064             this.fireEvent("focus", this);
21065         }
21066     },
21067
21068     beforeBlur : Roo.emptyFn,
21069
21070     // private
21071     onBlur : function(){
21072         this.beforeBlur();
21073         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21074             this.el.removeClass(this.focusClass);
21075         }
21076         this.hasFocus = false;
21077         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21078             this.validate();
21079         }
21080         var v = this.getValue();
21081         if(String(v) !== String(this.startValue)){
21082             this.fireEvent('change', this, v, this.startValue);
21083         }
21084         this.fireEvent("blur", this);
21085     },
21086
21087     /**
21088      * Returns whether or not the field value is currently valid
21089      * @param {Boolean} preventMark True to disable marking the field invalid
21090      * @return {Boolean} True if the value is valid, else false
21091      */
21092     isValid : function(preventMark){
21093         if(this.disabled){
21094             return true;
21095         }
21096         var restore = this.preventMark;
21097         this.preventMark = preventMark === true;
21098         var v = this.validateValue(this.processValue(this.getRawValue()));
21099         this.preventMark = restore;
21100         return v;
21101     },
21102
21103     /**
21104      * Validates the field value
21105      * @return {Boolean} True if the value is valid, else false
21106      */
21107     validate : function(){
21108         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21109             this.clearInvalid();
21110             return true;
21111         }
21112         return false;
21113     },
21114
21115     processValue : function(value){
21116         return value;
21117     },
21118
21119     // private
21120     // Subclasses should provide the validation implementation by overriding this
21121     validateValue : function(value){
21122         return true;
21123     },
21124
21125     /**
21126      * Mark this field as invalid
21127      * @param {String} msg The validation message
21128      */
21129     markInvalid : function(msg){
21130         if(!this.rendered || this.preventMark){ // not rendered
21131             return;
21132         }
21133         this.el.addClass(this.invalidClass);
21134         msg = msg || this.invalidText;
21135         switch(this.msgTarget){
21136             case 'qtip':
21137                 this.el.dom.qtip = msg;
21138                 this.el.dom.qclass = 'x-form-invalid-tip';
21139                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21140                     Roo.QuickTips.enable();
21141                 }
21142                 break;
21143             case 'title':
21144                 this.el.dom.title = msg;
21145                 break;
21146             case 'under':
21147                 if(!this.errorEl){
21148                     var elp = this.el.findParent('.x-form-element', 5, true);
21149                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21150                     this.errorEl.setWidth(elp.getWidth(true)-20);
21151                 }
21152                 this.errorEl.update(msg);
21153                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21154                 break;
21155             case 'side':
21156                 if(!this.errorIcon){
21157                     var elp = this.el.findParent('.x-form-element', 5, true);
21158                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21159                 }
21160                 this.alignErrorIcon();
21161                 this.errorIcon.dom.qtip = msg;
21162                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21163                 this.errorIcon.show();
21164                 this.on('resize', this.alignErrorIcon, this);
21165                 break;
21166             default:
21167                 var t = Roo.getDom(this.msgTarget);
21168                 t.innerHTML = msg;
21169                 t.style.display = this.msgDisplay;
21170                 break;
21171         }
21172         this.fireEvent('invalid', this, msg);
21173     },
21174
21175     // private
21176     alignErrorIcon : function(){
21177         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21178     },
21179
21180     /**
21181      * Clear any invalid styles/messages for this field
21182      */
21183     clearInvalid : function(){
21184         if(!this.rendered || this.preventMark){ // not rendered
21185             return;
21186         }
21187         this.el.removeClass(this.invalidClass);
21188         switch(this.msgTarget){
21189             case 'qtip':
21190                 this.el.dom.qtip = '';
21191                 break;
21192             case 'title':
21193                 this.el.dom.title = '';
21194                 break;
21195             case 'under':
21196                 if(this.errorEl){
21197                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21198                 }
21199                 break;
21200             case 'side':
21201                 if(this.errorIcon){
21202                     this.errorIcon.dom.qtip = '';
21203                     this.errorIcon.hide();
21204                     this.un('resize', this.alignErrorIcon, this);
21205                 }
21206                 break;
21207             default:
21208                 var t = Roo.getDom(this.msgTarget);
21209                 t.innerHTML = '';
21210                 t.style.display = 'none';
21211                 break;
21212         }
21213         this.fireEvent('valid', this);
21214     },
21215
21216     /**
21217      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21218      * @return {Mixed} value The field value
21219      */
21220     getRawValue : function(){
21221         var v = this.el.getValue();
21222         if(v === this.emptyText){
21223             v = '';
21224         }
21225         return v;
21226     },
21227
21228     /**
21229      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21230      * @return {Mixed} value The field value
21231      */
21232     getValue : function(){
21233         var v = this.el.getValue();
21234         if(v === this.emptyText || v === undefined){
21235             v = '';
21236         }
21237         return v;
21238     },
21239
21240     /**
21241      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21242      * @param {Mixed} value The value to set
21243      */
21244     setRawValue : function(v){
21245         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21246     },
21247
21248     /**
21249      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21250      * @param {Mixed} value The value to set
21251      */
21252     setValue : function(v){
21253         this.value = v;
21254         if(this.rendered){
21255             this.el.dom.value = (v === null || v === undefined ? '' : v);
21256             this.validate();
21257         }
21258     },
21259
21260     adjustSize : function(w, h){
21261         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21262         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21263         return s;
21264     },
21265
21266     adjustWidth : function(tag, w){
21267         tag = tag.toLowerCase();
21268         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21269             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21270                 if(tag == 'input'){
21271                     return w + 2;
21272                 }
21273                 if(tag = 'textarea'){
21274                     return w-2;
21275                 }
21276             }else if(Roo.isOpera){
21277                 if(tag == 'input'){
21278                     return w + 2;
21279                 }
21280                 if(tag = 'textarea'){
21281                     return w-2;
21282                 }
21283             }
21284         }
21285         return w;
21286     }
21287 });
21288
21289
21290 // anything other than normal should be considered experimental
21291 Roo.form.Field.msgFx = {
21292     normal : {
21293         show: function(msgEl, f){
21294             msgEl.setDisplayed('block');
21295         },
21296
21297         hide : function(msgEl, f){
21298             msgEl.setDisplayed(false).update('');
21299         }
21300     },
21301
21302     slide : {
21303         show: function(msgEl, f){
21304             msgEl.slideIn('t', {stopFx:true});
21305         },
21306
21307         hide : function(msgEl, f){
21308             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21309         }
21310     },
21311
21312     slideRight : {
21313         show: function(msgEl, f){
21314             msgEl.fixDisplay();
21315             msgEl.alignTo(f.el, 'tl-tr');
21316             msgEl.slideIn('l', {stopFx:true});
21317         },
21318
21319         hide : function(msgEl, f){
21320             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21321         }
21322     }
21323 };/*
21324  * Based on:
21325  * Ext JS Library 1.1.1
21326  * Copyright(c) 2006-2007, Ext JS, LLC.
21327  *
21328  * Originally Released Under LGPL - original licence link has changed is not relivant.
21329  *
21330  * Fork - LGPL
21331  * <script type="text/javascript">
21332  */
21333  
21334
21335 /**
21336  * @class Roo.form.TextField
21337  * @extends Roo.form.Field
21338  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21339  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21340  * @constructor
21341  * Creates a new TextField
21342  * @param {Object} config Configuration options
21343  */
21344 Roo.form.TextField = function(config){
21345     Roo.form.TextField.superclass.constructor.call(this, config);
21346     this.addEvents({
21347         /**
21348          * @event autosize
21349          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21350          * according to the default logic, but this event provides a hook for the developer to apply additional
21351          * logic at runtime to resize the field if needed.
21352              * @param {Roo.form.Field} this This text field
21353              * @param {Number} width The new field width
21354              */
21355         autosize : true
21356     });
21357 };
21358
21359 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21360     /**
21361      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21362      */
21363     grow : false,
21364     /**
21365      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21366      */
21367     growMin : 30,
21368     /**
21369      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21370      */
21371     growMax : 800,
21372     /**
21373      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21374      */
21375     vtype : null,
21376     /**
21377      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21378      */
21379     maskRe : null,
21380     /**
21381      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21382      */
21383     disableKeyFilter : false,
21384     /**
21385      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21386      */
21387     allowBlank : true,
21388     /**
21389      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21390      */
21391     minLength : 0,
21392     /**
21393      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21394      */
21395     maxLength : Number.MAX_VALUE,
21396     /**
21397      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21398      */
21399     minLengthText : "The minimum length for this field is {0}",
21400     /**
21401      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21402      */
21403     maxLengthText : "The maximum length for this field is {0}",
21404     /**
21405      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21406      */
21407     selectOnFocus : false,
21408     /**
21409      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21410      */
21411     blankText : "This field is required",
21412     /**
21413      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21414      * If available, this function will be called only after the basic validators all return true, and will be passed the
21415      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21416      */
21417     validator : null,
21418     /**
21419      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21420      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21421      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21422      */
21423     regex : null,
21424     /**
21425      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21426      */
21427     regexText : "",
21428     /**
21429      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21430      */
21431     emptyText : null,
21432     /**
21433      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21434      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21435      */
21436     emptyClass : 'x-form-empty-field',
21437
21438     // private
21439     initEvents : function(){
21440         Roo.form.TextField.superclass.initEvents.call(this);
21441         if(this.validationEvent == 'keyup'){
21442             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21443             this.el.on('keyup', this.filterValidation, this);
21444         }
21445         else if(this.validationEvent !== false){
21446             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21447         }
21448         if(this.selectOnFocus || this.emptyText){
21449             this.on("focus", this.preFocus, this);
21450             if(this.emptyText){
21451                 this.on('blur', this.postBlur, this);
21452                 this.applyEmptyText();
21453             }
21454         }
21455         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21456             this.el.on("keypress", this.filterKeys, this);
21457         }
21458         if(this.grow){
21459             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21460             this.el.on("click", this.autoSize,  this);
21461         }
21462     },
21463
21464     processValue : function(value){
21465         if(this.stripCharsRe){
21466             var newValue = value.replace(this.stripCharsRe, '');
21467             if(newValue !== value){
21468                 this.setRawValue(newValue);
21469                 return newValue;
21470             }
21471         }
21472         return value;
21473     },
21474
21475     filterValidation : function(e){
21476         if(!e.isNavKeyPress()){
21477             this.validationTask.delay(this.validationDelay);
21478         }
21479     },
21480
21481     // private
21482     onKeyUp : function(e){
21483         if(!e.isNavKeyPress()){
21484             this.autoSize();
21485         }
21486     },
21487
21488     /**
21489      * Resets the current field value to the originally-loaded value and clears any validation messages.
21490      * Also adds emptyText and emptyClass if the original value was blank.
21491      */
21492     reset : function(){
21493         Roo.form.TextField.superclass.reset.call(this);
21494         this.applyEmptyText();
21495     },
21496
21497     applyEmptyText : function(){
21498         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21499             this.setRawValue(this.emptyText);
21500             this.el.addClass(this.emptyClass);
21501         }
21502     },
21503
21504     // private
21505     preFocus : function(){
21506         if(this.emptyText){
21507             if(this.el.dom.value == this.emptyText){
21508                 this.setRawValue('');
21509             }
21510             this.el.removeClass(this.emptyClass);
21511         }
21512         if(this.selectOnFocus){
21513             this.el.dom.select();
21514         }
21515     },
21516
21517     // private
21518     postBlur : function(){
21519         this.applyEmptyText();
21520     },
21521
21522     // private
21523     filterKeys : function(e){
21524         var k = e.getKey();
21525         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21526             return;
21527         }
21528         var c = e.getCharCode(), cc = String.fromCharCode(c);
21529         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21530             return;
21531         }
21532         if(!this.maskRe.test(cc)){
21533             e.stopEvent();
21534         }
21535     },
21536
21537     setValue : function(v){
21538         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21539             this.el.removeClass(this.emptyClass);
21540         }
21541         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21542         this.applyEmptyText();
21543         this.autoSize();
21544     },
21545
21546     /**
21547      * Validates a value according to the field's validation rules and marks the field as invalid
21548      * if the validation fails
21549      * @param {Mixed} value The value to validate
21550      * @return {Boolean} True if the value is valid, else false
21551      */
21552     validateValue : function(value){
21553         if(value.length < 1 || value === this.emptyText){ // if it's blank
21554              if(this.allowBlank){
21555                 this.clearInvalid();
21556                 return true;
21557              }else{
21558                 this.markInvalid(this.blankText);
21559                 return false;
21560              }
21561         }
21562         if(value.length < this.minLength){
21563             this.markInvalid(String.format(this.minLengthText, this.minLength));
21564             return false;
21565         }
21566         if(value.length > this.maxLength){
21567             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21568             return false;
21569         }
21570         if(this.vtype){
21571             var vt = Roo.form.VTypes;
21572             if(!vt[this.vtype](value, this)){
21573                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21574                 return false;
21575             }
21576         }
21577         if(typeof this.validator == "function"){
21578             var msg = this.validator(value);
21579             if(msg !== true){
21580                 this.markInvalid(msg);
21581                 return false;
21582             }
21583         }
21584         if(this.regex && !this.regex.test(value)){
21585             this.markInvalid(this.regexText);
21586             return false;
21587         }
21588         return true;
21589     },
21590
21591     /**
21592      * Selects text in this field
21593      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21594      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21595      */
21596     selectText : function(start, end){
21597         var v = this.getRawValue();
21598         if(v.length > 0){
21599             start = start === undefined ? 0 : start;
21600             end = end === undefined ? v.length : end;
21601             var d = this.el.dom;
21602             if(d.setSelectionRange){
21603                 d.setSelectionRange(start, end);
21604             }else if(d.createTextRange){
21605                 var range = d.createTextRange();
21606                 range.moveStart("character", start);
21607                 range.moveEnd("character", v.length-end);
21608                 range.select();
21609             }
21610         }
21611     },
21612
21613     /**
21614      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21615      * This only takes effect if grow = true, and fires the autosize event.
21616      */
21617     autoSize : function(){
21618         if(!this.grow || !this.rendered){
21619             return;
21620         }
21621         if(!this.metrics){
21622             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21623         }
21624         var el = this.el;
21625         var v = el.dom.value;
21626         var d = document.createElement('div');
21627         d.appendChild(document.createTextNode(v));
21628         v = d.innerHTML;
21629         d = null;
21630         v += "&#160;";
21631         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21632         this.el.setWidth(w);
21633         this.fireEvent("autosize", this, w);
21634     }
21635 });/*
21636  * Based on:
21637  * Ext JS Library 1.1.1
21638  * Copyright(c) 2006-2007, Ext JS, LLC.
21639  *
21640  * Originally Released Under LGPL - original licence link has changed is not relivant.
21641  *
21642  * Fork - LGPL
21643  * <script type="text/javascript">
21644  */
21645  
21646 /**
21647  * @class Roo.form.Hidden
21648  * @extends Roo.form.TextField
21649  * Simple Hidden element used on forms 
21650  * 
21651  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21652  * 
21653  * @constructor
21654  * Creates a new Hidden form element.
21655  * @param {Object} config Configuration options
21656  */
21657
21658
21659
21660 // easy hidden field...
21661 Roo.form.Hidden = function(config){
21662     Roo.form.Hidden.superclass.constructor.call(this, config);
21663 };
21664   
21665 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21666     fieldLabel:      '',
21667     inputType:      'hidden',
21668     width:          50,
21669     allowBlank:     true,
21670     labelSeparator: '',
21671     hidden:         true,
21672     itemCls :       'x-form-item-display-none'
21673
21674
21675 });
21676
21677
21678 /*
21679  * Based on:
21680  * Ext JS Library 1.1.1
21681  * Copyright(c) 2006-2007, Ext JS, LLC.
21682  *
21683  * Originally Released Under LGPL - original licence link has changed is not relivant.
21684  *
21685  * Fork - LGPL
21686  * <script type="text/javascript">
21687  */
21688  
21689 /**
21690  * @class Roo.form.TriggerField
21691  * @extends Roo.form.TextField
21692  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21693  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21694  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21695  * for which you can provide a custom implementation.  For example:
21696  * <pre><code>
21697 var trigger = new Roo.form.TriggerField();
21698 trigger.onTriggerClick = myTriggerFn;
21699 trigger.applyTo('my-field');
21700 </code></pre>
21701  *
21702  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21703  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21704  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21705  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21706  * @constructor
21707  * Create a new TriggerField.
21708  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21709  * to the base TextField)
21710  */
21711 Roo.form.TriggerField = function(config){
21712     this.mimicing = false;
21713     Roo.form.TriggerField.superclass.constructor.call(this, config);
21714 };
21715
21716 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21717     /**
21718      * @cfg {String} triggerClass A CSS class to apply to the trigger
21719      */
21720     /**
21721      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21722      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21723      */
21724     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21725     /**
21726      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21727      */
21728     hideTrigger:false,
21729
21730     /** @cfg {Boolean} grow @hide */
21731     /** @cfg {Number} growMin @hide */
21732     /** @cfg {Number} growMax @hide */
21733
21734     /**
21735      * @hide 
21736      * @method
21737      */
21738     autoSize: Roo.emptyFn,
21739     // private
21740     monitorTab : true,
21741     // private
21742     deferHeight : true,
21743
21744     
21745     actionMode : 'wrap',
21746     // private
21747     onResize : function(w, h){
21748         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21749         if(typeof w == 'number'){
21750             var x = w - this.trigger.getWidth();
21751             this.el.setWidth(this.adjustWidth('input', x));
21752             this.trigger.setStyle('left', x+'px');
21753         }
21754     },
21755
21756     // private
21757     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21758
21759     // private
21760     getResizeEl : function(){
21761         return this.wrap;
21762     },
21763
21764     // private
21765     getPositionEl : function(){
21766         return this.wrap;
21767     },
21768
21769     // private
21770     alignErrorIcon : function(){
21771         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21772     },
21773
21774     // private
21775     onRender : function(ct, position){
21776         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21777         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21778         this.trigger = this.wrap.createChild(this.triggerConfig ||
21779                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21780         if(this.hideTrigger){
21781             this.trigger.setDisplayed(false);
21782         }
21783         this.initTrigger();
21784         if(!this.width){
21785             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21786         }
21787     },
21788
21789     // private
21790     initTrigger : function(){
21791         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21792         this.trigger.addClassOnOver('x-form-trigger-over');
21793         this.trigger.addClassOnClick('x-form-trigger-click');
21794     },
21795
21796     // private
21797     onDestroy : function(){
21798         if(this.trigger){
21799             this.trigger.removeAllListeners();
21800             this.trigger.remove();
21801         }
21802         if(this.wrap){
21803             this.wrap.remove();
21804         }
21805         Roo.form.TriggerField.superclass.onDestroy.call(this);
21806     },
21807
21808     // private
21809     onFocus : function(){
21810         Roo.form.TriggerField.superclass.onFocus.call(this);
21811         if(!this.mimicing){
21812             this.wrap.addClass('x-trigger-wrap-focus');
21813             this.mimicing = true;
21814             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21815             if(this.monitorTab){
21816                 this.el.on("keydown", this.checkTab, this);
21817             }
21818         }
21819     },
21820
21821     // private
21822     checkTab : function(e){
21823         if(e.getKey() == e.TAB){
21824             this.triggerBlur();
21825         }
21826     },
21827
21828     // private
21829     onBlur : function(){
21830         // do nothing
21831     },
21832
21833     // private
21834     mimicBlur : function(e, t){
21835         if(!this.wrap.contains(t) && this.validateBlur()){
21836             this.triggerBlur();
21837         }
21838     },
21839
21840     // private
21841     triggerBlur : function(){
21842         this.mimicing = false;
21843         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21844         if(this.monitorTab){
21845             this.el.un("keydown", this.checkTab, this);
21846         }
21847         this.wrap.removeClass('x-trigger-wrap-focus');
21848         Roo.form.TriggerField.superclass.onBlur.call(this);
21849     },
21850
21851     // private
21852     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21853     validateBlur : function(e, t){
21854         return true;
21855     },
21856
21857     // private
21858     onDisable : function(){
21859         Roo.form.TriggerField.superclass.onDisable.call(this);
21860         if(this.wrap){
21861             this.wrap.addClass('x-item-disabled');
21862         }
21863     },
21864
21865     // private
21866     onEnable : function(){
21867         Roo.form.TriggerField.superclass.onEnable.call(this);
21868         if(this.wrap){
21869             this.wrap.removeClass('x-item-disabled');
21870         }
21871     },
21872
21873     // private
21874     onShow : function(){
21875         var ae = this.getActionEl();
21876         
21877         if(ae){
21878             ae.dom.style.display = '';
21879             ae.dom.style.visibility = 'visible';
21880         }
21881     },
21882
21883     // private
21884     
21885     onHide : function(){
21886         var ae = this.getActionEl();
21887         ae.dom.style.display = 'none';
21888     },
21889
21890     /**
21891      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21892      * by an implementing function.
21893      * @method
21894      * @param {EventObject} e
21895      */
21896     onTriggerClick : Roo.emptyFn
21897 });
21898
21899 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21900 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21901 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21902 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21903     initComponent : function(){
21904         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21905
21906         this.triggerConfig = {
21907             tag:'span', cls:'x-form-twin-triggers', cn:[
21908             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21909             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21910         ]};
21911     },
21912
21913     getTrigger : function(index){
21914         return this.triggers[index];
21915     },
21916
21917     initTrigger : function(){
21918         var ts = this.trigger.select('.x-form-trigger', true);
21919         this.wrap.setStyle('overflow', 'hidden');
21920         var triggerField = this;
21921         ts.each(function(t, all, index){
21922             t.hide = function(){
21923                 var w = triggerField.wrap.getWidth();
21924                 this.dom.style.display = 'none';
21925                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21926             };
21927             t.show = function(){
21928                 var w = triggerField.wrap.getWidth();
21929                 this.dom.style.display = '';
21930                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21931             };
21932             var triggerIndex = 'Trigger'+(index+1);
21933
21934             if(this['hide'+triggerIndex]){
21935                 t.dom.style.display = 'none';
21936             }
21937             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21938             t.addClassOnOver('x-form-trigger-over');
21939             t.addClassOnClick('x-form-trigger-click');
21940         }, this);
21941         this.triggers = ts.elements;
21942     },
21943
21944     onTrigger1Click : Roo.emptyFn,
21945     onTrigger2Click : Roo.emptyFn
21946 });/*
21947  * Based on:
21948  * Ext JS Library 1.1.1
21949  * Copyright(c) 2006-2007, Ext JS, LLC.
21950  *
21951  * Originally Released Under LGPL - original licence link has changed is not relivant.
21952  *
21953  * Fork - LGPL
21954  * <script type="text/javascript">
21955  */
21956  
21957 /**
21958  * @class Roo.form.TextArea
21959  * @extends Roo.form.TextField
21960  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21961  * support for auto-sizing.
21962  * @constructor
21963  * Creates a new TextArea
21964  * @param {Object} config Configuration options
21965  */
21966 Roo.form.TextArea = function(config){
21967     Roo.form.TextArea.superclass.constructor.call(this, config);
21968     // these are provided exchanges for backwards compat
21969     // minHeight/maxHeight were replaced by growMin/growMax to be
21970     // compatible with TextField growing config values
21971     if(this.minHeight !== undefined){
21972         this.growMin = this.minHeight;
21973     }
21974     if(this.maxHeight !== undefined){
21975         this.growMax = this.maxHeight;
21976     }
21977 };
21978
21979 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21980     /**
21981      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21982      */
21983     growMin : 60,
21984     /**
21985      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21986      */
21987     growMax: 1000,
21988     /**
21989      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21990      * in the field (equivalent to setting overflow: hidden, defaults to false)
21991      */
21992     preventScrollbars: false,
21993     /**
21994      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21995      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21996      */
21997
21998     // private
21999     onRender : function(ct, position){
22000         if(!this.el){
22001             this.defaultAutoCreate = {
22002                 tag: "textarea",
22003                 style:"width:300px;height:60px;",
22004                 autocomplete: "off"
22005             };
22006         }
22007         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22008         if(this.grow){
22009             this.textSizeEl = Roo.DomHelper.append(document.body, {
22010                 tag: "pre", cls: "x-form-grow-sizer"
22011             });
22012             if(this.preventScrollbars){
22013                 this.el.setStyle("overflow", "hidden");
22014             }
22015             this.el.setHeight(this.growMin);
22016         }
22017     },
22018
22019     onDestroy : function(){
22020         if(this.textSizeEl){
22021             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22022         }
22023         Roo.form.TextArea.superclass.onDestroy.call(this);
22024     },
22025
22026     // private
22027     onKeyUp : function(e){
22028         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22029             this.autoSize();
22030         }
22031     },
22032
22033     /**
22034      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22035      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22036      */
22037     autoSize : function(){
22038         if(!this.grow || !this.textSizeEl){
22039             return;
22040         }
22041         var el = this.el;
22042         var v = el.dom.value;
22043         var ts = this.textSizeEl;
22044
22045         ts.innerHTML = '';
22046         ts.appendChild(document.createTextNode(v));
22047         v = ts.innerHTML;
22048
22049         Roo.fly(ts).setWidth(this.el.getWidth());
22050         if(v.length < 1){
22051             v = "&#160;&#160;";
22052         }else{
22053             if(Roo.isIE){
22054                 v = v.replace(/\n/g, '<p>&#160;</p>');
22055             }
22056             v += "&#160;\n&#160;";
22057         }
22058         ts.innerHTML = v;
22059         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22060         if(h != this.lastHeight){
22061             this.lastHeight = h;
22062             this.el.setHeight(h);
22063             this.fireEvent("autosize", this, h);
22064         }
22065     }
22066 });/*
22067  * Based on:
22068  * Ext JS Library 1.1.1
22069  * Copyright(c) 2006-2007, Ext JS, LLC.
22070  *
22071  * Originally Released Under LGPL - original licence link has changed is not relivant.
22072  *
22073  * Fork - LGPL
22074  * <script type="text/javascript">
22075  */
22076  
22077
22078 /**
22079  * @class Roo.form.NumberField
22080  * @extends Roo.form.TextField
22081  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22082  * @constructor
22083  * Creates a new NumberField
22084  * @param {Object} config Configuration options
22085  */
22086 Roo.form.NumberField = function(config){
22087     Roo.form.NumberField.superclass.constructor.call(this, config);
22088 };
22089
22090 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22091     /**
22092      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22093      */
22094     fieldClass: "x-form-field x-form-num-field",
22095     /**
22096      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22097      */
22098     allowDecimals : true,
22099     /**
22100      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22101      */
22102     decimalSeparator : ".",
22103     /**
22104      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22105      */
22106     decimalPrecision : 2,
22107     /**
22108      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22109      */
22110     allowNegative : true,
22111     /**
22112      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22113      */
22114     minValue : Number.NEGATIVE_INFINITY,
22115     /**
22116      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22117      */
22118     maxValue : Number.MAX_VALUE,
22119     /**
22120      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22121      */
22122     minText : "The minimum value for this field is {0}",
22123     /**
22124      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22125      */
22126     maxText : "The maximum value for this field is {0}",
22127     /**
22128      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22129      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22130      */
22131     nanText : "{0} is not a valid number",
22132
22133     // private
22134     initEvents : function(){
22135         Roo.form.NumberField.superclass.initEvents.call(this);
22136         var allowed = "0123456789";
22137         if(this.allowDecimals){
22138             allowed += this.decimalSeparator;
22139         }
22140         if(this.allowNegative){
22141             allowed += "-";
22142         }
22143         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22144         var keyPress = function(e){
22145             var k = e.getKey();
22146             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22147                 return;
22148             }
22149             var c = e.getCharCode();
22150             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22151                 e.stopEvent();
22152             }
22153         };
22154         this.el.on("keypress", keyPress, this);
22155     },
22156
22157     // private
22158     validateValue : function(value){
22159         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22160             return false;
22161         }
22162         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22163              return true;
22164         }
22165         var num = this.parseValue(value);
22166         if(isNaN(num)){
22167             this.markInvalid(String.format(this.nanText, value));
22168             return false;
22169         }
22170         if(num < this.minValue){
22171             this.markInvalid(String.format(this.minText, this.minValue));
22172             return false;
22173         }
22174         if(num > this.maxValue){
22175             this.markInvalid(String.format(this.maxText, this.maxValue));
22176             return false;
22177         }
22178         return true;
22179     },
22180
22181     getValue : function(){
22182         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22183     },
22184
22185     // private
22186     parseValue : function(value){
22187         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22188         return isNaN(value) ? '' : value;
22189     },
22190
22191     // private
22192     fixPrecision : function(value){
22193         var nan = isNaN(value);
22194         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22195             return nan ? '' : value;
22196         }
22197         return parseFloat(value).toFixed(this.decimalPrecision);
22198     },
22199
22200     setValue : function(v){
22201         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22202     },
22203
22204     // private
22205     decimalPrecisionFcn : function(v){
22206         return Math.floor(v);
22207     },
22208
22209     beforeBlur : function(){
22210         var v = this.parseValue(this.getRawValue());
22211         if(v){
22212             this.setValue(this.fixPrecision(v));
22213         }
22214     }
22215 });/*
22216  * Based on:
22217  * Ext JS Library 1.1.1
22218  * Copyright(c) 2006-2007, Ext JS, LLC.
22219  *
22220  * Originally Released Under LGPL - original licence link has changed is not relivant.
22221  *
22222  * Fork - LGPL
22223  * <script type="text/javascript">
22224  */
22225  
22226 /**
22227  * @class Roo.form.DateField
22228  * @extends Roo.form.TriggerField
22229  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22230 * @constructor
22231 * Create a new DateField
22232 * @param {Object} config
22233  */
22234 Roo.form.DateField = function(config){
22235     Roo.form.DateField.superclass.constructor.call(this, config);
22236     
22237       this.addEvents({
22238          
22239         /**
22240          * @event select
22241          * Fires when a date is selected
22242              * @param {Roo.form.DateField} combo This combo box
22243              * @param {Date} date The date selected
22244              */
22245         'select' : true
22246          
22247     });
22248     
22249     
22250     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22251     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22252     this.ddMatch = null;
22253     if(this.disabledDates){
22254         var dd = this.disabledDates;
22255         var re = "(?:";
22256         for(var i = 0; i < dd.length; i++){
22257             re += dd[i];
22258             if(i != dd.length-1) re += "|";
22259         }
22260         this.ddMatch = new RegExp(re + ")");
22261     }
22262 };
22263
22264 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22265     /**
22266      * @cfg {String} format
22267      * The default date format string which can be overriden for localization support.  The format must be
22268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22269      */
22270     format : "m/d/y",
22271     /**
22272      * @cfg {String} altFormats
22273      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22274      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22275      */
22276     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22277     /**
22278      * @cfg {Array} disabledDays
22279      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22280      */
22281     disabledDays : null,
22282     /**
22283      * @cfg {String} disabledDaysText
22284      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22285      */
22286     disabledDaysText : "Disabled",
22287     /**
22288      * @cfg {Array} disabledDates
22289      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22290      * expression so they are very powerful. Some examples:
22291      * <ul>
22292      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22293      * <li>["03/08", "09/16"] would disable those days for every year</li>
22294      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22295      * <li>["03/../2006"] would disable every day in March 2006</li>
22296      * <li>["^03"] would disable every day in every March</li>
22297      * </ul>
22298      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22299      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22300      */
22301     disabledDates : null,
22302     /**
22303      * @cfg {String} disabledDatesText
22304      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22305      */
22306     disabledDatesText : "Disabled",
22307     /**
22308      * @cfg {Date/String} minValue
22309      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22310      * valid format (defaults to null).
22311      */
22312     minValue : null,
22313     /**
22314      * @cfg {Date/String} maxValue
22315      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22316      * valid format (defaults to null).
22317      */
22318     maxValue : null,
22319     /**
22320      * @cfg {String} minText
22321      * The error text to display when the date in the cell is before minValue (defaults to
22322      * 'The date in this field must be after {minValue}').
22323      */
22324     minText : "The date in this field must be equal to or after {0}",
22325     /**
22326      * @cfg {String} maxText
22327      * The error text to display when the date in the cell is after maxValue (defaults to
22328      * 'The date in this field must be before {maxValue}').
22329      */
22330     maxText : "The date in this field must be equal to or before {0}",
22331     /**
22332      * @cfg {String} invalidText
22333      * The error text to display when the date in the field is invalid (defaults to
22334      * '{value} is not a valid date - it must be in the format {format}').
22335      */
22336     invalidText : "{0} is not a valid date - it must be in the format {1}",
22337     /**
22338      * @cfg {String} triggerClass
22339      * An additional CSS class used to style the trigger button.  The trigger will always get the
22340      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22341      * which displays a calendar icon).
22342      */
22343     triggerClass : 'x-form-date-trigger',
22344     
22345
22346     /**
22347      * @cfg {bool} useIso
22348      * if enabled, then the date field will use a hidden field to store the 
22349      * real value as iso formated date. default (false)
22350      */ 
22351     useIso : false,
22352     /**
22353      * @cfg {String/Object} autoCreate
22354      * A DomHelper element spec, or true for a default element spec (defaults to
22355      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22356      */ 
22357     // private
22358     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22359     
22360     // private
22361     hiddenField: false,
22362     
22363     onRender : function(ct, position)
22364     {
22365         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22366         if (this.useIso) {
22367             this.el.dom.removeAttribute('name'); 
22368             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22369                     'before', true);
22370             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22371             // prevent input submission
22372             this.hiddenName = this.name;
22373         }
22374             
22375             
22376     },
22377     
22378     // private
22379     validateValue : function(value)
22380     {
22381         value = this.formatDate(value);
22382         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22383             return false;
22384         }
22385         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22386              return true;
22387         }
22388         var svalue = value;
22389         value = this.parseDate(value);
22390         if(!value){
22391             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22392             return false;
22393         }
22394         var time = value.getTime();
22395         if(this.minValue && time < this.minValue.getTime()){
22396             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22397             return false;
22398         }
22399         if(this.maxValue && time > this.maxValue.getTime()){
22400             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22401             return false;
22402         }
22403         if(this.disabledDays){
22404             var day = value.getDay();
22405             for(var i = 0; i < this.disabledDays.length; i++) {
22406                 if(day === this.disabledDays[i]){
22407                     this.markInvalid(this.disabledDaysText);
22408                     return false;
22409                 }
22410             }
22411         }
22412         var fvalue = this.formatDate(value);
22413         if(this.ddMatch && this.ddMatch.test(fvalue)){
22414             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22415             return false;
22416         }
22417         return true;
22418     },
22419
22420     // private
22421     // Provides logic to override the default TriggerField.validateBlur which just returns true
22422     validateBlur : function(){
22423         return !this.menu || !this.menu.isVisible();
22424     },
22425
22426     /**
22427      * Returns the current date value of the date field.
22428      * @return {Date} The date value
22429      */
22430     getValue : function(){
22431         
22432         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22433     },
22434
22435     /**
22436      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22437      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22438      * (the default format used is "m/d/y").
22439      * <br />Usage:
22440      * <pre><code>
22441 //All of these calls set the same date value (May 4, 2006)
22442
22443 //Pass a date object:
22444 var dt = new Date('5/4/06');
22445 dateField.setValue(dt);
22446
22447 //Pass a date string (default format):
22448 dateField.setValue('5/4/06');
22449
22450 //Pass a date string (custom format):
22451 dateField.format = 'Y-m-d';
22452 dateField.setValue('2006-5-4');
22453 </code></pre>
22454      * @param {String/Date} date The date or valid date string
22455      */
22456     setValue : function(date){
22457         if (this.hiddenField) {
22458             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22459         }
22460         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22461     },
22462
22463     // private
22464     parseDate : function(value){
22465         if(!value || value instanceof Date){
22466             return value;
22467         }
22468         var v = Date.parseDate(value, this.format);
22469         if(!v && this.altFormats){
22470             if(!this.altFormatsArray){
22471                 this.altFormatsArray = this.altFormats.split("|");
22472             }
22473             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22474                 v = Date.parseDate(value, this.altFormatsArray[i]);
22475             }
22476         }
22477         return v;
22478     },
22479
22480     // private
22481     formatDate : function(date, fmt){
22482         return (!date || !(date instanceof Date)) ?
22483                date : date.dateFormat(fmt || this.format);
22484     },
22485
22486     // private
22487     menuListeners : {
22488         select: function(m, d){
22489             this.setValue(d);
22490             this.fireEvent('select', this, d);
22491         },
22492         show : function(){ // retain focus styling
22493             this.onFocus();
22494         },
22495         hide : function(){
22496             this.focus.defer(10, this);
22497             var ml = this.menuListeners;
22498             this.menu.un("select", ml.select,  this);
22499             this.menu.un("show", ml.show,  this);
22500             this.menu.un("hide", ml.hide,  this);
22501         }
22502     },
22503
22504     // private
22505     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22506     onTriggerClick : function(){
22507         if(this.disabled){
22508             return;
22509         }
22510         if(this.menu == null){
22511             this.menu = new Roo.menu.DateMenu();
22512         }
22513         Roo.apply(this.menu.picker,  {
22514             showClear: this.allowBlank,
22515             minDate : this.minValue,
22516             maxDate : this.maxValue,
22517             disabledDatesRE : this.ddMatch,
22518             disabledDatesText : this.disabledDatesText,
22519             disabledDays : this.disabledDays,
22520             disabledDaysText : this.disabledDaysText,
22521             format : this.format,
22522             minText : String.format(this.minText, this.formatDate(this.minValue)),
22523             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22524         });
22525         this.menu.on(Roo.apply({}, this.menuListeners, {
22526             scope:this
22527         }));
22528         this.menu.picker.setValue(this.getValue() || new Date());
22529         this.menu.show(this.el, "tl-bl?");
22530     },
22531
22532     beforeBlur : function(){
22533         var v = this.parseDate(this.getRawValue());
22534         if(v){
22535             this.setValue(v);
22536         }
22537     }
22538
22539     /** @cfg {Boolean} grow @hide */
22540     /** @cfg {Number} growMin @hide */
22541     /** @cfg {Number} growMax @hide */
22542     /**
22543      * @hide
22544      * @method autoSize
22545      */
22546 });/*
22547  * Based on:
22548  * Ext JS Library 1.1.1
22549  * Copyright(c) 2006-2007, Ext JS, LLC.
22550  *
22551  * Originally Released Under LGPL - original licence link has changed is not relivant.
22552  *
22553  * Fork - LGPL
22554  * <script type="text/javascript">
22555  */
22556  
22557
22558 /**
22559  * @class Roo.form.ComboBox
22560  * @extends Roo.form.TriggerField
22561  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22562  * @constructor
22563  * Create a new ComboBox.
22564  * @param {Object} config Configuration options
22565  */
22566 Roo.form.ComboBox = function(config){
22567     Roo.form.ComboBox.superclass.constructor.call(this, config);
22568     this.addEvents({
22569         /**
22570          * @event expand
22571          * Fires when the dropdown list is expanded
22572              * @param {Roo.form.ComboBox} combo This combo box
22573              */
22574         'expand' : true,
22575         /**
22576          * @event collapse
22577          * Fires when the dropdown list is collapsed
22578              * @param {Roo.form.ComboBox} combo This combo box
22579              */
22580         'collapse' : true,
22581         /**
22582          * @event beforeselect
22583          * Fires before a list item is selected. Return false to cancel the selection.
22584              * @param {Roo.form.ComboBox} combo This combo box
22585              * @param {Roo.data.Record} record The data record returned from the underlying store
22586              * @param {Number} index The index of the selected item in the dropdown list
22587              */
22588         'beforeselect' : true,
22589         /**
22590          * @event select
22591          * Fires when a list item is selected
22592              * @param {Roo.form.ComboBox} combo This combo box
22593              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22594              * @param {Number} index The index of the selected item in the dropdown list
22595              */
22596         'select' : true,
22597         /**
22598          * @event beforequery
22599          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22600          * The event object passed has these properties:
22601              * @param {Roo.form.ComboBox} combo This combo box
22602              * @param {String} query The query
22603              * @param {Boolean} forceAll true to force "all" query
22604              * @param {Boolean} cancel true to cancel the query
22605              * @param {Object} e The query event object
22606              */
22607         'beforequery': true,
22608          /**
22609          * @event add
22610          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22611              * @param {Roo.form.ComboBox} combo This combo box
22612              */
22613         'add' : true,
22614         /**
22615          * @event edit
22616          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22617              * @param {Roo.form.ComboBox} combo This combo box
22618              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22619              */
22620         'edit' : true
22621         
22622         
22623     });
22624     if(this.transform){
22625         this.allowDomMove = false;
22626         var s = Roo.getDom(this.transform);
22627         if(!this.hiddenName){
22628             this.hiddenName = s.name;
22629         }
22630         if(!this.store){
22631             this.mode = 'local';
22632             var d = [], opts = s.options;
22633             for(var i = 0, len = opts.length;i < len; i++){
22634                 var o = opts[i];
22635                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22636                 if(o.selected) {
22637                     this.value = value;
22638                 }
22639                 d.push([value, o.text]);
22640             }
22641             this.store = new Roo.data.SimpleStore({
22642                 'id': 0,
22643                 fields: ['value', 'text'],
22644                 data : d
22645             });
22646             this.valueField = 'value';
22647             this.displayField = 'text';
22648         }
22649         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22650         if(!this.lazyRender){
22651             this.target = true;
22652             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22653             s.parentNode.removeChild(s); // remove it
22654             this.render(this.el.parentNode);
22655         }else{
22656             s.parentNode.removeChild(s); // remove it
22657         }
22658
22659     }
22660     if (this.store) {
22661         this.store = Roo.factory(this.store, Roo.data);
22662     }
22663     
22664     this.selectedIndex = -1;
22665     if(this.mode == 'local'){
22666         if(config.queryDelay === undefined){
22667             this.queryDelay = 10;
22668         }
22669         if(config.minChars === undefined){
22670             this.minChars = 0;
22671         }
22672     }
22673 };
22674
22675 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22676     /**
22677      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22678      */
22679     /**
22680      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22681      * rendering into an Roo.Editor, defaults to false)
22682      */
22683     /**
22684      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22685      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22686      */
22687     /**
22688      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22689      */
22690     /**
22691      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22692      * the dropdown list (defaults to undefined, with no header element)
22693      */
22694
22695      /**
22696      * @cfg {String/Roo.Template} tpl The template to use to render the output
22697      */
22698      
22699     // private
22700     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22701     /**
22702      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22703      */
22704     listWidth: undefined,
22705     /**
22706      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22707      * mode = 'remote' or 'text' if mode = 'local')
22708      */
22709     displayField: undefined,
22710     /**
22711      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22712      * mode = 'remote' or 'value' if mode = 'local'). 
22713      * Note: use of a valueField requires the user make a selection
22714      * in order for a value to be mapped.
22715      */
22716     valueField: undefined,
22717     /**
22718      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22719      * field's data value (defaults to the underlying DOM element's name)
22720      */
22721     hiddenName: undefined,
22722     /**
22723      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22724      */
22725     listClass: '',
22726     /**
22727      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22728      */
22729     selectedClass: 'x-combo-selected',
22730     /**
22731      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22732      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22733      * which displays a downward arrow icon).
22734      */
22735     triggerClass : 'x-form-arrow-trigger',
22736     /**
22737      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22738      */
22739     shadow:'sides',
22740     /**
22741      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22742      * anchor positions (defaults to 'tl-bl')
22743      */
22744     listAlign: 'tl-bl?',
22745     /**
22746      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22747      */
22748     maxHeight: 300,
22749     /**
22750      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22751      * query specified by the allQuery config option (defaults to 'query')
22752      */
22753     triggerAction: 'query',
22754     /**
22755      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22756      * (defaults to 4, does not apply if editable = false)
22757      */
22758     minChars : 4,
22759     /**
22760      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22761      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22762      */
22763     typeAhead: false,
22764     /**
22765      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22766      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22767      */
22768     queryDelay: 500,
22769     /**
22770      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22771      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22772      */
22773     pageSize: 0,
22774     /**
22775      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22776      * when editable = true (defaults to false)
22777      */
22778     selectOnFocus:false,
22779     /**
22780      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22781      */
22782     queryParam: 'query',
22783     /**
22784      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22785      * when mode = 'remote' (defaults to 'Loading...')
22786      */
22787     loadingText: 'Loading...',
22788     /**
22789      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22790      */
22791     resizable: false,
22792     /**
22793      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22794      */
22795     handleHeight : 8,
22796     /**
22797      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22798      * traditional select (defaults to true)
22799      */
22800     editable: true,
22801     /**
22802      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22803      */
22804     allQuery: '',
22805     /**
22806      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22807      */
22808     mode: 'remote',
22809     /**
22810      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22811      * listWidth has a higher value)
22812      */
22813     minListWidth : 70,
22814     /**
22815      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22816      * allow the user to set arbitrary text into the field (defaults to false)
22817      */
22818     forceSelection:false,
22819     /**
22820      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22821      * if typeAhead = true (defaults to 250)
22822      */
22823     typeAheadDelay : 250,
22824     /**
22825      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22826      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22827      */
22828     valueNotFoundText : undefined,
22829     /**
22830      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22831      */
22832     blockFocus : false,
22833     
22834     /**
22835      * @cfg {Boolean} disableClear Disable showing of clear button.
22836      */
22837     disableClear : false,
22838     /**
22839      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22840      */
22841     alwaysQuery : false,
22842     
22843     //private
22844     addicon : false,
22845     editicon: false,
22846     
22847     
22848     // private
22849     onRender : function(ct, position){
22850         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22851         if(this.hiddenName){
22852             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22853                     'before', true);
22854             this.hiddenField.value =
22855                 this.hiddenValue !== undefined ? this.hiddenValue :
22856                 this.value !== undefined ? this.value : '';
22857
22858             // prevent input submission
22859             this.el.dom.removeAttribute('name');
22860         }
22861         if(Roo.isGecko){
22862             this.el.dom.setAttribute('autocomplete', 'off');
22863         }
22864
22865         var cls = 'x-combo-list';
22866
22867         this.list = new Roo.Layer({
22868             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22869         });
22870
22871         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22872         this.list.setWidth(lw);
22873         this.list.swallowEvent('mousewheel');
22874         this.assetHeight = 0;
22875
22876         if(this.title){
22877             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22878             this.assetHeight += this.header.getHeight();
22879         }
22880
22881         this.innerList = this.list.createChild({cls:cls+'-inner'});
22882         this.innerList.on('mouseover', this.onViewOver, this);
22883         this.innerList.on('mousemove', this.onViewMove, this);
22884         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22885         
22886         if(this.allowBlank && !this.pageSize && !this.disableClear){
22887             this.footer = this.list.createChild({cls:cls+'-ft'});
22888             this.pageTb = new Roo.Toolbar(this.footer);
22889            
22890         }
22891         if(this.pageSize){
22892             this.footer = this.list.createChild({cls:cls+'-ft'});
22893             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22894                     {pageSize: this.pageSize});
22895             
22896         }
22897         
22898         if (this.pageTb && this.allowBlank && !this.disableClear) {
22899             var _this = this;
22900             this.pageTb.add(new Roo.Toolbar.Fill(), {
22901                 cls: 'x-btn-icon x-btn-clear',
22902                 text: '&#160;',
22903                 handler: function()
22904                 {
22905                     _this.collapse();
22906                     _this.clearValue();
22907                     _this.onSelect(false, -1);
22908                 }
22909             });
22910         }
22911         if (this.footer) {
22912             this.assetHeight += this.footer.getHeight();
22913         }
22914         
22915
22916         if(!this.tpl){
22917             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22918         }
22919
22920         this.view = new Roo.View(this.innerList, this.tpl, {
22921             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22922         });
22923
22924         this.view.on('click', this.onViewClick, this);
22925
22926         this.store.on('beforeload', this.onBeforeLoad, this);
22927         this.store.on('load', this.onLoad, this);
22928         this.store.on('loadexception', this.collapse, this);
22929
22930         if(this.resizable){
22931             this.resizer = new Roo.Resizable(this.list,  {
22932                pinned:true, handles:'se'
22933             });
22934             this.resizer.on('resize', function(r, w, h){
22935                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22936                 this.listWidth = w;
22937                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22938                 this.restrictHeight();
22939             }, this);
22940             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22941         }
22942         if(!this.editable){
22943             this.editable = true;
22944             this.setEditable(false);
22945         }  
22946         
22947         
22948         if (typeof(this.events.add.listeners) != 'undefined') {
22949             
22950             this.addicon = this.wrap.createChild(
22951                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22952        
22953             this.addicon.on('click', function(e) {
22954                 this.fireEvent('add', this);
22955             }, this);
22956         }
22957         if (typeof(this.events.edit.listeners) != 'undefined') {
22958             
22959             this.editicon = this.wrap.createChild(
22960                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22961             if (this.addicon) {
22962                 this.editicon.setStyle('margin-left', '40px');
22963             }
22964             this.editicon.on('click', function(e) {
22965                 
22966                 // we fire even  if inothing is selected..
22967                 this.fireEvent('edit', this, this.lastData );
22968                 
22969             }, this);
22970         }
22971         
22972         
22973         
22974     },
22975
22976     // private
22977     initEvents : function(){
22978         Roo.form.ComboBox.superclass.initEvents.call(this);
22979
22980         this.keyNav = new Roo.KeyNav(this.el, {
22981             "up" : function(e){
22982                 this.inKeyMode = true;
22983                 this.selectPrev();
22984             },
22985
22986             "down" : function(e){
22987                 if(!this.isExpanded()){
22988                     this.onTriggerClick();
22989                 }else{
22990                     this.inKeyMode = true;
22991                     this.selectNext();
22992                 }
22993             },
22994
22995             "enter" : function(e){
22996                 this.onViewClick();
22997                 //return true;
22998             },
22999
23000             "esc" : function(e){
23001                 this.collapse();
23002             },
23003
23004             "tab" : function(e){
23005                 this.onViewClick(false);
23006                 return true;
23007             },
23008
23009             scope : this,
23010
23011             doRelay : function(foo, bar, hname){
23012                 if(hname == 'down' || this.scope.isExpanded()){
23013                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23014                 }
23015                 return true;
23016             },
23017
23018             forceKeyDown: true
23019         });
23020         this.queryDelay = Math.max(this.queryDelay || 10,
23021                 this.mode == 'local' ? 10 : 250);
23022         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23023         if(this.typeAhead){
23024             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23025         }
23026         if(this.editable !== false){
23027             this.el.on("keyup", this.onKeyUp, this);
23028         }
23029         if(this.forceSelection){
23030             this.on('blur', this.doForce, this);
23031         }
23032     },
23033
23034     onDestroy : function(){
23035         if(this.view){
23036             this.view.setStore(null);
23037             this.view.el.removeAllListeners();
23038             this.view.el.remove();
23039             this.view.purgeListeners();
23040         }
23041         if(this.list){
23042             this.list.destroy();
23043         }
23044         if(this.store){
23045             this.store.un('beforeload', this.onBeforeLoad, this);
23046             this.store.un('load', this.onLoad, this);
23047             this.store.un('loadexception', this.collapse, this);
23048         }
23049         Roo.form.ComboBox.superclass.onDestroy.call(this);
23050     },
23051
23052     // private
23053     fireKey : function(e){
23054         if(e.isNavKeyPress() && !this.list.isVisible()){
23055             this.fireEvent("specialkey", this, e);
23056         }
23057     },
23058
23059     // private
23060     onResize: function(w, h){
23061         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23062         
23063         if(typeof w != 'number'){
23064             // we do not handle it!?!?
23065             return;
23066         }
23067         var tw = this.trigger.getWidth();
23068         tw += this.addicon ? this.addicon.getWidth() : 0;
23069         tw += this.editicon ? this.editicon.getWidth() : 0;
23070         var x = w - tw;
23071         this.el.setWidth( this.adjustWidth('input', x));
23072             
23073         this.trigger.setStyle('left', x+'px');
23074         
23075         if(this.list && this.listWidth === undefined){
23076             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23077             this.list.setWidth(lw);
23078             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23079         }
23080         
23081     
23082         
23083     },
23084
23085     /**
23086      * Allow or prevent the user from directly editing the field text.  If false is passed,
23087      * the user will only be able to select from the items defined in the dropdown list.  This method
23088      * is the runtime equivalent of setting the 'editable' config option at config time.
23089      * @param {Boolean} value True to allow the user to directly edit the field text
23090      */
23091     setEditable : function(value){
23092         if(value == this.editable){
23093             return;
23094         }
23095         this.editable = value;
23096         if(!value){
23097             this.el.dom.setAttribute('readOnly', true);
23098             this.el.on('mousedown', this.onTriggerClick,  this);
23099             this.el.addClass('x-combo-noedit');
23100         }else{
23101             this.el.dom.setAttribute('readOnly', false);
23102             this.el.un('mousedown', this.onTriggerClick,  this);
23103             this.el.removeClass('x-combo-noedit');
23104         }
23105     },
23106
23107     // private
23108     onBeforeLoad : function(){
23109         if(!this.hasFocus){
23110             return;
23111         }
23112         this.innerList.update(this.loadingText ?
23113                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23114         this.restrictHeight();
23115         this.selectedIndex = -1;
23116     },
23117
23118     // private
23119     onLoad : function(){
23120         if(!this.hasFocus){
23121             return;
23122         }
23123         if(this.store.getCount() > 0){
23124             this.expand();
23125             this.restrictHeight();
23126             if(this.lastQuery == this.allQuery){
23127                 if(this.editable){
23128                     this.el.dom.select();
23129                 }
23130                 if(!this.selectByValue(this.value, true)){
23131                     this.select(0, true);
23132                 }
23133             }else{
23134                 this.selectNext();
23135                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23136                     this.taTask.delay(this.typeAheadDelay);
23137                 }
23138             }
23139         }else{
23140             this.onEmptyResults();
23141         }
23142         //this.el.focus();
23143     },
23144
23145     // private
23146     onTypeAhead : function(){
23147         if(this.store.getCount() > 0){
23148             var r = this.store.getAt(0);
23149             var newValue = r.data[this.displayField];
23150             var len = newValue.length;
23151             var selStart = this.getRawValue().length;
23152             if(selStart != len){
23153                 this.setRawValue(newValue);
23154                 this.selectText(selStart, newValue.length);
23155             }
23156         }
23157     },
23158
23159     // private
23160     onSelect : function(record, index){
23161         if(this.fireEvent('beforeselect', this, record, index) !== false){
23162             this.setFromData(index > -1 ? record.data : false);
23163             this.collapse();
23164             this.fireEvent('select', this, record, index);
23165         }
23166     },
23167
23168     /**
23169      * Returns the currently selected field value or empty string if no value is set.
23170      * @return {String} value The selected value
23171      */
23172     getValue : function(){
23173         if(this.valueField){
23174             return typeof this.value != 'undefined' ? this.value : '';
23175         }else{
23176             return Roo.form.ComboBox.superclass.getValue.call(this);
23177         }
23178     },
23179
23180     /**
23181      * Clears any text/value currently set in the field
23182      */
23183     clearValue : function(){
23184         if(this.hiddenField){
23185             this.hiddenField.value = '';
23186         }
23187         this.value = '';
23188         this.setRawValue('');
23189         this.lastSelectionText = '';
23190         this.applyEmptyText();
23191     },
23192
23193     /**
23194      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23195      * will be displayed in the field.  If the value does not match the data value of an existing item,
23196      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23197      * Otherwise the field will be blank (although the value will still be set).
23198      * @param {String} value The value to match
23199      */
23200     setValue : function(v){
23201         var text = v;
23202         if(this.valueField){
23203             var r = this.findRecord(this.valueField, v);
23204             if(r){
23205                 text = r.data[this.displayField];
23206             }else if(this.valueNotFoundText !== undefined){
23207                 text = this.valueNotFoundText;
23208             }
23209         }
23210         this.lastSelectionText = text;
23211         if(this.hiddenField){
23212             this.hiddenField.value = v;
23213         }
23214         Roo.form.ComboBox.superclass.setValue.call(this, text);
23215         this.value = v;
23216     },
23217     /**
23218      * @property {Object} the last set data for the element
23219      */
23220     
23221     lastData : false,
23222     /**
23223      * Sets the value of the field based on a object which is related to the record format for the store.
23224      * @param {Object} value the value to set as. or false on reset?
23225      */
23226     setFromData : function(o){
23227         var dv = ''; // display value
23228         var vv = ''; // value value..
23229         this.lastData = o;
23230         if (this.displayField) {
23231             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23232         } else {
23233             // this is an error condition!!!
23234             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23235         }
23236         
23237         if(this.valueField){
23238             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23239         }
23240         if(this.hiddenField){
23241             this.hiddenField.value = vv;
23242             
23243             this.lastSelectionText = dv;
23244             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23245             this.value = vv;
23246             return;
23247         }
23248         // no hidden field.. - we store the value in 'value', but still display
23249         // display field!!!!
23250         this.lastSelectionText = dv;
23251         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23252         this.value = vv;
23253         
23254         
23255     },
23256     // private
23257     reset : function(){
23258         // overridden so that last data is reset..
23259         this.setValue(this.originalValue);
23260         this.clearInvalid();
23261         this.lastData = false;
23262     },
23263     // private
23264     findRecord : function(prop, value){
23265         var record;
23266         if(this.store.getCount() > 0){
23267             this.store.each(function(r){
23268                 if(r.data[prop] == value){
23269                     record = r;
23270                     return false;
23271                 }
23272             });
23273         }
23274         return record;
23275     },
23276
23277     // private
23278     onViewMove : function(e, t){
23279         this.inKeyMode = false;
23280     },
23281
23282     // private
23283     onViewOver : function(e, t){
23284         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23285             return;
23286         }
23287         var item = this.view.findItemFromChild(t);
23288         if(item){
23289             var index = this.view.indexOf(item);
23290             this.select(index, false);
23291         }
23292     },
23293
23294     // private
23295     onViewClick : function(doFocus){
23296         var index = this.view.getSelectedIndexes()[0];
23297         var r = this.store.getAt(index);
23298         if(r){
23299             this.onSelect(r, index);
23300         }
23301         if(doFocus !== false && !this.blockFocus){
23302             this.el.focus();
23303         }
23304     },
23305
23306     // private
23307     restrictHeight : function(){
23308         this.innerList.dom.style.height = '';
23309         var inner = this.innerList.dom;
23310         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23311         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23312         this.list.beginUpdate();
23313         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23314         this.list.alignTo(this.el, this.listAlign);
23315         this.list.endUpdate();
23316     },
23317
23318     // private
23319     onEmptyResults : function(){
23320         this.collapse();
23321     },
23322
23323     /**
23324      * Returns true if the dropdown list is expanded, else false.
23325      */
23326     isExpanded : function(){
23327         return this.list.isVisible();
23328     },
23329
23330     /**
23331      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23332      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23333      * @param {String} value The data value of the item to select
23334      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23335      * selected item if it is not currently in view (defaults to true)
23336      * @return {Boolean} True if the value matched an item in the list, else false
23337      */
23338     selectByValue : function(v, scrollIntoView){
23339         if(v !== undefined && v !== null){
23340             var r = this.findRecord(this.valueField || this.displayField, v);
23341             if(r){
23342                 this.select(this.store.indexOf(r), scrollIntoView);
23343                 return true;
23344             }
23345         }
23346         return false;
23347     },
23348
23349     /**
23350      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23351      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23352      * @param {Number} index The zero-based index of the list item to select
23353      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23354      * selected item if it is not currently in view (defaults to true)
23355      */
23356     select : function(index, scrollIntoView){
23357         this.selectedIndex = index;
23358         this.view.select(index);
23359         if(scrollIntoView !== false){
23360             var el = this.view.getNode(index);
23361             if(el){
23362                 this.innerList.scrollChildIntoView(el, false);
23363             }
23364         }
23365     },
23366
23367     // private
23368     selectNext : function(){
23369         var ct = this.store.getCount();
23370         if(ct > 0){
23371             if(this.selectedIndex == -1){
23372                 this.select(0);
23373             }else if(this.selectedIndex < ct-1){
23374                 this.select(this.selectedIndex+1);
23375             }
23376         }
23377     },
23378
23379     // private
23380     selectPrev : function(){
23381         var ct = this.store.getCount();
23382         if(ct > 0){
23383             if(this.selectedIndex == -1){
23384                 this.select(0);
23385             }else if(this.selectedIndex != 0){
23386                 this.select(this.selectedIndex-1);
23387             }
23388         }
23389     },
23390
23391     // private
23392     onKeyUp : function(e){
23393         if(this.editable !== false && !e.isSpecialKey()){
23394             this.lastKey = e.getKey();
23395             this.dqTask.delay(this.queryDelay);
23396         }
23397     },
23398
23399     // private
23400     validateBlur : function(){
23401         return !this.list || !this.list.isVisible();   
23402     },
23403
23404     // private
23405     initQuery : function(){
23406         this.doQuery(this.getRawValue());
23407     },
23408
23409     // private
23410     doForce : function(){
23411         if(this.el.dom.value.length > 0){
23412             this.el.dom.value =
23413                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23414             this.applyEmptyText();
23415         }
23416     },
23417
23418     /**
23419      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23420      * query allowing the query action to be canceled if needed.
23421      * @param {String} query The SQL query to execute
23422      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23423      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23424      * saved in the current store (defaults to false)
23425      */
23426     doQuery : function(q, forceAll){
23427         if(q === undefined || q === null){
23428             q = '';
23429         }
23430         var qe = {
23431             query: q,
23432             forceAll: forceAll,
23433             combo: this,
23434             cancel:false
23435         };
23436         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23437             return false;
23438         }
23439         q = qe.query;
23440         forceAll = qe.forceAll;
23441         if(forceAll === true || (q.length >= this.minChars)){
23442             if(this.lastQuery != q || this.alwaysQuery){
23443                 this.lastQuery = q;
23444                 if(this.mode == 'local'){
23445                     this.selectedIndex = -1;
23446                     if(forceAll){
23447                         this.store.clearFilter();
23448                     }else{
23449                         this.store.filter(this.displayField, q);
23450                     }
23451                     this.onLoad();
23452                 }else{
23453                     this.store.baseParams[this.queryParam] = q;
23454                     this.store.load({
23455                         params: this.getParams(q)
23456                     });
23457                     this.expand();
23458                 }
23459             }else{
23460                 this.selectedIndex = -1;
23461                 this.onLoad();   
23462             }
23463         }
23464     },
23465
23466     // private
23467     getParams : function(q){
23468         var p = {};
23469         //p[this.queryParam] = q;
23470         if(this.pageSize){
23471             p.start = 0;
23472             p.limit = this.pageSize;
23473         }
23474         return p;
23475     },
23476
23477     /**
23478      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23479      */
23480     collapse : function(){
23481         if(!this.isExpanded()){
23482             return;
23483         }
23484         this.list.hide();
23485         Roo.get(document).un('mousedown', this.collapseIf, this);
23486         Roo.get(document).un('mousewheel', this.collapseIf, this);
23487         if (!this.editable) {
23488             Roo.get(document).un('keydown', this.listKeyPress, this);
23489         }
23490         this.fireEvent('collapse', this);
23491     },
23492
23493     // private
23494     collapseIf : function(e){
23495         if(!e.within(this.wrap) && !e.within(this.list)){
23496             this.collapse();
23497         }
23498     },
23499
23500     /**
23501      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23502      */
23503     expand : function(){
23504         if(this.isExpanded() || !this.hasFocus){
23505             return;
23506         }
23507         this.list.alignTo(this.el, this.listAlign);
23508         this.list.show();
23509         Roo.get(document).on('mousedown', this.collapseIf, this);
23510         Roo.get(document).on('mousewheel', this.collapseIf, this);
23511         if (!this.editable) {
23512             Roo.get(document).on('keydown', this.listKeyPress, this);
23513         }
23514         
23515         this.fireEvent('expand', this);
23516     },
23517
23518     // private
23519     // Implements the default empty TriggerField.onTriggerClick function
23520     onTriggerClick : function(){
23521         if(this.disabled){
23522             return;
23523         }
23524         if(this.isExpanded()){
23525             this.collapse();
23526             if (!this.blockFocus) {
23527                 this.el.focus();
23528             }
23529             
23530         }else {
23531             this.hasFocus = true;
23532             if(this.triggerAction == 'all') {
23533                 this.doQuery(this.allQuery, true);
23534             } else {
23535                 this.doQuery(this.getRawValue());
23536             }
23537             if (!this.blockFocus) {
23538                 this.el.focus();
23539             }
23540         }
23541     },
23542     listKeyPress : function(e)
23543     {
23544         //Roo.log('listkeypress');
23545         // scroll to first matching element based on key pres..
23546         if (e.isSpecialKey()) {
23547             return false;
23548         }
23549         var k = String.fromCharCode(e.getKey()).toUpperCase();
23550         //Roo.log(k);
23551         var match  = false;
23552         var csel = this.view.getSelectedNodes();
23553         var cselitem = false;
23554         if (csel.length) {
23555             var ix = this.view.indexOf(csel[0]);
23556             cselitem  = this.store.getAt(ix);
23557             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23558                 cselitem = false;
23559             }
23560             
23561         }
23562         
23563         this.store.each(function(v) { 
23564             if (cselitem) {
23565                 // start at existing selection.
23566                 if (cselitem.id == v.id) {
23567                     cselitem = false;
23568                 }
23569                 return;
23570             }
23571                 
23572             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23573                 match = this.store.indexOf(v);
23574                 return false;
23575             }
23576         }, this);
23577         
23578         if (match === false) {
23579             return true; // no more action?
23580         }
23581         // scroll to?
23582         this.view.select(match);
23583         var sn = Roo.get(this.view.getSelectedNodes()[0])
23584         sn.scrollIntoView(sn.dom.parentNode, false);
23585     }
23586
23587     /** 
23588     * @cfg {Boolean} grow 
23589     * @hide 
23590     */
23591     /** 
23592     * @cfg {Number} growMin 
23593     * @hide 
23594     */
23595     /** 
23596     * @cfg {Number} growMax 
23597     * @hide 
23598     */
23599     /**
23600      * @hide
23601      * @method autoSize
23602      */
23603 });/*
23604  * Based on:
23605  * Ext JS Library 1.1.1
23606  * Copyright(c) 2006-2007, Ext JS, LLC.
23607  *
23608  * Originally Released Under LGPL - original licence link has changed is not relivant.
23609  *
23610  * Fork - LGPL
23611  * <script type="text/javascript">
23612  */
23613 /**
23614  * @class Roo.form.Checkbox
23615  * @extends Roo.form.Field
23616  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23617  * @constructor
23618  * Creates a new Checkbox
23619  * @param {Object} config Configuration options
23620  */
23621 Roo.form.Checkbox = function(config){
23622     Roo.form.Checkbox.superclass.constructor.call(this, config);
23623     this.addEvents({
23624         /**
23625          * @event check
23626          * Fires when the checkbox is checked or unchecked.
23627              * @param {Roo.form.Checkbox} this This checkbox
23628              * @param {Boolean} checked The new checked value
23629              */
23630         check : true
23631     });
23632 };
23633
23634 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23635     /**
23636      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23637      */
23638     focusClass : undefined,
23639     /**
23640      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23641      */
23642     fieldClass: "x-form-field",
23643     /**
23644      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23645      */
23646     checked: false,
23647     /**
23648      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23649      * {tag: "input", type: "checkbox", autocomplete: "off"})
23650      */
23651     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23652     /**
23653      * @cfg {String} boxLabel The text that appears beside the checkbox
23654      */
23655     boxLabel : "",
23656     /**
23657      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23658      */  
23659     inputValue : '1',
23660     /**
23661      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23662      */
23663      valueOff: '0', // value when not checked..
23664
23665     actionMode : 'viewEl', 
23666     //
23667     // private
23668     itemCls : 'x-menu-check-item x-form-item',
23669     groupClass : 'x-menu-group-item',
23670     inputType : 'hidden',
23671     
23672     
23673     inSetChecked: false, // check that we are not calling self...
23674     
23675     inputElement: false, // real input element?
23676     basedOn: false, // ????
23677     
23678     isFormField: true, // not sure where this is needed!!!!
23679
23680     onResize : function(){
23681         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23682         if(!this.boxLabel){
23683             this.el.alignTo(this.wrap, 'c-c');
23684         }
23685     },
23686
23687     initEvents : function(){
23688         Roo.form.Checkbox.superclass.initEvents.call(this);
23689         this.el.on("click", this.onClick,  this);
23690         this.el.on("change", this.onClick,  this);
23691     },
23692
23693
23694     getResizeEl : function(){
23695         return this.wrap;
23696     },
23697
23698     getPositionEl : function(){
23699         return this.wrap;
23700     },
23701
23702     // private
23703     onRender : function(ct, position){
23704         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23705         /*
23706         if(this.inputValue !== undefined){
23707             this.el.dom.value = this.inputValue;
23708         }
23709         */
23710         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23711         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23712         var viewEl = this.wrap.createChild({ 
23713             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23714         this.viewEl = viewEl;   
23715         this.wrap.on('click', this.onClick,  this); 
23716         
23717         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23718         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23719         
23720         
23721         
23722         if(this.boxLabel){
23723             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23724         //    viewEl.on('click', this.onClick,  this); 
23725         }
23726         //if(this.checked){
23727             this.setChecked(this.checked);
23728         //}else{
23729             //this.checked = this.el.dom;
23730         //}
23731
23732     },
23733
23734     // private
23735     initValue : Roo.emptyFn,
23736
23737     /**
23738      * Returns the checked state of the checkbox.
23739      * @return {Boolean} True if checked, else false
23740      */
23741     getValue : function(){
23742         if(this.el){
23743             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23744         }
23745         return this.valueOff;
23746         
23747     },
23748
23749         // private
23750     onClick : function(){ 
23751         this.setChecked(!this.checked);
23752
23753         //if(this.el.dom.checked != this.checked){
23754         //    this.setValue(this.el.dom.checked);
23755        // }
23756     },
23757
23758     /**
23759      * Sets the checked state of the checkbox.
23760      * On is always based on a string comparison between inputValue and the param.
23761      * @param {Boolean/String} value - the value to set 
23762      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23763      */
23764     setValue : function(v,suppressEvent){
23765         
23766         
23767         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23768         //if(this.el && this.el.dom){
23769         //    this.el.dom.checked = this.checked;
23770         //    this.el.dom.defaultChecked = this.checked;
23771         //}
23772         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23773         //this.fireEvent("check", this, this.checked);
23774     },
23775     // private..
23776     setChecked : function(state,suppressEvent)
23777     {
23778         if (this.inSetChecked) {
23779             this.checked = state;
23780             return;
23781         }
23782         
23783     
23784         if(this.wrap){
23785             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23786         }
23787         this.checked = state;
23788         if(suppressEvent !== true){
23789             this.fireEvent('check', this, state);
23790         }
23791         this.inSetChecked = true;
23792         this.el.dom.value = state ? this.inputValue : this.valueOff;
23793         this.inSetChecked = false;
23794         
23795     },
23796     // handle setting of hidden value by some other method!!?!?
23797     setFromHidden: function()
23798     {
23799         if(!this.el){
23800             return;
23801         }
23802         //console.log("SET FROM HIDDEN");
23803         //alert('setFrom hidden');
23804         this.setValue(this.el.dom.value);
23805     },
23806     
23807     onDestroy : function()
23808     {
23809         if(this.viewEl){
23810             Roo.get(this.viewEl).remove();
23811         }
23812          
23813         Roo.form.Checkbox.superclass.onDestroy.call(this);
23814     }
23815
23816 });/*
23817  * Based on:
23818  * Ext JS Library 1.1.1
23819  * Copyright(c) 2006-2007, Ext JS, LLC.
23820  *
23821  * Originally Released Under LGPL - original licence link has changed is not relivant.
23822  *
23823  * Fork - LGPL
23824  * <script type="text/javascript">
23825  */
23826  
23827 /**
23828  * @class Roo.form.Radio
23829  * @extends Roo.form.Checkbox
23830  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23831  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23832  * @constructor
23833  * Creates a new Radio
23834  * @param {Object} config Configuration options
23835  */
23836 Roo.form.Radio = function(){
23837     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23838 };
23839 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23840     inputType: 'radio',
23841
23842     /**
23843      * If this radio is part of a group, it will return the selected value
23844      * @return {String}
23845      */
23846     getGroupValue : function(){
23847         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23848     }
23849 });//<script type="text/javascript">
23850
23851 /*
23852  * Ext JS Library 1.1.1
23853  * Copyright(c) 2006-2007, Ext JS, LLC.
23854  * licensing@extjs.com
23855  * 
23856  * http://www.extjs.com/license
23857  */
23858  
23859  /*
23860   * 
23861   * Known bugs:
23862   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23863   * - IE ? - no idea how much works there.
23864   * 
23865   * 
23866   * 
23867   */
23868  
23869
23870 /**
23871  * @class Ext.form.HtmlEditor
23872  * @extends Ext.form.Field
23873  * Provides a lightweight HTML Editor component.
23874  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23875  * 
23876  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23877  * supported by this editor.</b><br/><br/>
23878  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23879  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23880  */
23881 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23882       /**
23883      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23884      */
23885     toolbars : false,
23886     /**
23887      * @cfg {String} createLinkText The default text for the create link prompt
23888      */
23889     createLinkText : 'Please enter the URL for the link:',
23890     /**
23891      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23892      */
23893     defaultLinkValue : 'http:/'+'/',
23894    
23895     
23896     // id of frame..
23897     frameId: false,
23898     
23899     // private properties
23900     validationEvent : false,
23901     deferHeight: true,
23902     initialized : false,
23903     activated : false,
23904     sourceEditMode : false,
23905     onFocus : Roo.emptyFn,
23906     iframePad:3,
23907     hideMode:'offsets',
23908     defaultAutoCreate : {
23909         tag: "textarea",
23910         style:"width:500px;height:300px;",
23911         autocomplete: "off"
23912     },
23913
23914     // private
23915     initComponent : function(){
23916         this.addEvents({
23917             /**
23918              * @event initialize
23919              * Fires when the editor is fully initialized (including the iframe)
23920              * @param {HtmlEditor} this
23921              */
23922             initialize: true,
23923             /**
23924              * @event activate
23925              * Fires when the editor is first receives the focus. Any insertion must wait
23926              * until after this event.
23927              * @param {HtmlEditor} this
23928              */
23929             activate: true,
23930              /**
23931              * @event beforesync
23932              * Fires before the textarea is updated with content from the editor iframe. Return false
23933              * to cancel the sync.
23934              * @param {HtmlEditor} this
23935              * @param {String} html
23936              */
23937             beforesync: true,
23938              /**
23939              * @event beforepush
23940              * Fires before the iframe editor is updated with content from the textarea. Return false
23941              * to cancel the push.
23942              * @param {HtmlEditor} this
23943              * @param {String} html
23944              */
23945             beforepush: true,
23946              /**
23947              * @event sync
23948              * Fires when the textarea is updated with content from the editor iframe.
23949              * @param {HtmlEditor} this
23950              * @param {String} html
23951              */
23952             sync: true,
23953              /**
23954              * @event push
23955              * Fires when the iframe editor is updated with content from the textarea.
23956              * @param {HtmlEditor} this
23957              * @param {String} html
23958              */
23959             push: true,
23960              /**
23961              * @event editmodechange
23962              * Fires when the editor switches edit modes
23963              * @param {HtmlEditor} this
23964              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23965              */
23966             editmodechange: true,
23967             /**
23968              * @event editorevent
23969              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23970              * @param {HtmlEditor} this
23971              */
23972             editorevent: true
23973         })
23974     },
23975
23976     /**
23977      * Protected method that will not generally be called directly. It
23978      * is called when the editor creates its toolbar. Override this method if you need to
23979      * add custom toolbar buttons.
23980      * @param {HtmlEditor} editor
23981      */
23982     createToolbar : function(editor){
23983         if (!editor.toolbars || !editor.toolbars.length) {
23984             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23985         }
23986         
23987         for (var i =0 ; i < editor.toolbars.length;i++) {
23988             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23989             editor.toolbars[i].init(editor);
23990         }
23991          
23992         
23993     },
23994
23995     /**
23996      * Protected method that will not generally be called directly. It
23997      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23998      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23999      */
24000     getDocMarkup : function(){
24001         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24002     },
24003
24004     // private
24005     onRender : function(ct, position){
24006         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24007         this.el.dom.style.border = '0 none';
24008         this.el.dom.setAttribute('tabIndex', -1);
24009         this.el.addClass('x-hidden');
24010         if(Roo.isIE){ // fix IE 1px bogus margin
24011             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24012         }
24013         this.wrap = this.el.wrap({
24014             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24015         });
24016
24017         this.frameId = Roo.id();
24018         this.createToolbar(this);
24019         
24020         
24021         
24022         
24023       
24024         
24025         var iframe = this.wrap.createChild({
24026             tag: 'iframe',
24027             id: this.frameId,
24028             name: this.frameId,
24029             frameBorder : 'no',
24030             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24031         });
24032         
24033        // console.log(iframe);
24034         //this.wrap.dom.appendChild(iframe);
24035
24036         this.iframe = iframe.dom;
24037
24038          this.assignDocWin();
24039         
24040         this.doc.designMode = 'on';
24041        
24042         this.doc.open();
24043         this.doc.write(this.getDocMarkup());
24044         this.doc.close();
24045
24046         
24047         var task = { // must defer to wait for browser to be ready
24048             run : function(){
24049                 //console.log("run task?" + this.doc.readyState);
24050                 this.assignDocWin();
24051                 if(this.doc.body || this.doc.readyState == 'complete'){
24052                     try {
24053                         this.doc.designMode="on";
24054                     } catch (e) {
24055                         return;
24056                     }
24057                     Roo.TaskMgr.stop(task);
24058                     this.initEditor.defer(10, this);
24059                 }
24060             },
24061             interval : 10,
24062             duration:10000,
24063             scope: this
24064         };
24065         Roo.TaskMgr.start(task);
24066
24067         if(!this.width){
24068             this.setSize(this.el.getSize());
24069         }
24070     },
24071
24072     // private
24073     onResize : function(w, h){
24074         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24075         if(this.el && this.iframe){
24076             if(typeof w == 'number'){
24077                 var aw = w - this.wrap.getFrameWidth('lr');
24078                 this.el.setWidth(this.adjustWidth('textarea', aw));
24079                 this.iframe.style.width = aw + 'px';
24080             }
24081             if(typeof h == 'number'){
24082                 var tbh = 0;
24083                 for (var i =0; i < this.toolbars.length;i++) {
24084                     // fixme - ask toolbars for heights?
24085                     tbh += this.toolbars[i].tb.el.getHeight();
24086                 }
24087                 
24088                 
24089                 
24090                 
24091                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24092                 this.el.setHeight(this.adjustWidth('textarea', ah));
24093                 this.iframe.style.height = ah + 'px';
24094                 if(this.doc){
24095                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24096                 }
24097             }
24098         }
24099     },
24100
24101     /**
24102      * Toggles the editor between standard and source edit mode.
24103      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24104      */
24105     toggleSourceEdit : function(sourceEditMode){
24106         
24107         this.sourceEditMode = sourceEditMode === true;
24108         
24109         if(this.sourceEditMode){
24110           
24111             this.syncValue();
24112             this.iframe.className = 'x-hidden';
24113             this.el.removeClass('x-hidden');
24114             this.el.dom.removeAttribute('tabIndex');
24115             this.el.focus();
24116         }else{
24117              
24118             this.pushValue();
24119             this.iframe.className = '';
24120             this.el.addClass('x-hidden');
24121             this.el.dom.setAttribute('tabIndex', -1);
24122             this.deferFocus();
24123         }
24124         this.setSize(this.wrap.getSize());
24125         this.fireEvent('editmodechange', this, this.sourceEditMode);
24126     },
24127
24128     // private used internally
24129     createLink : function(){
24130         var url = prompt(this.createLinkText, this.defaultLinkValue);
24131         if(url && url != 'http:/'+'/'){
24132             this.relayCmd('createlink', url);
24133         }
24134     },
24135
24136     // private (for BoxComponent)
24137     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24138
24139     // private (for BoxComponent)
24140     getResizeEl : function(){
24141         return this.wrap;
24142     },
24143
24144     // private (for BoxComponent)
24145     getPositionEl : function(){
24146         return this.wrap;
24147     },
24148
24149     // private
24150     initEvents : function(){
24151         this.originalValue = this.getValue();
24152     },
24153
24154     /**
24155      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24156      * @method
24157      */
24158     markInvalid : Roo.emptyFn,
24159     /**
24160      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24161      * @method
24162      */
24163     clearInvalid : Roo.emptyFn,
24164
24165     setValue : function(v){
24166         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24167         this.pushValue();
24168     },
24169
24170     /**
24171      * Protected method that will not generally be called directly. If you need/want
24172      * custom HTML cleanup, this is the method you should override.
24173      * @param {String} html The HTML to be cleaned
24174      * return {String} The cleaned HTML
24175      */
24176     cleanHtml : function(html){
24177         html = String(html);
24178         if(html.length > 5){
24179             if(Roo.isSafari){ // strip safari nonsense
24180                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24181             }
24182         }
24183         if(html == '&nbsp;'){
24184             html = '';
24185         }
24186         return html;
24187     },
24188
24189     /**
24190      * Protected method that will not generally be called directly. Syncs the contents
24191      * of the editor iframe with the textarea.
24192      */
24193     syncValue : function(){
24194         if(this.initialized){
24195             var bd = (this.doc.body || this.doc.documentElement);
24196             var html = bd.innerHTML;
24197             if(Roo.isSafari){
24198                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24199                 var m = bs.match(/text-align:(.*?);/i);
24200                 if(m && m[1]){
24201                     html = '<div style="'+m[0]+'">' + html + '</div>';
24202                 }
24203             }
24204             html = this.cleanHtml(html);
24205             if(this.fireEvent('beforesync', this, html) !== false){
24206                 this.el.dom.value = html;
24207                 this.fireEvent('sync', this, html);
24208             }
24209         }
24210     },
24211
24212     /**
24213      * Protected method that will not generally be called directly. Pushes the value of the textarea
24214      * into the iframe editor.
24215      */
24216     pushValue : function(){
24217         if(this.initialized){
24218             var v = this.el.dom.value;
24219             if(v.length < 1){
24220                 v = '&#160;';
24221             }
24222             if(this.fireEvent('beforepush', this, v) !== false){
24223                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24224                 this.fireEvent('push', this, v);
24225             }
24226         }
24227     },
24228
24229     // private
24230     deferFocus : function(){
24231         this.focus.defer(10, this);
24232     },
24233
24234     // doc'ed in Field
24235     focus : function(){
24236         if(this.win && !this.sourceEditMode){
24237             this.win.focus();
24238         }else{
24239             this.el.focus();
24240         }
24241     },
24242     
24243     assignDocWin: function()
24244     {
24245         var iframe = this.iframe;
24246         
24247          if(Roo.isIE){
24248             this.doc = iframe.contentWindow.document;
24249             this.win = iframe.contentWindow;
24250         } else {
24251             if (!Roo.get(this.frameId)) {
24252                 return;
24253             }
24254             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24255             this.win = Roo.get(this.frameId).dom.contentWindow;
24256         }
24257     },
24258     
24259     // private
24260     initEditor : function(){
24261         //console.log("INIT EDITOR");
24262         this.assignDocWin();
24263         
24264         
24265         
24266         this.doc.designMode="on";
24267         this.doc.open();
24268         this.doc.write(this.getDocMarkup());
24269         this.doc.close();
24270         
24271         var dbody = (this.doc.body || this.doc.documentElement);
24272         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24273         // this copies styles from the containing element into thsi one..
24274         // not sure why we need all of this..
24275         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24276         ss['background-attachment'] = 'fixed'; // w3c
24277         dbody.bgProperties = 'fixed'; // ie
24278         Roo.DomHelper.applyStyles(dbody, ss);
24279         Roo.EventManager.on(this.doc, {
24280             'mousedown': this.onEditorEvent,
24281             'dblclick': this.onEditorEvent,
24282             'click': this.onEditorEvent,
24283             'keyup': this.onEditorEvent,
24284             buffer:100,
24285             scope: this
24286         });
24287         if(Roo.isGecko){
24288             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24289         }
24290         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24291             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24292         }
24293         this.initialized = true;
24294
24295         this.fireEvent('initialize', this);
24296         this.pushValue();
24297     },
24298
24299     // private
24300     onDestroy : function(){
24301         
24302         
24303         
24304         if(this.rendered){
24305             
24306             for (var i =0; i < this.toolbars.length;i++) {
24307                 // fixme - ask toolbars for heights?
24308                 this.toolbars[i].onDestroy();
24309             }
24310             
24311             this.wrap.dom.innerHTML = '';
24312             this.wrap.remove();
24313         }
24314     },
24315
24316     // private
24317     onFirstFocus : function(){
24318         
24319         this.assignDocWin();
24320         
24321         
24322         this.activated = true;
24323         for (var i =0; i < this.toolbars.length;i++) {
24324             this.toolbars[i].onFirstFocus();
24325         }
24326        
24327         if(Roo.isGecko){ // prevent silly gecko errors
24328             this.win.focus();
24329             var s = this.win.getSelection();
24330             if(!s.focusNode || s.focusNode.nodeType != 3){
24331                 var r = s.getRangeAt(0);
24332                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24333                 r.collapse(true);
24334                 this.deferFocus();
24335             }
24336             try{
24337                 this.execCmd('useCSS', true);
24338                 this.execCmd('styleWithCSS', false);
24339             }catch(e){}
24340         }
24341         this.fireEvent('activate', this);
24342     },
24343
24344     // private
24345     adjustFont: function(btn){
24346         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24347         //if(Roo.isSafari){ // safari
24348         //    adjust *= 2;
24349        // }
24350         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24351         if(Roo.isSafari){ // safari
24352             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24353             v =  (v < 10) ? 10 : v;
24354             v =  (v > 48) ? 48 : v;
24355             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24356             
24357         }
24358         
24359         
24360         v = Math.max(1, v+adjust);
24361         
24362         this.execCmd('FontSize', v  );
24363     },
24364
24365     onEditorEvent : function(e){
24366         this.fireEvent('editorevent', this, e);
24367       //  this.updateToolbar();
24368         this.syncValue();
24369     },
24370
24371     insertTag : function(tg)
24372     {
24373         // could be a bit smarter... -> wrap the current selected tRoo..
24374         
24375         this.execCmd("formatblock",   tg);
24376         
24377     },
24378     
24379     insertText : function(txt)
24380     {
24381         
24382         
24383         range = this.createRange();
24384         range.deleteContents();
24385                //alert(Sender.getAttribute('label'));
24386                
24387         range.insertNode(this.doc.createTextNode(txt));
24388     } ,
24389     
24390     // private
24391     relayBtnCmd : function(btn){
24392         this.relayCmd(btn.cmd);
24393     },
24394
24395     /**
24396      * Executes a Midas editor command on the editor document and performs necessary focus and
24397      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24398      * @param {String} cmd The Midas command
24399      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24400      */
24401     relayCmd : function(cmd, value){
24402         this.win.focus();
24403         this.execCmd(cmd, value);
24404         this.fireEvent('editorevent', this);
24405         //this.updateToolbar();
24406         this.deferFocus();
24407     },
24408
24409     /**
24410      * Executes a Midas editor command directly on the editor document.
24411      * For visual commands, you should use {@link #relayCmd} instead.
24412      * <b>This should only be called after the editor is initialized.</b>
24413      * @param {String} cmd The Midas command
24414      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24415      */
24416     execCmd : function(cmd, value){
24417         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24418         this.syncValue();
24419     },
24420
24421    
24422     /**
24423      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24424      * to insert tRoo.
24425      * @param {String} text
24426      */
24427     insertAtCursor : function(text){
24428         if(!this.activated){
24429             return;
24430         }
24431         if(Roo.isIE){
24432             this.win.focus();
24433             var r = this.doc.selection.createRange();
24434             if(r){
24435                 r.collapse(true);
24436                 r.pasteHTML(text);
24437                 this.syncValue();
24438                 this.deferFocus();
24439             }
24440         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24441             this.win.focus();
24442             this.execCmd('InsertHTML', text);
24443             this.deferFocus();
24444         }
24445     },
24446  // private
24447     mozKeyPress : function(e){
24448         if(e.ctrlKey){
24449             var c = e.getCharCode(), cmd;
24450           
24451             if(c > 0){
24452                 c = String.fromCharCode(c).toLowerCase();
24453                 switch(c){
24454                     case 'b':
24455                         cmd = 'bold';
24456                     break;
24457                     case 'i':
24458                         cmd = 'italic';
24459                     break;
24460                     case 'u':
24461                         cmd = 'underline';
24462                     case 'v':
24463                         this.cleanUpPaste.defer(100, this);
24464                         return;
24465                     break;
24466                 }
24467                 if(cmd){
24468                     this.win.focus();
24469                     this.execCmd(cmd);
24470                     this.deferFocus();
24471                     e.preventDefault();
24472                 }
24473                 
24474             }
24475         }
24476     },
24477
24478     // private
24479     fixKeys : function(){ // load time branching for fastest keydown performance
24480         if(Roo.isIE){
24481             return function(e){
24482                 var k = e.getKey(), r;
24483                 if(k == e.TAB){
24484                     e.stopEvent();
24485                     r = this.doc.selection.createRange();
24486                     if(r){
24487                         r.collapse(true);
24488                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24489                         this.deferFocus();
24490                     }
24491                     return;
24492                 }
24493                 
24494                 if(k == e.ENTER){
24495                     r = this.doc.selection.createRange();
24496                     if(r){
24497                         var target = r.parentElement();
24498                         if(!target || target.tagName.toLowerCase() != 'li'){
24499                             e.stopEvent();
24500                             r.pasteHTML('<br />');
24501                             r.collapse(false);
24502                             r.select();
24503                         }
24504                     }
24505                 }
24506                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24507                     this.cleanUpPaste.defer(100, this);
24508                     return;
24509                 }
24510                 
24511                 
24512             };
24513         }else if(Roo.isOpera){
24514             return function(e){
24515                 var k = e.getKey();
24516                 if(k == e.TAB){
24517                     e.stopEvent();
24518                     this.win.focus();
24519                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24520                     this.deferFocus();
24521                 }
24522                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24523                     this.cleanUpPaste.defer(100, this);
24524                     return;
24525                 }
24526                 
24527             };
24528         }else if(Roo.isSafari){
24529             return function(e){
24530                 var k = e.getKey();
24531                 
24532                 if(k == e.TAB){
24533                     e.stopEvent();
24534                     this.execCmd('InsertText','\t');
24535                     this.deferFocus();
24536                     return;
24537                 }
24538                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24539                     this.cleanUpPaste.defer(100, this);
24540                     return;
24541                 }
24542                 
24543              };
24544         }
24545     }(),
24546     
24547     getAllAncestors: function()
24548     {
24549         var p = this.getSelectedNode();
24550         var a = [];
24551         if (!p) {
24552             a.push(p); // push blank onto stack..
24553             p = this.getParentElement();
24554         }
24555         
24556         
24557         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24558             a.push(p);
24559             p = p.parentNode;
24560         }
24561         a.push(this.doc.body);
24562         return a;
24563     },
24564     lastSel : false,
24565     lastSelNode : false,
24566     
24567     
24568     getSelection : function() 
24569     {
24570         this.assignDocWin();
24571         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24572     },
24573     
24574     getSelectedNode: function() 
24575     {
24576         // this may only work on Gecko!!!
24577         
24578         // should we cache this!!!!
24579         
24580         
24581         
24582          
24583         var range = this.createRange(this.getSelection());
24584         
24585         if (Roo.isIE) {
24586             var parent = range.parentElement();
24587             while (true) {
24588                 var testRange = range.duplicate();
24589                 testRange.moveToElementText(parent);
24590                 if (testRange.inRange(range)) {
24591                     break;
24592                 }
24593                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24594                     break;
24595                 }
24596                 parent = parent.parentElement;
24597             }
24598             return parent;
24599         }
24600         
24601         
24602         var ar = range.endContainer.childNodes;
24603         if (!ar.length) {
24604             ar = range.commonAncestorContainer.childNodes;
24605             //alert(ar.length);
24606         }
24607         var nodes = [];
24608         var other_nodes = [];
24609         var has_other_nodes = false;
24610         for (var i=0;i<ar.length;i++) {
24611             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24612                 continue;
24613             }
24614             // fullly contained node.
24615             
24616             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24617                 nodes.push(ar[i]);
24618                 continue;
24619             }
24620             
24621             // probably selected..
24622             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24623                 other_nodes.push(ar[i]);
24624                 continue;
24625             }
24626             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24627                 continue;
24628             }
24629             
24630             
24631             has_other_nodes = true;
24632         }
24633         if (!nodes.length && other_nodes.length) {
24634             nodes= other_nodes;
24635         }
24636         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24637             return false;
24638         }
24639         
24640         return nodes[0];
24641     },
24642     createRange: function(sel)
24643     {
24644         // this has strange effects when using with 
24645         // top toolbar - not sure if it's a great idea.
24646         //this.editor.contentWindow.focus();
24647         if (typeof sel != "undefined") {
24648             try {
24649                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24650             } catch(e) {
24651                 return this.doc.createRange();
24652             }
24653         } else {
24654             return this.doc.createRange();
24655         }
24656     },
24657     getParentElement: function()
24658     {
24659         
24660         this.assignDocWin();
24661         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24662         
24663         var range = this.createRange(sel);
24664          
24665         try {
24666             var p = range.commonAncestorContainer;
24667             while (p.nodeType == 3) { // text node
24668                 p = p.parentNode;
24669             }
24670             return p;
24671         } catch (e) {
24672             return null;
24673         }
24674     
24675     },
24676     
24677     
24678     
24679     // BC Hacks - cause I cant work out what i was trying to do..
24680     rangeIntersectsNode : function(range, node)
24681     {
24682         var nodeRange = node.ownerDocument.createRange();
24683         try {
24684             nodeRange.selectNode(node);
24685         }
24686         catch (e) {
24687             nodeRange.selectNodeContents(node);
24688         }
24689
24690         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24691                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24692     },
24693     rangeCompareNode : function(range, node) {
24694         var nodeRange = node.ownerDocument.createRange();
24695         try {
24696             nodeRange.selectNode(node);
24697         } catch (e) {
24698             nodeRange.selectNodeContents(node);
24699         }
24700         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24701         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24702
24703         if (nodeIsBefore && !nodeIsAfter)
24704             return 0;
24705         if (!nodeIsBefore && nodeIsAfter)
24706             return 1;
24707         if (nodeIsBefore && nodeIsAfter)
24708             return 2;
24709
24710         return 3;
24711     },
24712
24713     // private? - in a new class?
24714     cleanUpPaste :  function()
24715     {
24716         // cleans up the whole document..
24717       //  console.log('cleanuppaste');
24718         this.cleanUpChildren(this.doc.body)
24719         
24720         
24721     },
24722     cleanUpChildren : function (n)
24723     {
24724         if (!n.childNodes.length) {
24725             return;
24726         }
24727         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24728            this.cleanUpChild(n.childNodes[i]);
24729         }
24730     },
24731     
24732     
24733         
24734     
24735     cleanUpChild : function (node)
24736     {
24737         //console.log(node);
24738         if (node.nodeName == "#text") {
24739             // clean up silly Windows -- stuff?
24740             return; 
24741         }
24742         if (node.nodeName == "#comment") {
24743             node.parentNode.removeChild(node);
24744             // clean up silly Windows -- stuff?
24745             return; 
24746         }
24747         
24748         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24749             // remove node.
24750             node.parentNode.removeChild(node);
24751             return;
24752             
24753         }
24754         if (!node.attributes || !node.attributes.length) {
24755             this.cleanUpChildren(node);
24756             return;
24757         }
24758         
24759         function cleanAttr(n,v)
24760         {
24761             
24762             if (v.match(/^\./) || v.match(/^\//)) {
24763                 return;
24764             }
24765             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24766                 return;
24767             }
24768             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24769             node.removeAttribute(n);
24770             
24771         }
24772         
24773         function cleanStyle(n,v)
24774         {
24775             if (v.match(/expression/)) { //XSS?? should we even bother..
24776                 node.removeAttribute(n);
24777                 return;
24778             }
24779             
24780             
24781             var parts = v.split(/;/);
24782             Roo.each(parts, function(p) {
24783                 p = p.replace(/\s+/g,'');
24784                 if (!p.length) {
24785                     return;
24786                 }
24787                 var l = p.split(':').shift().replace(/\s+/g,'');
24788                 
24789                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24790                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24791                     node.removeAttribute(n);
24792                     return false;
24793                 }
24794             });
24795             
24796             
24797         }
24798         
24799         
24800         for (var i = node.attributes.length-1; i > -1 ; i--) {
24801             var a = node.attributes[i];
24802             //console.log(a);
24803             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24804                 node.removeAttribute(a.name);
24805                 return;
24806             }
24807             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24808                 cleanAttr(a.name,a.value); // fixme..
24809                 return;
24810             }
24811             if (a.name == 'style') {
24812                 cleanStyle(a.name,a.value);
24813             }
24814             /// clean up MS crap..
24815             if (a.name == 'class') {
24816                 if (a.value.match(/^Mso/)) {
24817                     node.className = '';
24818                 }
24819             }
24820             
24821             // style cleanup!?
24822             // class cleanup?
24823             
24824         }
24825         
24826         
24827         this.cleanUpChildren(node);
24828         
24829         
24830     }
24831     
24832     
24833     // hide stuff that is not compatible
24834     /**
24835      * @event blur
24836      * @hide
24837      */
24838     /**
24839      * @event change
24840      * @hide
24841      */
24842     /**
24843      * @event focus
24844      * @hide
24845      */
24846     /**
24847      * @event specialkey
24848      * @hide
24849      */
24850     /**
24851      * @cfg {String} fieldClass @hide
24852      */
24853     /**
24854      * @cfg {String} focusClass @hide
24855      */
24856     /**
24857      * @cfg {String} autoCreate @hide
24858      */
24859     /**
24860      * @cfg {String} inputType @hide
24861      */
24862     /**
24863      * @cfg {String} invalidClass @hide
24864      */
24865     /**
24866      * @cfg {String} invalidText @hide
24867      */
24868     /**
24869      * @cfg {String} msgFx @hide
24870      */
24871     /**
24872      * @cfg {String} validateOnBlur @hide
24873      */
24874 });
24875
24876 Roo.form.HtmlEditor.white = [
24877         'area', 'br', 'img', 'input', 'hr', 'wbr',
24878         
24879        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24880        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24881        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24882        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24883        'table',   'ul',         'xmp', 
24884        
24885        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24886       'thead',   'tr', 
24887      
24888       'dir', 'menu', 'ol', 'ul', 'dl',
24889        
24890       'embed',  'object'
24891 ];
24892
24893
24894 Roo.form.HtmlEditor.black = [
24895     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24896         'applet', // 
24897         'base',   'basefont', 'bgsound', 'blink',  'body', 
24898         'frame',  'frameset', 'head',    'html',   'ilayer', 
24899         'iframe', 'layer',  'link',     'meta',    'object',   
24900         'script', 'style' ,'title',  'xml' // clean later..
24901 ];
24902 Roo.form.HtmlEditor.clean = [
24903     'script', 'style', 'title', 'xml'
24904 ];
24905
24906 // attributes..
24907
24908 Roo.form.HtmlEditor.ablack = [
24909     'on'
24910 ];
24911     
24912 Roo.form.HtmlEditor.aclean = [ 
24913     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24914 ];
24915
24916 // protocols..
24917 Roo.form.HtmlEditor.pwhite= [
24918         'http',  'https',  'mailto'
24919 ];
24920
24921 Roo.form.HtmlEditor.cwhite= [
24922         'text-align',
24923         'font-size'
24924 ];
24925
24926 // <script type="text/javascript">
24927 /*
24928  * Based on
24929  * Ext JS Library 1.1.1
24930  * Copyright(c) 2006-2007, Ext JS, LLC.
24931  *  
24932  
24933  */
24934
24935 /**
24936  * @class Roo.form.HtmlEditorToolbar1
24937  * Basic Toolbar
24938  * 
24939  * Usage:
24940  *
24941  new Roo.form.HtmlEditor({
24942     ....
24943     toolbars : [
24944         new Roo.form.HtmlEditorToolbar1({
24945             disable : { fonts: 1 , format: 1, ..., ... , ...],
24946             btns : [ .... ]
24947         })
24948     }
24949      
24950  * 
24951  * @cfg {Object} disable List of elements to disable..
24952  * @cfg {Array} btns List of additional buttons.
24953  * 
24954  * 
24955  * NEEDS Extra CSS? 
24956  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24957  */
24958  
24959 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24960 {
24961     
24962     Roo.apply(this, config);
24963     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24964     // dont call parent... till later.
24965 }
24966
24967 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24968     
24969     tb: false,
24970     
24971     rendered: false,
24972     
24973     editor : false,
24974     /**
24975      * @cfg {Object} disable  List of toolbar elements to disable
24976          
24977      */
24978     disable : false,
24979       /**
24980      * @cfg {Array} fontFamilies An array of available font families
24981      */
24982     fontFamilies : [
24983         'Arial',
24984         'Courier New',
24985         'Tahoma',
24986         'Times New Roman',
24987         'Verdana'
24988     ],
24989     
24990     specialChars : [
24991            "&#169;",
24992           "&#174;",     
24993           "&#8482;",    
24994           "&#163;" ,    
24995          // "&#8212;",    
24996           "&#8230;",    
24997           "&#247;" ,    
24998         //  "&#225;" ,     ?? a acute?
24999            "&#8364;"    , //Euro
25000        //   "&#8220;"    ,
25001         //  "&#8221;"    ,
25002         //  "&#8226;"    ,
25003           "&#176;"  //   , // degrees
25004
25005          // "&#233;"     , // e ecute
25006          // "&#250;"     , // u ecute?
25007     ],
25008     inputElements : [ 
25009             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25010             "input:submit", "input:button", "select", "textarea", "label" ],
25011     formats : [
25012         ["p"] ,  
25013         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25014         ["pre"],[ "code"], 
25015         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25016     ],
25017      /**
25018      * @cfg {String} defaultFont default font to use.
25019      */
25020     defaultFont: 'tahoma',
25021    
25022     fontSelect : false,
25023     
25024     
25025     formatCombo : false,
25026     
25027     init : function(editor)
25028     {
25029         this.editor = editor;
25030         
25031         
25032         var fid = editor.frameId;
25033         var etb = this;
25034         function btn(id, toggle, handler){
25035             var xid = fid + '-'+ id ;
25036             return {
25037                 id : xid,
25038                 cmd : id,
25039                 cls : 'x-btn-icon x-edit-'+id,
25040                 enableToggle:toggle !== false,
25041                 scope: editor, // was editor...
25042                 handler:handler||editor.relayBtnCmd,
25043                 clickEvent:'mousedown',
25044                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25045                 tabIndex:-1
25046             };
25047         }
25048         
25049         
25050         
25051         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25052         this.tb = tb;
25053          // stop form submits
25054         tb.el.on('click', function(e){
25055             e.preventDefault(); // what does this do?
25056         });
25057
25058         if(!this.disable.font && !Roo.isSafari){
25059             /* why no safari for fonts
25060             editor.fontSelect = tb.el.createChild({
25061                 tag:'select',
25062                 tabIndex: -1,
25063                 cls:'x-font-select',
25064                 html: editor.createFontOptions()
25065             });
25066             editor.fontSelect.on('change', function(){
25067                 var font = editor.fontSelect.dom.value;
25068                 editor.relayCmd('fontname', font);
25069                 editor.deferFocus();
25070             }, editor);
25071             tb.add(
25072                 editor.fontSelect.dom,
25073                 '-'
25074             );
25075             */
25076         };
25077         if(!this.disable.formats){
25078             this.formatCombo = new Roo.form.ComboBox({
25079                 store: new Roo.data.SimpleStore({
25080                     id : 'tag',
25081                     fields: ['tag'],
25082                     data : this.formats // from states.js
25083                 }),
25084                 blockFocus : true,
25085                 //autoCreate : {tag: "div",  size: "20"},
25086                 displayField:'tag',
25087                 typeAhead: false,
25088                 mode: 'local',
25089                 editable : false,
25090                 triggerAction: 'all',
25091                 emptyText:'Add tag',
25092                 selectOnFocus:true,
25093                 width:135,
25094                 listeners : {
25095                     'select': function(c, r, i) {
25096                         editor.insertTag(r.get('tag'));
25097                         editor.focus();
25098                     }
25099                 }
25100
25101             });
25102             tb.addField(this.formatCombo);
25103             
25104         }
25105         
25106         if(!this.disable.format){
25107             tb.add(
25108                 btn('bold'),
25109                 btn('italic'),
25110                 btn('underline')
25111             );
25112         };
25113         if(!this.disable.fontSize){
25114             tb.add(
25115                 '-',
25116                 
25117                 
25118                 btn('increasefontsize', false, editor.adjustFont),
25119                 btn('decreasefontsize', false, editor.adjustFont)
25120             );
25121         };
25122         
25123         
25124         if(this.disable.colors){
25125             tb.add(
25126                 '-', {
25127                     id:editor.frameId +'-forecolor',
25128                     cls:'x-btn-icon x-edit-forecolor',
25129                     clickEvent:'mousedown',
25130                     tooltip: this.buttonTips['forecolor'] || undefined,
25131                     tabIndex:-1,
25132                     menu : new Roo.menu.ColorMenu({
25133                         allowReselect: true,
25134                         focus: Roo.emptyFn,
25135                         value:'000000',
25136                         plain:true,
25137                         selectHandler: function(cp, color){
25138                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25139                             editor.deferFocus();
25140                         },
25141                         scope: editor,
25142                         clickEvent:'mousedown'
25143                     })
25144                 }, {
25145                     id:editor.frameId +'backcolor',
25146                     cls:'x-btn-icon x-edit-backcolor',
25147                     clickEvent:'mousedown',
25148                     tooltip: this.buttonTips['backcolor'] || undefined,
25149                     tabIndex:-1,
25150                     menu : new Roo.menu.ColorMenu({
25151                         focus: Roo.emptyFn,
25152                         value:'FFFFFF',
25153                         plain:true,
25154                         allowReselect: true,
25155                         selectHandler: function(cp, color){
25156                             if(Roo.isGecko){
25157                                 editor.execCmd('useCSS', false);
25158                                 editor.execCmd('hilitecolor', color);
25159                                 editor.execCmd('useCSS', true);
25160                                 editor.deferFocus();
25161                             }else{
25162                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25163                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25164                                 editor.deferFocus();
25165                             }
25166                         },
25167                         scope:editor,
25168                         clickEvent:'mousedown'
25169                     })
25170                 }
25171             );
25172         };
25173         // now add all the items...
25174         
25175
25176         if(!this.disable.alignments){
25177             tb.add(
25178                 '-',
25179                 btn('justifyleft'),
25180                 btn('justifycenter'),
25181                 btn('justifyright')
25182             );
25183         };
25184
25185         //if(!Roo.isSafari){
25186             if(!this.disable.links){
25187                 tb.add(
25188                     '-',
25189                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25190                 );
25191             };
25192
25193             if(!this.disable.lists){
25194                 tb.add(
25195                     '-',
25196                     btn('insertorderedlist'),
25197                     btn('insertunorderedlist')
25198                 );
25199             }
25200             if(!this.disable.sourceEdit){
25201                 tb.add(
25202                     '-',
25203                     btn('sourceedit', true, function(btn){
25204                         this.toggleSourceEdit(btn.pressed);
25205                     })
25206                 );
25207             }
25208         //}
25209         
25210         var smenu = { };
25211         // special menu.. - needs to be tidied up..
25212         if (!this.disable.special) {
25213             smenu = {
25214                 text: "&#169;",
25215                 cls: 'x-edit-none',
25216                 menu : {
25217                     items : []
25218                    }
25219             };
25220             for (var i =0; i < this.specialChars.length; i++) {
25221                 smenu.menu.items.push({
25222                     
25223                     html: this.specialChars[i],
25224                     handler: function(a,b) {
25225                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25226                         
25227                     },
25228                     tabIndex:-1
25229                 });
25230             }
25231             
25232             
25233             tb.add(smenu);
25234             
25235             
25236         }
25237         if (this.btns) {
25238             for(var i =0; i< this.btns.length;i++) {
25239                 var b = this.btns[i];
25240                 b.cls =  'x-edit-none';
25241                 b.scope = editor;
25242                 tb.add(b);
25243             }
25244         
25245         }
25246         
25247         
25248         
25249         // disable everything...
25250         
25251         this.tb.items.each(function(item){
25252            if(item.id != editor.frameId+ '-sourceedit'){
25253                 item.disable();
25254             }
25255         });
25256         this.rendered = true;
25257         
25258         // the all the btns;
25259         editor.on('editorevent', this.updateToolbar, this);
25260         // other toolbars need to implement this..
25261         //editor.on('editmodechange', this.updateToolbar, this);
25262     },
25263     
25264     
25265     
25266     /**
25267      * Protected method that will not generally be called directly. It triggers
25268      * a toolbar update by reading the markup state of the current selection in the editor.
25269      */
25270     updateToolbar: function(){
25271
25272         if(!this.editor.activated){
25273             this.editor.onFirstFocus();
25274             return;
25275         }
25276
25277         var btns = this.tb.items.map, 
25278             doc = this.editor.doc,
25279             frameId = this.editor.frameId;
25280
25281         if(!this.disable.font && !Roo.isSafari){
25282             /*
25283             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25284             if(name != this.fontSelect.dom.value){
25285                 this.fontSelect.dom.value = name;
25286             }
25287             */
25288         }
25289         if(!this.disable.format){
25290             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25291             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25292             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25293         }
25294         if(!this.disable.alignments){
25295             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25296             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25297             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25298         }
25299         if(!Roo.isSafari && !this.disable.lists){
25300             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25301             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25302         }
25303         
25304         var ans = this.editor.getAllAncestors();
25305         if (this.formatCombo) {
25306             
25307             
25308             var store = this.formatCombo.store;
25309             this.formatCombo.setValue("");
25310             for (var i =0; i < ans.length;i++) {
25311                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25312                     // select it..
25313                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25314                     break;
25315                 }
25316             }
25317         }
25318         
25319         
25320         
25321         // hides menus... - so this cant be on a menu...
25322         Roo.menu.MenuMgr.hideAll();
25323
25324         //this.editorsyncValue();
25325     },
25326    
25327     
25328     createFontOptions : function(){
25329         var buf = [], fs = this.fontFamilies, ff, lc;
25330         for(var i = 0, len = fs.length; i< len; i++){
25331             ff = fs[i];
25332             lc = ff.toLowerCase();
25333             buf.push(
25334                 '<option value="',lc,'" style="font-family:',ff,';"',
25335                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25336                     ff,
25337                 '</option>'
25338             );
25339         }
25340         return buf.join('');
25341     },
25342     
25343     toggleSourceEdit : function(sourceEditMode){
25344         if(sourceEditMode === undefined){
25345             sourceEditMode = !this.sourceEditMode;
25346         }
25347         this.sourceEditMode = sourceEditMode === true;
25348         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25349         // just toggle the button?
25350         if(btn.pressed !== this.editor.sourceEditMode){
25351             btn.toggle(this.editor.sourceEditMode);
25352             return;
25353         }
25354         
25355         if(this.sourceEditMode){
25356             this.tb.items.each(function(item){
25357                 if(item.cmd != 'sourceedit'){
25358                     item.disable();
25359                 }
25360             });
25361           
25362         }else{
25363             if(this.initialized){
25364                 this.tb.items.each(function(item){
25365                     item.enable();
25366                 });
25367             }
25368             
25369         }
25370         // tell the editor that it's been pressed..
25371         this.editor.toggleSourceEdit(sourceEditMode);
25372        
25373     },
25374      /**
25375      * Object collection of toolbar tooltips for the buttons in the editor. The key
25376      * is the command id associated with that button and the value is a valid QuickTips object.
25377      * For example:
25378 <pre><code>
25379 {
25380     bold : {
25381         title: 'Bold (Ctrl+B)',
25382         text: 'Make the selected text bold.',
25383         cls: 'x-html-editor-tip'
25384     },
25385     italic : {
25386         title: 'Italic (Ctrl+I)',
25387         text: 'Make the selected text italic.',
25388         cls: 'x-html-editor-tip'
25389     },
25390     ...
25391 </code></pre>
25392     * @type Object
25393      */
25394     buttonTips : {
25395         bold : {
25396             title: 'Bold (Ctrl+B)',
25397             text: 'Make the selected text bold.',
25398             cls: 'x-html-editor-tip'
25399         },
25400         italic : {
25401             title: 'Italic (Ctrl+I)',
25402             text: 'Make the selected text italic.',
25403             cls: 'x-html-editor-tip'
25404         },
25405         underline : {
25406             title: 'Underline (Ctrl+U)',
25407             text: 'Underline the selected text.',
25408             cls: 'x-html-editor-tip'
25409         },
25410         increasefontsize : {
25411             title: 'Grow Text',
25412             text: 'Increase the font size.',
25413             cls: 'x-html-editor-tip'
25414         },
25415         decreasefontsize : {
25416             title: 'Shrink Text',
25417             text: 'Decrease the font size.',
25418             cls: 'x-html-editor-tip'
25419         },
25420         backcolor : {
25421             title: 'Text Highlight Color',
25422             text: 'Change the background color of the selected text.',
25423             cls: 'x-html-editor-tip'
25424         },
25425         forecolor : {
25426             title: 'Font Color',
25427             text: 'Change the color of the selected text.',
25428             cls: 'x-html-editor-tip'
25429         },
25430         justifyleft : {
25431             title: 'Align Text Left',
25432             text: 'Align text to the left.',
25433             cls: 'x-html-editor-tip'
25434         },
25435         justifycenter : {
25436             title: 'Center Text',
25437             text: 'Center text in the editor.',
25438             cls: 'x-html-editor-tip'
25439         },
25440         justifyright : {
25441             title: 'Align Text Right',
25442             text: 'Align text to the right.',
25443             cls: 'x-html-editor-tip'
25444         },
25445         insertunorderedlist : {
25446             title: 'Bullet List',
25447             text: 'Start a bulleted list.',
25448             cls: 'x-html-editor-tip'
25449         },
25450         insertorderedlist : {
25451             title: 'Numbered List',
25452             text: 'Start a numbered list.',
25453             cls: 'x-html-editor-tip'
25454         },
25455         createlink : {
25456             title: 'Hyperlink',
25457             text: 'Make the selected text a hyperlink.',
25458             cls: 'x-html-editor-tip'
25459         },
25460         sourceedit : {
25461             title: 'Source Edit',
25462             text: 'Switch to source editing mode.',
25463             cls: 'x-html-editor-tip'
25464         }
25465     },
25466     // private
25467     onDestroy : function(){
25468         if(this.rendered){
25469             
25470             this.tb.items.each(function(item){
25471                 if(item.menu){
25472                     item.menu.removeAll();
25473                     if(item.menu.el){
25474                         item.menu.el.destroy();
25475                     }
25476                 }
25477                 item.destroy();
25478             });
25479              
25480         }
25481     },
25482     onFirstFocus: function() {
25483         this.tb.items.each(function(item){
25484            item.enable();
25485         });
25486     }
25487 });
25488
25489
25490
25491
25492 // <script type="text/javascript">
25493 /*
25494  * Based on
25495  * Ext JS Library 1.1.1
25496  * Copyright(c) 2006-2007, Ext JS, LLC.
25497  *  
25498  
25499  */
25500
25501  
25502 /**
25503  * @class Roo.form.HtmlEditor.ToolbarContext
25504  * Context Toolbar
25505  * 
25506  * Usage:
25507  *
25508  new Roo.form.HtmlEditor({
25509     ....
25510     toolbars : [
25511         new Roo.form.HtmlEditor.ToolbarStandard(),
25512         new Roo.form.HtmlEditor.ToolbarContext()
25513         })
25514     }
25515      
25516  * 
25517  * @config : {Object} disable List of elements to disable.. (not done yet.)
25518  * 
25519  * 
25520  */
25521
25522 Roo.form.HtmlEditor.ToolbarContext = function(config)
25523 {
25524     
25525     Roo.apply(this, config);
25526     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25527     // dont call parent... till later.
25528 }
25529 Roo.form.HtmlEditor.ToolbarContext.types = {
25530     'IMG' : {
25531         width : {
25532             title: "Width",
25533             width: 40
25534         },
25535         height:  {
25536             title: "Height",
25537             width: 40
25538         },
25539         align: {
25540             title: "Align",
25541             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25542             width : 80
25543             
25544         },
25545         border: {
25546             title: "Border",
25547             width: 40
25548         },
25549         alt: {
25550             title: "Alt",
25551             width: 120
25552         },
25553         src : {
25554             title: "Src",
25555             width: 220
25556         }
25557         
25558     },
25559     'A' : {
25560         name : {
25561             title: "Name",
25562             width: 50
25563         },
25564         href:  {
25565             title: "Href",
25566             width: 220
25567         } // border?
25568         
25569     },
25570     'TABLE' : {
25571         rows : {
25572             title: "Rows",
25573             width: 20
25574         },
25575         cols : {
25576             title: "Cols",
25577             width: 20
25578         },
25579         width : {
25580             title: "Width",
25581             width: 40
25582         },
25583         height : {
25584             title: "Height",
25585             width: 40
25586         },
25587         border : {
25588             title: "Border",
25589             width: 20
25590         }
25591     },
25592     'TD' : {
25593         width : {
25594             title: "Width",
25595             width: 40
25596         },
25597         height : {
25598             title: "Height",
25599             width: 40
25600         },   
25601         align: {
25602             title: "Align",
25603             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25604             width: 40
25605         },
25606         valign: {
25607             title: "Valign",
25608             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25609             width: 40
25610         },
25611         colspan: {
25612             title: "Colspan",
25613             width: 20
25614             
25615         }
25616     },
25617     'INPUT' : {
25618         name : {
25619             title: "name",
25620             width: 120
25621         },
25622         value : {
25623             title: "Value",
25624             width: 120
25625         },
25626         width : {
25627             title: "Width",
25628             width: 40
25629         }
25630     },
25631     'LABEL' : {
25632         'for' : {
25633             title: "For",
25634             width: 120
25635         }
25636     },
25637     'TEXTAREA' : {
25638           name : {
25639             title: "name",
25640             width: 120
25641         },
25642         rows : {
25643             title: "Rows",
25644             width: 20
25645         },
25646         cols : {
25647             title: "Cols",
25648             width: 20
25649         }
25650     },
25651     'SELECT' : {
25652         name : {
25653             title: "name",
25654             width: 120
25655         },
25656         selectoptions : {
25657             title: "Options",
25658             width: 200
25659         }
25660     },
25661     'BODY' : {
25662         title : {
25663             title: "title",
25664             width: 120,
25665             disabled : true
25666         }
25667     }
25668 };
25669
25670
25671
25672 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25673     
25674     tb: false,
25675     
25676     rendered: false,
25677     
25678     editor : false,
25679     /**
25680      * @cfg {Object} disable  List of toolbar elements to disable
25681          
25682      */
25683     disable : false,
25684     
25685     
25686     
25687     toolbars : false,
25688     
25689     init : function(editor)
25690     {
25691         this.editor = editor;
25692         
25693         
25694         var fid = editor.frameId;
25695         var etb = this;
25696         function btn(id, toggle, handler){
25697             var xid = fid + '-'+ id ;
25698             return {
25699                 id : xid,
25700                 cmd : id,
25701                 cls : 'x-btn-icon x-edit-'+id,
25702                 enableToggle:toggle !== false,
25703                 scope: editor, // was editor...
25704                 handler:handler||editor.relayBtnCmd,
25705                 clickEvent:'mousedown',
25706                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25707                 tabIndex:-1
25708             };
25709         }
25710         // create a new element.
25711         var wdiv = editor.wrap.createChild({
25712                 tag: 'div'
25713             }, editor.wrap.dom.firstChild.nextSibling, true);
25714         
25715         // can we do this more than once??
25716         
25717          // stop form submits
25718       
25719  
25720         // disable everything...
25721         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25722         this.toolbars = {};
25723            
25724         for (var i in  ty) {
25725           
25726             this.toolbars[i] = this.buildToolbar(ty[i],i);
25727         }
25728         this.tb = this.toolbars.BODY;
25729         this.tb.el.show();
25730         
25731          
25732         this.rendered = true;
25733         
25734         // the all the btns;
25735         editor.on('editorevent', this.updateToolbar, this);
25736         // other toolbars need to implement this..
25737         //editor.on('editmodechange', this.updateToolbar, this);
25738     },
25739     
25740     
25741     
25742     /**
25743      * Protected method that will not generally be called directly. It triggers
25744      * a toolbar update by reading the markup state of the current selection in the editor.
25745      */
25746     updateToolbar: function(){
25747
25748         if(!this.editor.activated){
25749             this.editor.onFirstFocus();
25750             return;
25751         }
25752
25753         
25754         var ans = this.editor.getAllAncestors();
25755         
25756         // pick
25757         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25758         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25759         sel = sel ? sel : this.editor.doc.body;
25760         sel = sel.tagName.length ? sel : this.editor.doc.body;
25761         var tn = sel.tagName.toUpperCase();
25762         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25763         tn = sel.tagName.toUpperCase();
25764         if (this.tb.name  == tn) {
25765             return; // no change
25766         }
25767         this.tb.el.hide();
25768         ///console.log("show: " + tn);
25769         this.tb =  this.toolbars[tn];
25770         this.tb.el.show();
25771         this.tb.fields.each(function(e) {
25772             e.setValue(sel.getAttribute(e.name));
25773         });
25774         this.tb.selectedNode = sel;
25775         
25776         
25777         Roo.menu.MenuMgr.hideAll();
25778
25779         //this.editorsyncValue();
25780     },
25781    
25782        
25783     // private
25784     onDestroy : function(){
25785         if(this.rendered){
25786             
25787             this.tb.items.each(function(item){
25788                 if(item.menu){
25789                     item.menu.removeAll();
25790                     if(item.menu.el){
25791                         item.menu.el.destroy();
25792                     }
25793                 }
25794                 item.destroy();
25795             });
25796              
25797         }
25798     },
25799     onFirstFocus: function() {
25800         // need to do this for all the toolbars..
25801         this.tb.items.each(function(item){
25802            item.enable();
25803         });
25804     },
25805     buildToolbar: function(tlist, nm)
25806     {
25807         var editor = this.editor;
25808          // create a new element.
25809         var wdiv = editor.wrap.createChild({
25810                 tag: 'div'
25811             }, editor.wrap.dom.firstChild.nextSibling, true);
25812         
25813        
25814         var tb = new Roo.Toolbar(wdiv);
25815         tb.add(nm+ ":&nbsp;");
25816         for (var i in tlist) {
25817             var item = tlist[i];
25818             tb.add(item.title + ":&nbsp;");
25819             if (item.opts) {
25820                 // fixme
25821                 
25822               
25823                 tb.addField( new Roo.form.ComboBox({
25824                     store: new Roo.data.SimpleStore({
25825                         id : 'val',
25826                         fields: ['val'],
25827                         data : item.opts // from states.js
25828                     }),
25829                     name : i,
25830                     displayField:'val',
25831                     typeAhead: false,
25832                     mode: 'local',
25833                     editable : false,
25834                     triggerAction: 'all',
25835                     emptyText:'Select',
25836                     selectOnFocus:true,
25837                     width: item.width ? item.width  : 130,
25838                     listeners : {
25839                         'select': function(c, r, i) {
25840                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25841                         }
25842                     }
25843
25844                 }));
25845                 continue;
25846                     
25847                 
25848                 
25849                 
25850                 
25851                 tb.addField( new Roo.form.TextField({
25852                     name: i,
25853                     width: 100,
25854                     //allowBlank:false,
25855                     value: ''
25856                 }));
25857                 continue;
25858             }
25859             tb.addField( new Roo.form.TextField({
25860                 name: i,
25861                 width: item.width,
25862                 //allowBlank:true,
25863                 value: '',
25864                 listeners: {
25865                     'change' : function(f, nv, ov) {
25866                         tb.selectedNode.setAttribute(f.name, nv);
25867                     }
25868                 }
25869             }));
25870              
25871         }
25872         tb.el.on('click', function(e){
25873             e.preventDefault(); // what does this do?
25874         });
25875         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25876         tb.el.hide();
25877         tb.name = nm;
25878         // dont need to disable them... as they will get hidden
25879         return tb;
25880          
25881         
25882     }
25883     
25884     
25885     
25886     
25887 });
25888
25889
25890
25891
25892
25893 /*
25894  * Based on:
25895  * Ext JS Library 1.1.1
25896  * Copyright(c) 2006-2007, Ext JS, LLC.
25897  *
25898  * Originally Released Under LGPL - original licence link has changed is not relivant.
25899  *
25900  * Fork - LGPL
25901  * <script type="text/javascript">
25902  */
25903  
25904 /**
25905  * @class Roo.form.BasicForm
25906  * @extends Roo.util.Observable
25907  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25908  * @constructor
25909  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25910  * @param {Object} config Configuration options
25911  */
25912 Roo.form.BasicForm = function(el, config){
25913     this.allItems = [];
25914     this.childForms = [];
25915     Roo.apply(this, config);
25916     /*
25917      * The Roo.form.Field items in this form.
25918      * @type MixedCollection
25919      */
25920      
25921      
25922     this.items = new Roo.util.MixedCollection(false, function(o){
25923         return o.id || (o.id = Roo.id());
25924     });
25925     this.addEvents({
25926         /**
25927          * @event beforeaction
25928          * Fires before any action is performed. Return false to cancel the action.
25929          * @param {Form} this
25930          * @param {Action} action The action to be performed
25931          */
25932         beforeaction: true,
25933         /**
25934          * @event actionfailed
25935          * Fires when an action fails.
25936          * @param {Form} this
25937          * @param {Action} action The action that failed
25938          */
25939         actionfailed : true,
25940         /**
25941          * @event actioncomplete
25942          * Fires when an action is completed.
25943          * @param {Form} this
25944          * @param {Action} action The action that completed
25945          */
25946         actioncomplete : true
25947     });
25948     if(el){
25949         this.initEl(el);
25950     }
25951     Roo.form.BasicForm.superclass.constructor.call(this);
25952 };
25953
25954 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25955     /**
25956      * @cfg {String} method
25957      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25958      */
25959     /**
25960      * @cfg {DataReader} reader
25961      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25962      * This is optional as there is built-in support for processing JSON.
25963      */
25964     /**
25965      * @cfg {DataReader} errorReader
25966      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25967      * This is completely optional as there is built-in support for processing JSON.
25968      */
25969     /**
25970      * @cfg {String} url
25971      * The URL to use for form actions if one isn't supplied in the action options.
25972      */
25973     /**
25974      * @cfg {Boolean} fileUpload
25975      * Set to true if this form is a file upload.
25976      */
25977     /**
25978      * @cfg {Object} baseParams
25979      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25980      */
25981     /**
25982      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25983      */
25984     timeout: 30,
25985
25986     // private
25987     activeAction : null,
25988
25989     /**
25990      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25991      * or setValues() data instead of when the form was first created.
25992      */
25993     trackResetOnLoad : false,
25994     
25995     
25996     /**
25997      * childForms - used for multi-tab forms
25998      * @type {Array}
25999      */
26000     childForms : false,
26001     
26002     /**
26003      * allItems - full list of fields.
26004      * @type {Array}
26005      */
26006     allItems : false,
26007     
26008     /**
26009      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26010      * element by passing it or its id or mask the form itself by passing in true.
26011      * @type Mixed
26012      */
26013     waitMsgTarget : undefined,
26014
26015     // private
26016     initEl : function(el){
26017         this.el = Roo.get(el);
26018         this.id = this.el.id || Roo.id();
26019         this.el.on('submit', this.onSubmit, this);
26020         this.el.addClass('x-form');
26021     },
26022
26023     // private
26024     onSubmit : function(e){
26025         e.stopEvent();
26026     },
26027
26028     /**
26029      * Returns true if client-side validation on the form is successful.
26030      * @return Boolean
26031      */
26032     isValid : function(){
26033         var valid = true;
26034         this.items.each(function(f){
26035            if(!f.validate()){
26036                valid = false;
26037            }
26038         });
26039         return valid;
26040     },
26041
26042     /**
26043      * Returns true if any fields in this form have changed since their original load.
26044      * @return Boolean
26045      */
26046     isDirty : function(){
26047         var dirty = false;
26048         this.items.each(function(f){
26049            if(f.isDirty()){
26050                dirty = true;
26051                return false;
26052            }
26053         });
26054         return dirty;
26055     },
26056
26057     /**
26058      * Performs a predefined action (submit or load) or custom actions you define on this form.
26059      * @param {String} actionName The name of the action type
26060      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26061      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26062      * accept other config options):
26063      * <pre>
26064 Property          Type             Description
26065 ----------------  ---------------  ----------------------------------------------------------------------------------
26066 url               String           The url for the action (defaults to the form's url)
26067 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26068 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26069 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26070                                    validate the form on the client (defaults to false)
26071      * </pre>
26072      * @return {BasicForm} this
26073      */
26074     doAction : function(action, options){
26075         if(typeof action == 'string'){
26076             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26077         }
26078         if(this.fireEvent('beforeaction', this, action) !== false){
26079             this.beforeAction(action);
26080             action.run.defer(100, action);
26081         }
26082         return this;
26083     },
26084
26085     /**
26086      * Shortcut to do a submit action.
26087      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26088      * @return {BasicForm} this
26089      */
26090     submit : function(options){
26091         this.doAction('submit', options);
26092         return this;
26093     },
26094
26095     /**
26096      * Shortcut to do a load action.
26097      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26098      * @return {BasicForm} this
26099      */
26100     load : function(options){
26101         this.doAction('load', options);
26102         return this;
26103     },
26104
26105     /**
26106      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26107      * @param {Record} record The record to edit
26108      * @return {BasicForm} this
26109      */
26110     updateRecord : function(record){
26111         record.beginEdit();
26112         var fs = record.fields;
26113         fs.each(function(f){
26114             var field = this.findField(f.name);
26115             if(field){
26116                 record.set(f.name, field.getValue());
26117             }
26118         }, this);
26119         record.endEdit();
26120         return this;
26121     },
26122
26123     /**
26124      * Loads an Roo.data.Record into this form.
26125      * @param {Record} record The record to load
26126      * @return {BasicForm} this
26127      */
26128     loadRecord : function(record){
26129         this.setValues(record.data);
26130         return this;
26131     },
26132
26133     // private
26134     beforeAction : function(action){
26135         var o = action.options;
26136         if(o.waitMsg){
26137             if(this.waitMsgTarget === true){
26138                 this.el.mask(o.waitMsg, 'x-mask-loading');
26139             }else if(this.waitMsgTarget){
26140                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26141                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26142             }else{
26143                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26144             }
26145         }
26146     },
26147
26148     // private
26149     afterAction : function(action, success){
26150         this.activeAction = null;
26151         var o = action.options;
26152         if(o.waitMsg){
26153             if(this.waitMsgTarget === true){
26154                 this.el.unmask();
26155             }else if(this.waitMsgTarget){
26156                 this.waitMsgTarget.unmask();
26157             }else{
26158                 Roo.MessageBox.updateProgress(1);
26159                 Roo.MessageBox.hide();
26160             }
26161         }
26162         if(success){
26163             if(o.reset){
26164                 this.reset();
26165             }
26166             Roo.callback(o.success, o.scope, [this, action]);
26167             this.fireEvent('actioncomplete', this, action);
26168         }else{
26169             Roo.callback(o.failure, o.scope, [this, action]);
26170             this.fireEvent('actionfailed', this, action);
26171         }
26172     },
26173
26174     /**
26175      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26176      * @param {String} id The value to search for
26177      * @return Field
26178      */
26179     findField : function(id){
26180         var field = this.items.get(id);
26181         if(!field){
26182             this.items.each(function(f){
26183                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26184                     field = f;
26185                     return false;
26186                 }
26187             });
26188         }
26189         return field || null;
26190     },
26191
26192     /**
26193      * Add a secondary form to this one, 
26194      * Used to provide tabbed forms. One form is primary, with hidden values 
26195      * which mirror the elements from the other forms.
26196      * 
26197      * @param {Roo.form.Form} form to add.
26198      * 
26199      */
26200     addForm : function(form)
26201     {
26202        
26203         if (this.childForms.indexOf(form) > -1) {
26204             // already added..
26205             return;
26206         }
26207         this.childForms.push(form);
26208         var n = '';
26209         Roo.each(form.allItems, function (fe) {
26210             
26211             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26212             if (this.findField(n)) { // already added..
26213                 return;
26214             }
26215             var add = new Roo.form.Hidden({
26216                 name : n
26217             });
26218             add.render(this.el);
26219             
26220             this.add( add );
26221         }, this);
26222         
26223     },
26224     /**
26225      * Mark fields in this form invalid in bulk.
26226      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26227      * @return {BasicForm} this
26228      */
26229     markInvalid : function(errors){
26230         if(errors instanceof Array){
26231             for(var i = 0, len = errors.length; i < len; i++){
26232                 var fieldError = errors[i];
26233                 var f = this.findField(fieldError.id);
26234                 if(f){
26235                     f.markInvalid(fieldError.msg);
26236                 }
26237             }
26238         }else{
26239             var field, id;
26240             for(id in errors){
26241                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26242                     field.markInvalid(errors[id]);
26243                 }
26244             }
26245         }
26246         Roo.each(this.childForms || [], function (f) {
26247             f.markInvalid(errors);
26248         });
26249         
26250         return this;
26251     },
26252
26253     /**
26254      * Set values for fields in this form in bulk.
26255      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26256      * @return {BasicForm} this
26257      */
26258     setValues : function(values){
26259         if(values instanceof Array){ // array of objects
26260             for(var i = 0, len = values.length; i < len; i++){
26261                 var v = values[i];
26262                 var f = this.findField(v.id);
26263                 if(f){
26264                     f.setValue(v.value);
26265                     if(this.trackResetOnLoad){
26266                         f.originalValue = f.getValue();
26267                     }
26268                 }
26269             }
26270         }else{ // object hash
26271             var field, id;
26272             for(id in values){
26273                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26274                     
26275                     if (field.setFromData && 
26276                         field.valueField && 
26277                         field.displayField &&
26278                         // combos' with local stores can 
26279                         // be queried via setValue()
26280                         // to set their value..
26281                         (field.store && !field.store.isLocal)
26282                         ) {
26283                         // it's a combo
26284                         var sd = { };
26285                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26286                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26287                         field.setFromData(sd);
26288                         
26289                     } else {
26290                         field.setValue(values[id]);
26291                     }
26292                     
26293                     
26294                     if(this.trackResetOnLoad){
26295                         field.originalValue = field.getValue();
26296                     }
26297                 }
26298             }
26299         }
26300          
26301         Roo.each(this.childForms || [], function (f) {
26302             f.setValues(values);
26303         });
26304                 
26305         return this;
26306     },
26307
26308     /**
26309      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26310      * they are returned as an array.
26311      * @param {Boolean} asString
26312      * @return {Object}
26313      */
26314     getValues : function(asString){
26315         if (this.childForms) {
26316             // copy values from the child forms
26317             Roo.each(this.childForms, function (f) {
26318                 this.setValues(f.getValues());
26319             }, this);
26320         }
26321         
26322         
26323         
26324         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26325         if(asString === true){
26326             return fs;
26327         }
26328         return Roo.urlDecode(fs);
26329     },
26330     
26331     /**
26332      * Returns the fields in this form as an object with key/value pairs. 
26333      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26334      * @return {Object}
26335      */
26336     getFieldValues : function()
26337     {
26338         if (this.childForms) {
26339             // copy values from the child forms
26340             Roo.each(this.childForms, function (f) {
26341                 this.setValues(f.getValues());
26342             }, this);
26343         }
26344         
26345         var ret = {};
26346         this.items.each(function(f){
26347             if (!f.getName()) {
26348                 return;
26349             }
26350             var v = f.getValue();
26351             if ((typeof(v) == 'object') && f.getRawValue) {
26352                 v = f.getRawValue() ; // dates..
26353             }
26354             ret[f.getName()] = v;
26355         });
26356         
26357         return ret;
26358     },
26359
26360     /**
26361      * Clears all invalid messages in this form.
26362      * @return {BasicForm} this
26363      */
26364     clearInvalid : function(){
26365         this.items.each(function(f){
26366            f.clearInvalid();
26367         });
26368         
26369         Roo.each(this.childForms || [], function (f) {
26370             f.clearInvalid();
26371         });
26372         
26373         
26374         return this;
26375     },
26376
26377     /**
26378      * Resets this form.
26379      * @return {BasicForm} this
26380      */
26381     reset : function(){
26382         this.items.each(function(f){
26383             f.reset();
26384         });
26385         
26386         Roo.each(this.childForms || [], function (f) {
26387             f.reset();
26388         });
26389        
26390         
26391         return this;
26392     },
26393
26394     /**
26395      * Add Roo.form components to this form.
26396      * @param {Field} field1
26397      * @param {Field} field2 (optional)
26398      * @param {Field} etc (optional)
26399      * @return {BasicForm} this
26400      */
26401     add : function(){
26402         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26403         return this;
26404     },
26405
26406
26407     /**
26408      * Removes a field from the items collection (does NOT remove its markup).
26409      * @param {Field} field
26410      * @return {BasicForm} this
26411      */
26412     remove : function(field){
26413         this.items.remove(field);
26414         return this;
26415     },
26416
26417     /**
26418      * Looks at the fields in this form, checks them for an id attribute,
26419      * and calls applyTo on the existing dom element with that id.
26420      * @return {BasicForm} this
26421      */
26422     render : function(){
26423         this.items.each(function(f){
26424             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26425                 f.applyTo(f.id);
26426             }
26427         });
26428         return this;
26429     },
26430
26431     /**
26432      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26433      * @param {Object} values
26434      * @return {BasicForm} this
26435      */
26436     applyToFields : function(o){
26437         this.items.each(function(f){
26438            Roo.apply(f, o);
26439         });
26440         return this;
26441     },
26442
26443     /**
26444      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26445      * @param {Object} values
26446      * @return {BasicForm} this
26447      */
26448     applyIfToFields : function(o){
26449         this.items.each(function(f){
26450            Roo.applyIf(f, o);
26451         });
26452         return this;
26453     }
26454 });
26455
26456 // back compat
26457 Roo.BasicForm = Roo.form.BasicForm;/*
26458  * Based on:
26459  * Ext JS Library 1.1.1
26460  * Copyright(c) 2006-2007, Ext JS, LLC.
26461  *
26462  * Originally Released Under LGPL - original licence link has changed is not relivant.
26463  *
26464  * Fork - LGPL
26465  * <script type="text/javascript">
26466  */
26467
26468 /**
26469  * @class Roo.form.Form
26470  * @extends Roo.form.BasicForm
26471  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26472  * @constructor
26473  * @param {Object} config Configuration options
26474  */
26475 Roo.form.Form = function(config){
26476     var xitems =  [];
26477     if (config.items) {
26478         xitems = config.items;
26479         delete config.items;
26480     }
26481    
26482     
26483     Roo.form.Form.superclass.constructor.call(this, null, config);
26484     this.url = this.url || this.action;
26485     if(!this.root){
26486         this.root = new Roo.form.Layout(Roo.applyIf({
26487             id: Roo.id()
26488         }, config));
26489     }
26490     this.active = this.root;
26491     /**
26492      * Array of all the buttons that have been added to this form via {@link addButton}
26493      * @type Array
26494      */
26495     this.buttons = [];
26496     this.allItems = [];
26497     this.addEvents({
26498         /**
26499          * @event clientvalidation
26500          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26501          * @param {Form} this
26502          * @param {Boolean} valid true if the form has passed client-side validation
26503          */
26504         clientvalidation: true,
26505         /**
26506          * @event rendered
26507          * Fires when the form is rendered
26508          * @param {Roo.form.Form} form
26509          */
26510         rendered : true
26511     });
26512     
26513     if (this.progressUrl) {
26514             // push a hidden field onto the list of fields..
26515             this.addxtype( {
26516                     xns: Roo.form, 
26517                     xtype : 'Hidden', 
26518                     name : 'UPLOAD_IDENTIFIER' 
26519             });
26520         }
26521         
26522     
26523     Roo.each(xitems, this.addxtype, this);
26524     
26525     
26526     
26527 };
26528
26529 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26530     /**
26531      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26532      */
26533     /**
26534      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26535      */
26536     /**
26537      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26538      */
26539     buttonAlign:'center',
26540
26541     /**
26542      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26543      */
26544     minButtonWidth:75,
26545
26546     /**
26547      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26548      * This property cascades to child containers if not set.
26549      */
26550     labelAlign:'left',
26551
26552     /**
26553      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26554      * fires a looping event with that state. This is required to bind buttons to the valid
26555      * state using the config value formBind:true on the button.
26556      */
26557     monitorValid : false,
26558
26559     /**
26560      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26561      */
26562     monitorPoll : 200,
26563     
26564     /**
26565      * @cfg {String} progressUrl - Url to return progress data 
26566      */
26567     
26568     progressUrl : false,
26569   
26570     /**
26571      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26572      * fields are added and the column is closed. If no fields are passed the column remains open
26573      * until end() is called.
26574      * @param {Object} config The config to pass to the column
26575      * @param {Field} field1 (optional)
26576      * @param {Field} field2 (optional)
26577      * @param {Field} etc (optional)
26578      * @return Column The column container object
26579      */
26580     column : function(c){
26581         var col = new Roo.form.Column(c);
26582         this.start(col);
26583         if(arguments.length > 1){ // duplicate code required because of Opera
26584             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26585             this.end();
26586         }
26587         return col;
26588     },
26589
26590     /**
26591      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26592      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26593      * until end() is called.
26594      * @param {Object} config The config to pass to the fieldset
26595      * @param {Field} field1 (optional)
26596      * @param {Field} field2 (optional)
26597      * @param {Field} etc (optional)
26598      * @return FieldSet The fieldset container object
26599      */
26600     fieldset : function(c){
26601         var fs = new Roo.form.FieldSet(c);
26602         this.start(fs);
26603         if(arguments.length > 1){ // duplicate code required because of Opera
26604             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26605             this.end();
26606         }
26607         return fs;
26608     },
26609
26610     /**
26611      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26612      * fields are added and the container is closed. If no fields are passed the container remains open
26613      * until end() is called.
26614      * @param {Object} config The config to pass to the Layout
26615      * @param {Field} field1 (optional)
26616      * @param {Field} field2 (optional)
26617      * @param {Field} etc (optional)
26618      * @return Layout The container object
26619      */
26620     container : function(c){
26621         var l = new Roo.form.Layout(c);
26622         this.start(l);
26623         if(arguments.length > 1){ // duplicate code required because of Opera
26624             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26625             this.end();
26626         }
26627         return l;
26628     },
26629
26630     /**
26631      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26632      * @param {Object} container A Roo.form.Layout or subclass of Layout
26633      * @return {Form} this
26634      */
26635     start : function(c){
26636         // cascade label info
26637         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26638         this.active.stack.push(c);
26639         c.ownerCt = this.active;
26640         this.active = c;
26641         return this;
26642     },
26643
26644     /**
26645      * Closes the current open container
26646      * @return {Form} this
26647      */
26648     end : function(){
26649         if(this.active == this.root){
26650             return this;
26651         }
26652         this.active = this.active.ownerCt;
26653         return this;
26654     },
26655
26656     /**
26657      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26658      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26659      * as the label of the field.
26660      * @param {Field} field1
26661      * @param {Field} field2 (optional)
26662      * @param {Field} etc. (optional)
26663      * @return {Form} this
26664      */
26665     add : function(){
26666         this.active.stack.push.apply(this.active.stack, arguments);
26667         this.allItems.push.apply(this.allItems,arguments);
26668         var r = [];
26669         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26670             if(a[i].isFormField){
26671                 r.push(a[i]);
26672             }
26673         }
26674         if(r.length > 0){
26675             Roo.form.Form.superclass.add.apply(this, r);
26676         }
26677         return this;
26678     },
26679     
26680
26681     
26682     
26683     
26684      /**
26685      * Find any element that has been added to a form, using it's ID or name
26686      * This can include framesets, columns etc. along with regular fields..
26687      * @param {String} id - id or name to find.
26688      
26689      * @return {Element} e - or false if nothing found.
26690      */
26691     findbyId : function(id)
26692     {
26693         var ret = false;
26694         if (!id) {
26695             return ret;
26696         }
26697         Ext.each(this.allItems, function(f){
26698             if (f.id == id || f.name == id ){
26699                 ret = f;
26700                 return false;
26701             }
26702         });
26703         return ret;
26704     },
26705
26706     
26707     
26708     /**
26709      * Render this form into the passed container. This should only be called once!
26710      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26711      * @return {Form} this
26712      */
26713     render : function(ct)
26714     {
26715         
26716         
26717         
26718         ct = Roo.get(ct);
26719         var o = this.autoCreate || {
26720             tag: 'form',
26721             method : this.method || 'POST',
26722             id : this.id || Roo.id()
26723         };
26724         this.initEl(ct.createChild(o));
26725
26726         this.root.render(this.el);
26727         
26728        
26729              
26730         this.items.each(function(f){
26731             f.render('x-form-el-'+f.id);
26732         });
26733
26734         if(this.buttons.length > 0){
26735             // tables are required to maintain order and for correct IE layout
26736             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26737                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26738                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26739             }}, null, true);
26740             var tr = tb.getElementsByTagName('tr')[0];
26741             for(var i = 0, len = this.buttons.length; i < len; i++) {
26742                 var b = this.buttons[i];
26743                 var td = document.createElement('td');
26744                 td.className = 'x-form-btn-td';
26745                 b.render(tr.appendChild(td));
26746             }
26747         }
26748         if(this.monitorValid){ // initialize after render
26749             this.startMonitoring();
26750         }
26751         this.fireEvent('rendered', this);
26752         return this;
26753     },
26754
26755     /**
26756      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26757      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26758      * object or a valid Roo.DomHelper element config
26759      * @param {Function} handler The function called when the button is clicked
26760      * @param {Object} scope (optional) The scope of the handler function
26761      * @return {Roo.Button}
26762      */
26763     addButton : function(config, handler, scope){
26764         var bc = {
26765             handler: handler,
26766             scope: scope,
26767             minWidth: this.minButtonWidth,
26768             hideParent:true
26769         };
26770         if(typeof config == "string"){
26771             bc.text = config;
26772         }else{
26773             Roo.apply(bc, config);
26774         }
26775         var btn = new Roo.Button(null, bc);
26776         this.buttons.push(btn);
26777         return btn;
26778     },
26779
26780      /**
26781      * Adds a series of form elements (using the xtype property as the factory method.
26782      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26783      * @param {Object} config 
26784      */
26785     
26786     addxtype : function()
26787     {
26788         var ar = Array.prototype.slice.call(arguments, 0);
26789         var ret = false;
26790         for(var i = 0; i < ar.length; i++) {
26791             if (!ar[i]) {
26792                 continue; // skip -- if this happends something invalid got sent, we 
26793                 // should ignore it, as basically that interface element will not show up
26794                 // and that should be pretty obvious!!
26795             }
26796             
26797             if (Roo.form[ar[i].xtype]) {
26798                 ar[i].form = this;
26799                 var fe = Roo.factory(ar[i], Roo.form);
26800                 if (!ret) {
26801                     ret = fe;
26802                 }
26803                 fe.form = this;
26804                 if (fe.store) {
26805                     fe.store.form = this;
26806                 }
26807                 if (fe.isLayout) {  
26808                          
26809                     this.start(fe);
26810                     this.allItems.push(fe);
26811                     if (fe.items && fe.addxtype) {
26812                         fe.addxtype.apply(fe, fe.items);
26813                         delete fe.items;
26814                     }
26815                      this.end();
26816                     continue;
26817                 }
26818                 
26819                 
26820                  
26821                 this.add(fe);
26822               //  console.log('adding ' + ar[i].xtype);
26823             }
26824             if (ar[i].xtype == 'Button') {  
26825                 //console.log('adding button');
26826                 //console.log(ar[i]);
26827                 this.addButton(ar[i]);
26828                 this.allItems.push(fe);
26829                 continue;
26830             }
26831             
26832             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26833                 alert('end is not supported on xtype any more, use items');
26834             //    this.end();
26835             //    //console.log('adding end');
26836             }
26837             
26838         }
26839         return ret;
26840     },
26841     
26842     /**
26843      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26844      * option "monitorValid"
26845      */
26846     startMonitoring : function(){
26847         if(!this.bound){
26848             this.bound = true;
26849             Roo.TaskMgr.start({
26850                 run : this.bindHandler,
26851                 interval : this.monitorPoll || 200,
26852                 scope: this
26853             });
26854         }
26855     },
26856
26857     /**
26858      * Stops monitoring of the valid state of this form
26859      */
26860     stopMonitoring : function(){
26861         this.bound = false;
26862     },
26863
26864     // private
26865     bindHandler : function(){
26866         if(!this.bound){
26867             return false; // stops binding
26868         }
26869         var valid = true;
26870         this.items.each(function(f){
26871             if(!f.isValid(true)){
26872                 valid = false;
26873                 return false;
26874             }
26875         });
26876         for(var i = 0, len = this.buttons.length; i < len; i++){
26877             var btn = this.buttons[i];
26878             if(btn.formBind === true && btn.disabled === valid){
26879                 btn.setDisabled(!valid);
26880             }
26881         }
26882         this.fireEvent('clientvalidation', this, valid);
26883     }
26884     
26885     
26886     
26887     
26888     
26889     
26890     
26891     
26892 });
26893
26894
26895 // back compat
26896 Roo.Form = Roo.form.Form;
26897 /*
26898  * Based on:
26899  * Ext JS Library 1.1.1
26900  * Copyright(c) 2006-2007, Ext JS, LLC.
26901  *
26902  * Originally Released Under LGPL - original licence link has changed is not relivant.
26903  *
26904  * Fork - LGPL
26905  * <script type="text/javascript">
26906  */
26907  
26908  /**
26909  * @class Roo.form.Action
26910  * Internal Class used to handle form actions
26911  * @constructor
26912  * @param {Roo.form.BasicForm} el The form element or its id
26913  * @param {Object} config Configuration options
26914  */
26915  
26916  
26917 // define the action interface
26918 Roo.form.Action = function(form, options){
26919     this.form = form;
26920     this.options = options || {};
26921 };
26922 /**
26923  * Client Validation Failed
26924  * @const 
26925  */
26926 Roo.form.Action.CLIENT_INVALID = 'client';
26927 /**
26928  * Server Validation Failed
26929  * @const 
26930  */
26931  Roo.form.Action.SERVER_INVALID = 'server';
26932  /**
26933  * Connect to Server Failed
26934  * @const 
26935  */
26936 Roo.form.Action.CONNECT_FAILURE = 'connect';
26937 /**
26938  * Reading Data from Server Failed
26939  * @const 
26940  */
26941 Roo.form.Action.LOAD_FAILURE = 'load';
26942
26943 Roo.form.Action.prototype = {
26944     type : 'default',
26945     failureType : undefined,
26946     response : undefined,
26947     result : undefined,
26948
26949     // interface method
26950     run : function(options){
26951
26952     },
26953
26954     // interface method
26955     success : function(response){
26956
26957     },
26958
26959     // interface method
26960     handleResponse : function(response){
26961
26962     },
26963
26964     // default connection failure
26965     failure : function(response){
26966         this.response = response;
26967         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26968         this.form.afterAction(this, false);
26969     },
26970
26971     processResponse : function(response){
26972         this.response = response;
26973         if(!response.responseText){
26974             return true;
26975         }
26976         this.result = this.handleResponse(response);
26977         return this.result;
26978     },
26979
26980     // utility functions used internally
26981     getUrl : function(appendParams){
26982         var url = this.options.url || this.form.url || this.form.el.dom.action;
26983         if(appendParams){
26984             var p = this.getParams();
26985             if(p){
26986                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26987             }
26988         }
26989         return url;
26990     },
26991
26992     getMethod : function(){
26993         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26994     },
26995
26996     getParams : function(){
26997         var bp = this.form.baseParams;
26998         var p = this.options.params;
26999         if(p){
27000             if(typeof p == "object"){
27001                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27002             }else if(typeof p == 'string' && bp){
27003                 p += '&' + Roo.urlEncode(bp);
27004             }
27005         }else if(bp){
27006             p = Roo.urlEncode(bp);
27007         }
27008         return p;
27009     },
27010
27011     createCallback : function(){
27012         return {
27013             success: this.success,
27014             failure: this.failure,
27015             scope: this,
27016             timeout: (this.form.timeout*1000),
27017             upload: this.form.fileUpload ? this.success : undefined
27018         };
27019     }
27020 };
27021
27022 Roo.form.Action.Submit = function(form, options){
27023     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27024 };
27025
27026 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27027     type : 'submit',
27028
27029     haveProgress : false,
27030     uploadComplete : false,
27031     
27032     // uploadProgress indicator.
27033     uploadProgress : function()
27034     {
27035         if (!this.form.progressUrl) {
27036             return;
27037         }
27038         
27039         if (!this.haveProgress) {
27040             Roo.MessageBox.progress("Uploading", "Uploading");
27041         }
27042         if (this.uploadComplete) {
27043            Roo.MessageBox.hide();
27044            return;
27045         }
27046         
27047         this.haveProgress = true;
27048    
27049         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27050         
27051         var c = new Roo.data.Connection();
27052         c.request({
27053             url : this.form.progressUrl,
27054             params: {
27055                 id : uid
27056             },
27057             method: 'GET',
27058             success : function(req){
27059                //console.log(data);
27060                 var rdata = false;
27061                 var edata;
27062                 try  {
27063                    rdata = Roo.decode(req.responseText)
27064                 } catch (e) {
27065                     Roo.log("Invalid data from server..");
27066                     Roo.log(edata);
27067                     return;
27068                 }
27069                 if (!rdata || !rdata.success) {
27070                     Roo.log(rdata);
27071                     return;
27072                 }
27073                 var data = rdata.data;
27074                 
27075                 if (this.uploadComplete) {
27076                    Roo.MessageBox.hide();
27077                    return;
27078                 }
27079                    
27080                 if (data){
27081                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27082                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27083                     );
27084                 }
27085                 this.uploadProgress.defer(2000,this);
27086             },
27087        
27088             failure: function(data) {
27089                 Roo.log('progress url failed ');
27090                 Roo.log(data);
27091             },
27092             scope : this
27093         });
27094            
27095     },
27096     
27097     
27098     run : function()
27099     {
27100         // run get Values on the form, so it syncs any secondary forms.
27101         this.form.getValues();
27102         
27103         var o = this.options;
27104         var method = this.getMethod();
27105         var isPost = method == 'POST';
27106         if(o.clientValidation === false || this.form.isValid()){
27107             
27108             if (this.form.progressUrl) {
27109                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27110                     (new Date() * 1) + '' + Math.random());
27111                     
27112             } 
27113             
27114             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27115                 form:this.form.el.dom,
27116                 url:this.getUrl(!isPost),
27117                 method: method,
27118                 params:isPost ? this.getParams() : null,
27119                 isUpload: this.form.fileUpload
27120             }));
27121             
27122             this.uploadProgress();
27123
27124         }else if (o.clientValidation !== false){ // client validation failed
27125             this.failureType = Roo.form.Action.CLIENT_INVALID;
27126             this.form.afterAction(this, false);
27127         }
27128     },
27129
27130     success : function(response)
27131     {
27132         this.uploadComplete= true;
27133         if (this.haveProgress) {
27134             Roo.MessageBox.hide();
27135         }
27136         
27137         var result = this.processResponse(response);
27138         if(result === true || result.success){
27139             this.form.afterAction(this, true);
27140             return;
27141         }
27142         if(result.errors){
27143             this.form.markInvalid(result.errors);
27144             this.failureType = Roo.form.Action.SERVER_INVALID;
27145         }
27146         this.form.afterAction(this, false);
27147     },
27148     failure : function(response)
27149     {
27150         this.uploadComplete= true;
27151         if (this.haveProgress) {
27152             Roo.MessageBox.hide();
27153         }
27154         
27155         this.response = response;
27156         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27157         this.form.afterAction(this, false);
27158     },
27159     
27160     handleResponse : function(response){
27161         if(this.form.errorReader){
27162             var rs = this.form.errorReader.read(response);
27163             var errors = [];
27164             if(rs.records){
27165                 for(var i = 0, len = rs.records.length; i < len; i++) {
27166                     var r = rs.records[i];
27167                     errors[i] = r.data;
27168                 }
27169             }
27170             if(errors.length < 1){
27171                 errors = null;
27172             }
27173             return {
27174                 success : rs.success,
27175                 errors : errors
27176             };
27177         }
27178         var ret = false;
27179         try {
27180             ret = Roo.decode(response.responseText);
27181         } catch (e) {
27182             ret = {
27183                 success: false,
27184                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27185                 errors : []
27186             };
27187         }
27188         return ret;
27189         
27190     }
27191 });
27192
27193
27194 Roo.form.Action.Load = function(form, options){
27195     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27196     this.reader = this.form.reader;
27197 };
27198
27199 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27200     type : 'load',
27201
27202     run : function(){
27203         Roo.Ajax.request(Roo.apply(
27204                 this.createCallback(), {
27205                     method:this.getMethod(),
27206                     url:this.getUrl(false),
27207                     params:this.getParams()
27208         }));
27209     },
27210
27211     success : function(response){
27212         var result = this.processResponse(response);
27213         if(result === true || !result.success || !result.data){
27214             this.failureType = Roo.form.Action.LOAD_FAILURE;
27215             this.form.afterAction(this, false);
27216             return;
27217         }
27218         this.form.clearInvalid();
27219         this.form.setValues(result.data);
27220         this.form.afterAction(this, true);
27221     },
27222
27223     handleResponse : function(response){
27224         if(this.form.reader){
27225             var rs = this.form.reader.read(response);
27226             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27227             return {
27228                 success : rs.success,
27229                 data : data
27230             };
27231         }
27232         return Roo.decode(response.responseText);
27233     }
27234 });
27235
27236 Roo.form.Action.ACTION_TYPES = {
27237     'load' : Roo.form.Action.Load,
27238     'submit' : Roo.form.Action.Submit
27239 };/*
27240  * Based on:
27241  * Ext JS Library 1.1.1
27242  * Copyright(c) 2006-2007, Ext JS, LLC.
27243  *
27244  * Originally Released Under LGPL - original licence link has changed is not relivant.
27245  *
27246  * Fork - LGPL
27247  * <script type="text/javascript">
27248  */
27249  
27250 /**
27251  * @class Roo.form.Layout
27252  * @extends Roo.Component
27253  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27254  * @constructor
27255  * @param {Object} config Configuration options
27256  */
27257 Roo.form.Layout = function(config){
27258     var xitems = [];
27259     if (config.items) {
27260         xitems = config.items;
27261         delete config.items;
27262     }
27263     Roo.form.Layout.superclass.constructor.call(this, config);
27264     this.stack = [];
27265     Roo.each(xitems, this.addxtype, this);
27266      
27267 };
27268
27269 Roo.extend(Roo.form.Layout, Roo.Component, {
27270     /**
27271      * @cfg {String/Object} autoCreate
27272      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27273      */
27274     /**
27275      * @cfg {String/Object/Function} style
27276      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27277      * a function which returns such a specification.
27278      */
27279     /**
27280      * @cfg {String} labelAlign
27281      * Valid values are "left," "top" and "right" (defaults to "left")
27282      */
27283     /**
27284      * @cfg {Number} labelWidth
27285      * Fixed width in pixels of all field labels (defaults to undefined)
27286      */
27287     /**
27288      * @cfg {Boolean} clear
27289      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27290      */
27291     clear : true,
27292     /**
27293      * @cfg {String} labelSeparator
27294      * The separator to use after field labels (defaults to ':')
27295      */
27296     labelSeparator : ':',
27297     /**
27298      * @cfg {Boolean} hideLabels
27299      * True to suppress the display of field labels in this layout (defaults to false)
27300      */
27301     hideLabels : false,
27302
27303     // private
27304     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27305     
27306     isLayout : true,
27307     
27308     // private
27309     onRender : function(ct, position){
27310         if(this.el){ // from markup
27311             this.el = Roo.get(this.el);
27312         }else {  // generate
27313             var cfg = this.getAutoCreate();
27314             this.el = ct.createChild(cfg, position);
27315         }
27316         if(this.style){
27317             this.el.applyStyles(this.style);
27318         }
27319         if(this.labelAlign){
27320             this.el.addClass('x-form-label-'+this.labelAlign);
27321         }
27322         if(this.hideLabels){
27323             this.labelStyle = "display:none";
27324             this.elementStyle = "padding-left:0;";
27325         }else{
27326             if(typeof this.labelWidth == 'number'){
27327                 this.labelStyle = "width:"+this.labelWidth+"px;";
27328                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27329             }
27330             if(this.labelAlign == 'top'){
27331                 this.labelStyle = "width:auto;";
27332                 this.elementStyle = "padding-left:0;";
27333             }
27334         }
27335         var stack = this.stack;
27336         var slen = stack.length;
27337         if(slen > 0){
27338             if(!this.fieldTpl){
27339                 var t = new Roo.Template(
27340                     '<div class="x-form-item {5}">',
27341                         '<label for="{0}" style="{2}">{1}{4}</label>',
27342                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27343                         '</div>',
27344                     '</div><div class="x-form-clear-left"></div>'
27345                 );
27346                 t.disableFormats = true;
27347                 t.compile();
27348                 Roo.form.Layout.prototype.fieldTpl = t;
27349             }
27350             for(var i = 0; i < slen; i++) {
27351                 if(stack[i].isFormField){
27352                     this.renderField(stack[i]);
27353                 }else{
27354                     this.renderComponent(stack[i]);
27355                 }
27356             }
27357         }
27358         if(this.clear){
27359             this.el.createChild({cls:'x-form-clear'});
27360         }
27361     },
27362
27363     // private
27364     renderField : function(f){
27365         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27366                f.id, //0
27367                f.fieldLabel, //1
27368                f.labelStyle||this.labelStyle||'', //2
27369                this.elementStyle||'', //3
27370                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27371                f.itemCls||this.itemCls||''  //5
27372        ], true).getPrevSibling());
27373     },
27374
27375     // private
27376     renderComponent : function(c){
27377         c.render(c.isLayout ? this.el : this.el.createChild());    
27378     },
27379     /**
27380      * Adds a object form elements (using the xtype property as the factory method.)
27381      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27382      * @param {Object} config 
27383      */
27384     addxtype : function(o)
27385     {
27386         // create the lement.
27387         o.form = this.form;
27388         var fe = Roo.factory(o, Roo.form);
27389         this.form.allItems.push(fe);
27390         this.stack.push(fe);
27391         
27392         if (fe.isFormField) {
27393             this.form.items.add(fe);
27394         }
27395          
27396         return fe;
27397     }
27398 });
27399
27400 /**
27401  * @class Roo.form.Column
27402  * @extends Roo.form.Layout
27403  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27404  * @constructor
27405  * @param {Object} config Configuration options
27406  */
27407 Roo.form.Column = function(config){
27408     Roo.form.Column.superclass.constructor.call(this, config);
27409 };
27410
27411 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27412     /**
27413      * @cfg {Number/String} width
27414      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27415      */
27416     /**
27417      * @cfg {String/Object} autoCreate
27418      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27419      */
27420
27421     // private
27422     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27423
27424     // private
27425     onRender : function(ct, position){
27426         Roo.form.Column.superclass.onRender.call(this, ct, position);
27427         if(this.width){
27428             this.el.setWidth(this.width);
27429         }
27430     }
27431 });
27432
27433
27434 /**
27435  * @class Roo.form.Row
27436  * @extends Roo.form.Layout
27437  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27438  * @constructor
27439  * @param {Object} config Configuration options
27440  */
27441
27442  
27443 Roo.form.Row = function(config){
27444     Roo.form.Row.superclass.constructor.call(this, config);
27445 };
27446  
27447 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27448       /**
27449      * @cfg {Number/String} width
27450      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27451      */
27452     /**
27453      * @cfg {Number/String} height
27454      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27455      */
27456     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27457     
27458     padWidth : 20,
27459     // private
27460     onRender : function(ct, position){
27461         //console.log('row render');
27462         if(!this.rowTpl){
27463             var t = new Roo.Template(
27464                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27465                     '<label for="{0}" style="{2}">{1}{4}</label>',
27466                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27467                     '</div>',
27468                 '</div>'
27469             );
27470             t.disableFormats = true;
27471             t.compile();
27472             Roo.form.Layout.prototype.rowTpl = t;
27473         }
27474         this.fieldTpl = this.rowTpl;
27475         
27476         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27477         var labelWidth = 100;
27478         
27479         if ((this.labelAlign != 'top')) {
27480             if (typeof this.labelWidth == 'number') {
27481                 labelWidth = this.labelWidth
27482             }
27483             this.padWidth =  20 + labelWidth;
27484             
27485         }
27486         
27487         Roo.form.Column.superclass.onRender.call(this, ct, position);
27488         if(this.width){
27489             this.el.setWidth(this.width);
27490         }
27491         if(this.height){
27492             this.el.setHeight(this.height);
27493         }
27494     },
27495     
27496     // private
27497     renderField : function(f){
27498         f.fieldEl = this.fieldTpl.append(this.el, [
27499                f.id, f.fieldLabel,
27500                f.labelStyle||this.labelStyle||'',
27501                this.elementStyle||'',
27502                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27503                f.itemCls||this.itemCls||'',
27504                f.width ? f.width + this.padWidth : 160 + this.padWidth
27505        ],true);
27506     }
27507 });
27508  
27509
27510 /**
27511  * @class Roo.form.FieldSet
27512  * @extends Roo.form.Layout
27513  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27514  * @constructor
27515  * @param {Object} config Configuration options
27516  */
27517 Roo.form.FieldSet = function(config){
27518     Roo.form.FieldSet.superclass.constructor.call(this, config);
27519 };
27520
27521 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27522     /**
27523      * @cfg {String} legend
27524      * The text to display as the legend for the FieldSet (defaults to '')
27525      */
27526     /**
27527      * @cfg {String/Object} autoCreate
27528      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27529      */
27530
27531     // private
27532     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27533
27534     // private
27535     onRender : function(ct, position){
27536         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27537         if(this.legend){
27538             this.setLegend(this.legend);
27539         }
27540     },
27541
27542     // private
27543     setLegend : function(text){
27544         if(this.rendered){
27545             this.el.child('legend').update(text);
27546         }
27547     }
27548 });/*
27549  * Based on:
27550  * Ext JS Library 1.1.1
27551  * Copyright(c) 2006-2007, Ext JS, LLC.
27552  *
27553  * Originally Released Under LGPL - original licence link has changed is not relivant.
27554  *
27555  * Fork - LGPL
27556  * <script type="text/javascript">
27557  */
27558 /**
27559  * @class Roo.form.VTypes
27560  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27561  * @singleton
27562  */
27563 Roo.form.VTypes = function(){
27564     // closure these in so they are only created once.
27565     var alpha = /^[a-zA-Z_]+$/;
27566     var alphanum = /^[a-zA-Z0-9_]+$/;
27567     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27568     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27569
27570     // All these messages and functions are configurable
27571     return {
27572         /**
27573          * The function used to validate email addresses
27574          * @param {String} value The email address
27575          */
27576         'email' : function(v){
27577             return email.test(v);
27578         },
27579         /**
27580          * The error text to display when the email validation function returns false
27581          * @type String
27582          */
27583         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27584         /**
27585          * The keystroke filter mask to be applied on email input
27586          * @type RegExp
27587          */
27588         'emailMask' : /[a-z0-9_\.\-@]/i,
27589
27590         /**
27591          * The function used to validate URLs
27592          * @param {String} value The URL
27593          */
27594         'url' : function(v){
27595             return url.test(v);
27596         },
27597         /**
27598          * The error text to display when the url validation function returns false
27599          * @type String
27600          */
27601         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27602         
27603         /**
27604          * The function used to validate alpha values
27605          * @param {String} value The value
27606          */
27607         'alpha' : function(v){
27608             return alpha.test(v);
27609         },
27610         /**
27611          * The error text to display when the alpha validation function returns false
27612          * @type String
27613          */
27614         'alphaText' : 'This field should only contain letters and _',
27615         /**
27616          * The keystroke filter mask to be applied on alpha input
27617          * @type RegExp
27618          */
27619         'alphaMask' : /[a-z_]/i,
27620
27621         /**
27622          * The function used to validate alphanumeric values
27623          * @param {String} value The value
27624          */
27625         'alphanum' : function(v){
27626             return alphanum.test(v);
27627         },
27628         /**
27629          * The error text to display when the alphanumeric validation function returns false
27630          * @type String
27631          */
27632         'alphanumText' : 'This field should only contain letters, numbers and _',
27633         /**
27634          * The keystroke filter mask to be applied on alphanumeric input
27635          * @type RegExp
27636          */
27637         'alphanumMask' : /[a-z0-9_]/i
27638     };
27639 }();//<script type="text/javascript">
27640
27641 /**
27642  * @class Roo.form.FCKeditor
27643  * @extends Roo.form.TextArea
27644  * Wrapper around the FCKEditor http://www.fckeditor.net
27645  * @constructor
27646  * Creates a new FCKeditor
27647  * @param {Object} config Configuration options
27648  */
27649 Roo.form.FCKeditor = function(config){
27650     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27651     this.addEvents({
27652          /**
27653          * @event editorinit
27654          * Fired when the editor is initialized - you can add extra handlers here..
27655          * @param {FCKeditor} this
27656          * @param {Object} the FCK object.
27657          */
27658         editorinit : true
27659     });
27660     
27661     
27662 };
27663 Roo.form.FCKeditor.editors = { };
27664 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27665 {
27666     //defaultAutoCreate : {
27667     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27668     //},
27669     // private
27670     /**
27671      * @cfg {Object} fck options - see fck manual for details.
27672      */
27673     fckconfig : false,
27674     
27675     /**
27676      * @cfg {Object} fck toolbar set (Basic or Default)
27677      */
27678     toolbarSet : 'Basic',
27679     /**
27680      * @cfg {Object} fck BasePath
27681      */ 
27682     basePath : '/fckeditor/',
27683     
27684     
27685     frame : false,
27686     
27687     value : '',
27688     
27689    
27690     onRender : function(ct, position)
27691     {
27692         if(!this.el){
27693             this.defaultAutoCreate = {
27694                 tag: "textarea",
27695                 style:"width:300px;height:60px;",
27696                 autocomplete: "off"
27697             };
27698         }
27699         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27700         /*
27701         if(this.grow){
27702             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27703             if(this.preventScrollbars){
27704                 this.el.setStyle("overflow", "hidden");
27705             }
27706             this.el.setHeight(this.growMin);
27707         }
27708         */
27709         //console.log('onrender' + this.getId() );
27710         Roo.form.FCKeditor.editors[this.getId()] = this;
27711          
27712
27713         this.replaceTextarea() ;
27714         
27715     },
27716     
27717     getEditor : function() {
27718         return this.fckEditor;
27719     },
27720     /**
27721      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27722      * @param {Mixed} value The value to set
27723      */
27724     
27725     
27726     setValue : function(value)
27727     {
27728         //console.log('setValue: ' + value);
27729         
27730         if(typeof(value) == 'undefined') { // not sure why this is happending...
27731             return;
27732         }
27733         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27734         
27735         //if(!this.el || !this.getEditor()) {
27736         //    this.value = value;
27737             //this.setValue.defer(100,this,[value]);    
27738         //    return;
27739         //} 
27740         
27741         if(!this.getEditor()) {
27742             return;
27743         }
27744         
27745         this.getEditor().SetData(value);
27746         
27747         //
27748
27749     },
27750
27751     /**
27752      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27753      * @return {Mixed} value The field value
27754      */
27755     getValue : function()
27756     {
27757         
27758         if (this.frame && this.frame.dom.style.display == 'none') {
27759             return Roo.form.FCKeditor.superclass.getValue.call(this);
27760         }
27761         
27762         if(!this.el || !this.getEditor()) {
27763            
27764            // this.getValue.defer(100,this); 
27765             return this.value;
27766         }
27767        
27768         
27769         var value=this.getEditor().GetData();
27770         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27771         return Roo.form.FCKeditor.superclass.getValue.call(this);
27772         
27773
27774     },
27775
27776     /**
27777      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27778      * @return {Mixed} value The field value
27779      */
27780     getRawValue : function()
27781     {
27782         if (this.frame && this.frame.dom.style.display == 'none') {
27783             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27784         }
27785         
27786         if(!this.el || !this.getEditor()) {
27787             //this.getRawValue.defer(100,this); 
27788             return this.value;
27789             return;
27790         }
27791         
27792         
27793         
27794         var value=this.getEditor().GetData();
27795         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27796         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27797          
27798     },
27799     
27800     setSize : function(w,h) {
27801         
27802         
27803         
27804         //if (this.frame && this.frame.dom.style.display == 'none') {
27805         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27806         //    return;
27807         //}
27808         //if(!this.el || !this.getEditor()) {
27809         //    this.setSize.defer(100,this, [w,h]); 
27810         //    return;
27811         //}
27812         
27813         
27814         
27815         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27816         
27817         this.frame.dom.setAttribute('width', w);
27818         this.frame.dom.setAttribute('height', h);
27819         this.frame.setSize(w,h);
27820         
27821     },
27822     
27823     toggleSourceEdit : function(value) {
27824         
27825       
27826          
27827         this.el.dom.style.display = value ? '' : 'none';
27828         this.frame.dom.style.display = value ?  'none' : '';
27829         
27830     },
27831     
27832     
27833     focus: function(tag)
27834     {
27835         if (this.frame.dom.style.display == 'none') {
27836             return Roo.form.FCKeditor.superclass.focus.call(this);
27837         }
27838         if(!this.el || !this.getEditor()) {
27839             this.focus.defer(100,this, [tag]); 
27840             return;
27841         }
27842         
27843         
27844         
27845         
27846         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27847         this.getEditor().Focus();
27848         if (tgs.length) {
27849             if (!this.getEditor().Selection.GetSelection()) {
27850                 this.focus.defer(100,this, [tag]); 
27851                 return;
27852             }
27853             
27854             
27855             var r = this.getEditor().EditorDocument.createRange();
27856             r.setStart(tgs[0],0);
27857             r.setEnd(tgs[0],0);
27858             this.getEditor().Selection.GetSelection().removeAllRanges();
27859             this.getEditor().Selection.GetSelection().addRange(r);
27860             this.getEditor().Focus();
27861         }
27862         
27863     },
27864     
27865     
27866     
27867     replaceTextarea : function()
27868     {
27869         if ( document.getElementById( this.getId() + '___Frame' ) )
27870             return ;
27871         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27872         //{
27873             // We must check the elements firstly using the Id and then the name.
27874         var oTextarea = document.getElementById( this.getId() );
27875         
27876         var colElementsByName = document.getElementsByName( this.getId() ) ;
27877          
27878         oTextarea.style.display = 'none' ;
27879
27880         if ( oTextarea.tabIndex ) {            
27881             this.TabIndex = oTextarea.tabIndex ;
27882         }
27883         
27884         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27885         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27886         this.frame = Roo.get(this.getId() + '___Frame')
27887     },
27888     
27889     _getConfigHtml : function()
27890     {
27891         var sConfig = '' ;
27892
27893         for ( var o in this.fckconfig ) {
27894             sConfig += sConfig.length > 0  ? '&amp;' : '';
27895             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27896         }
27897
27898         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27899     },
27900     
27901     
27902     _getIFrameHtml : function()
27903     {
27904         var sFile = 'fckeditor.html' ;
27905         /* no idea what this is about..
27906         try
27907         {
27908             if ( (/fcksource=true/i).test( window.top.location.search ) )
27909                 sFile = 'fckeditor.original.html' ;
27910         }
27911         catch (e) { 
27912         */
27913
27914         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27915         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27916         
27917         
27918         var html = '<iframe id="' + this.getId() +
27919             '___Frame" src="' + sLink +
27920             '" width="' + this.width +
27921             '" height="' + this.height + '"' +
27922             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27923             ' frameborder="0" scrolling="no"></iframe>' ;
27924
27925         return html ;
27926     },
27927     
27928     _insertHtmlBefore : function( html, element )
27929     {
27930         if ( element.insertAdjacentHTML )       {
27931             // IE
27932             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27933         } else { // Gecko
27934             var oRange = document.createRange() ;
27935             oRange.setStartBefore( element ) ;
27936             var oFragment = oRange.createContextualFragment( html );
27937             element.parentNode.insertBefore( oFragment, element ) ;
27938         }
27939     }
27940     
27941     
27942   
27943     
27944     
27945     
27946     
27947
27948 });
27949
27950 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27951
27952 function FCKeditor_OnComplete(editorInstance){
27953     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27954     f.fckEditor = editorInstance;
27955     //console.log("loaded");
27956     f.fireEvent('editorinit', f, editorInstance);
27957
27958   
27959
27960  
27961
27962
27963
27964
27965
27966
27967
27968
27969
27970
27971
27972
27973
27974
27975
27976 //<script type="text/javascript">
27977 /**
27978  * @class Roo.form.GridField
27979  * @extends Roo.form.Field
27980  * Embed a grid (or editable grid into a form)
27981  * STATUS ALPHA
27982  * 
27983  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27984  * it needs 
27985  * xgrid.store = Roo.data.Store
27986  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27987  * xgrid.store.reader = Roo.data.JsonReader 
27988  * 
27989  * 
27990  * @constructor
27991  * Creates a new GridField
27992  * @param {Object} config Configuration options
27993  */
27994 Roo.form.GridField = function(config){
27995     Roo.form.GridField.superclass.constructor.call(this, config);
27996      
27997 };
27998
27999 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28000     /**
28001      * @cfg {Number} width  - used to restrict width of grid..
28002      */
28003     width : 100,
28004     /**
28005      * @cfg {Number} height - used to restrict height of grid..
28006      */
28007     height : 50,
28008      /**
28009      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28010          * 
28011          *}
28012      */
28013     xgrid : false, 
28014     /**
28015      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28016      * {tag: "input", type: "checkbox", autocomplete: "off"})
28017      */
28018    // defaultAutoCreate : { tag: 'div' },
28019     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28020     /**
28021      * @cfg {String} addTitle Text to include for adding a title.
28022      */
28023     addTitle : false,
28024     //
28025     onResize : function(){
28026         Roo.form.Field.superclass.onResize.apply(this, arguments);
28027     },
28028
28029     initEvents : function(){
28030         // Roo.form.Checkbox.superclass.initEvents.call(this);
28031         // has no events...
28032        
28033     },
28034
28035
28036     getResizeEl : function(){
28037         return this.wrap;
28038     },
28039
28040     getPositionEl : function(){
28041         return this.wrap;
28042     },
28043
28044     // private
28045     onRender : function(ct, position){
28046         
28047         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28048         var style = this.style;
28049         delete this.style;
28050         
28051         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28052         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28053         this.viewEl = this.wrap.createChild({ tag: 'div' });
28054         if (style) {
28055             this.viewEl.applyStyles(style);
28056         }
28057         if (this.width) {
28058             this.viewEl.setWidth(this.width);
28059         }
28060         if (this.height) {
28061             this.viewEl.setHeight(this.height);
28062         }
28063         //if(this.inputValue !== undefined){
28064         //this.setValue(this.value);
28065         
28066         
28067         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28068         
28069         
28070         this.grid.render();
28071         this.grid.getDataSource().on('remove', this.refreshValue, this);
28072         this.grid.getDataSource().on('update', this.refreshValue, this);
28073         this.grid.on('afteredit', this.refreshValue, this);
28074  
28075     },
28076      
28077     
28078     /**
28079      * Sets the value of the item. 
28080      * @param {String} either an object  or a string..
28081      */
28082     setValue : function(v){
28083         //this.value = v;
28084         v = v || []; // empty set..
28085         // this does not seem smart - it really only affects memoryproxy grids..
28086         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28087             var ds = this.grid.getDataSource();
28088             // assumes a json reader..
28089             var data = {}
28090             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28091             ds.loadData( data);
28092         }
28093         Roo.form.GridField.superclass.setValue.call(this, v);
28094         this.refreshValue();
28095         // should load data in the grid really....
28096     },
28097     
28098     // private
28099     refreshValue: function() {
28100          var val = [];
28101         this.grid.getDataSource().each(function(r) {
28102             val.push(r.data);
28103         });
28104         this.el.dom.value = Roo.encode(val);
28105     }
28106     
28107      
28108     
28109     
28110 });/*
28111  * Based on:
28112  * Ext JS Library 1.1.1
28113  * Copyright(c) 2006-2007, Ext JS, LLC.
28114  *
28115  * Originally Released Under LGPL - original licence link has changed is not relivant.
28116  *
28117  * Fork - LGPL
28118  * <script type="text/javascript">
28119  */
28120 /**
28121  * @class Roo.form.DisplayField
28122  * @extends Roo.form.Field
28123  * A generic Field to display non-editable data.
28124  * @constructor
28125  * Creates a new Display Field item.
28126  * @param {Object} config Configuration options
28127  */
28128 Roo.form.DisplayField = function(config){
28129     Roo.form.DisplayField.superclass.constructor.call(this, config);
28130     
28131 };
28132
28133 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28134     inputType:      'hidden',
28135     allowBlank:     true,
28136     readOnly:         true,
28137     
28138  
28139     /**
28140      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28141      */
28142     focusClass : undefined,
28143     /**
28144      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28145      */
28146     fieldClass: 'x-form-field',
28147     
28148      /**
28149      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28150      */
28151     valueRenderer: undefined,
28152     
28153     width: 100,
28154     /**
28155      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28156      * {tag: "input", type: "checkbox", autocomplete: "off"})
28157      */
28158      
28159  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28160
28161     onResize : function(){
28162         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28163         
28164     },
28165
28166     initEvents : function(){
28167         // Roo.form.Checkbox.superclass.initEvents.call(this);
28168         // has no events...
28169        
28170     },
28171
28172
28173     getResizeEl : function(){
28174         return this.wrap;
28175     },
28176
28177     getPositionEl : function(){
28178         return this.wrap;
28179     },
28180
28181     // private
28182     onRender : function(ct, position){
28183         
28184         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28185         //if(this.inputValue !== undefined){
28186         this.wrap = this.el.wrap();
28187         
28188         this.viewEl = this.wrap.createChild({ tag: 'div'});
28189         
28190         if (this.bodyStyle) {
28191             this.viewEl.applyStyles(this.bodyStyle);
28192         }
28193         //this.viewEl.setStyle('padding', '2px');
28194         
28195         this.setValue(this.value);
28196         
28197     },
28198 /*
28199     // private
28200     initValue : Roo.emptyFn,
28201
28202   */
28203
28204         // private
28205     onClick : function(){
28206         
28207     },
28208
28209     /**
28210      * Sets the checked state of the checkbox.
28211      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28212      */
28213     setValue : function(v){
28214         this.value = v;
28215         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28216         // this might be called before we have a dom element..
28217         if (!this.viewEl) {
28218             return;
28219         }
28220         this.viewEl.dom.innerHTML = html;
28221         Roo.form.DisplayField.superclass.setValue.call(this, v);
28222
28223     }
28224 });//<script type="text/javasscript">
28225  
28226
28227 /**
28228  * @class Roo.DDView
28229  * A DnD enabled version of Roo.View.
28230  * @param {Element/String} container The Element in which to create the View.
28231  * @param {String} tpl The template string used to create the markup for each element of the View
28232  * @param {Object} config The configuration properties. These include all the config options of
28233  * {@link Roo.View} plus some specific to this class.<br>
28234  * <p>
28235  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28236  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28237  * <p>
28238  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28239 .x-view-drag-insert-above {
28240         border-top:1px dotted #3366cc;
28241 }
28242 .x-view-drag-insert-below {
28243         border-bottom:1px dotted #3366cc;
28244 }
28245 </code></pre>
28246  * 
28247  */
28248  
28249 Roo.DDView = function(container, tpl, config) {
28250     Roo.DDView.superclass.constructor.apply(this, arguments);
28251     this.getEl().setStyle("outline", "0px none");
28252     this.getEl().unselectable();
28253     if (this.dragGroup) {
28254                 this.setDraggable(this.dragGroup.split(","));
28255     }
28256     if (this.dropGroup) {
28257                 this.setDroppable(this.dropGroup.split(","));
28258     }
28259     if (this.deletable) {
28260         this.setDeletable();
28261     }
28262     this.isDirtyFlag = false;
28263         this.addEvents({
28264                 "drop" : true
28265         });
28266 };
28267
28268 Roo.extend(Roo.DDView, Roo.View, {
28269 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28270 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28271 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28272 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28273
28274         isFormField: true,
28275
28276         reset: Roo.emptyFn,
28277         
28278         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28279
28280         validate: function() {
28281                 return true;
28282         },
28283         
28284         destroy: function() {
28285                 this.purgeListeners();
28286                 this.getEl.removeAllListeners();
28287                 this.getEl().remove();
28288                 if (this.dragZone) {
28289                         if (this.dragZone.destroy) {
28290                                 this.dragZone.destroy();
28291                         }
28292                 }
28293                 if (this.dropZone) {
28294                         if (this.dropZone.destroy) {
28295                                 this.dropZone.destroy();
28296                         }
28297                 }
28298         },
28299
28300 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28301         getName: function() {
28302                 return this.name;
28303         },
28304
28305 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28306         setValue: function(v) {
28307                 if (!this.store) {
28308                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28309                 }
28310                 var data = {};
28311                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28312                 this.store.proxy = new Roo.data.MemoryProxy(data);
28313                 this.store.load();
28314         },
28315
28316 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28317         getValue: function() {
28318                 var result = '(';
28319                 this.store.each(function(rec) {
28320                         result += rec.id + ',';
28321                 });
28322                 return result.substr(0, result.length - 1) + ')';
28323         },
28324         
28325         getIds: function() {
28326                 var i = 0, result = new Array(this.store.getCount());
28327                 this.store.each(function(rec) {
28328                         result[i++] = rec.id;
28329                 });
28330                 return result;
28331         },
28332         
28333         isDirty: function() {
28334                 return this.isDirtyFlag;
28335         },
28336
28337 /**
28338  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28339  *      whole Element becomes the target, and this causes the drop gesture to append.
28340  */
28341     getTargetFromEvent : function(e) {
28342                 var target = e.getTarget();
28343                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28344                 target = target.parentNode;
28345                 }
28346                 if (!target) {
28347                         target = this.el.dom.lastChild || this.el.dom;
28348                 }
28349                 return target;
28350     },
28351
28352 /**
28353  *      Create the drag data which consists of an object which has the property "ddel" as
28354  *      the drag proxy element. 
28355  */
28356     getDragData : function(e) {
28357         var target = this.findItemFromChild(e.getTarget());
28358                 if(target) {
28359                         this.handleSelection(e);
28360                         var selNodes = this.getSelectedNodes();
28361             var dragData = {
28362                 source: this,
28363                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28364                 nodes: selNodes,
28365                 records: []
28366                         };
28367                         var selectedIndices = this.getSelectedIndexes();
28368                         for (var i = 0; i < selectedIndices.length; i++) {
28369                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28370                         }
28371                         if (selNodes.length == 1) {
28372                                 dragData.ddel = target.cloneNode(true); // the div element
28373                         } else {
28374                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28375                                 div.className = 'multi-proxy';
28376                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28377                                         div.appendChild(selNodes[i].cloneNode(true));
28378                                 }
28379                                 dragData.ddel = div;
28380                         }
28381             //console.log(dragData)
28382             //console.log(dragData.ddel.innerHTML)
28383                         return dragData;
28384                 }
28385         //console.log('nodragData')
28386                 return false;
28387     },
28388     
28389 /**     Specify to which ddGroup items in this DDView may be dragged. */
28390     setDraggable: function(ddGroup) {
28391         if (ddGroup instanceof Array) {
28392                 Roo.each(ddGroup, this.setDraggable, this);
28393                 return;
28394         }
28395         if (this.dragZone) {
28396                 this.dragZone.addToGroup(ddGroup);
28397         } else {
28398                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28399                                 containerScroll: true,
28400                                 ddGroup: ddGroup 
28401
28402                         });
28403 //                      Draggability implies selection. DragZone's mousedown selects the element.
28404                         if (!this.multiSelect) { this.singleSelect = true; }
28405
28406 //                      Wire the DragZone's handlers up to methods in *this*
28407                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28408                 }
28409     },
28410
28411 /**     Specify from which ddGroup this DDView accepts drops. */
28412     setDroppable: function(ddGroup) {
28413         if (ddGroup instanceof Array) {
28414                 Roo.each(ddGroup, this.setDroppable, this);
28415                 return;
28416         }
28417         if (this.dropZone) {
28418                 this.dropZone.addToGroup(ddGroup);
28419         } else {
28420                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28421                                 containerScroll: true,
28422                                 ddGroup: ddGroup
28423                         });
28424
28425 //                      Wire the DropZone's handlers up to methods in *this*
28426                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28427                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28428                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28429                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28430                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28431                 }
28432     },
28433
28434 /**     Decide whether to drop above or below a View node. */
28435     getDropPoint : function(e, n, dd){
28436         if (n == this.el.dom) { return "above"; }
28437                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28438                 var c = t + (b - t) / 2;
28439                 var y = Roo.lib.Event.getPageY(e);
28440                 if(y <= c) {
28441                         return "above";
28442                 }else{
28443                         return "below";
28444                 }
28445     },
28446
28447     onNodeEnter : function(n, dd, e, data){
28448                 return false;
28449     },
28450     
28451     onNodeOver : function(n, dd, e, data){
28452                 var pt = this.getDropPoint(e, n, dd);
28453                 // set the insert point style on the target node
28454                 var dragElClass = this.dropNotAllowed;
28455                 if (pt) {
28456                         var targetElClass;
28457                         if (pt == "above"){
28458                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28459                                 targetElClass = "x-view-drag-insert-above";
28460                         } else {
28461                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28462                                 targetElClass = "x-view-drag-insert-below";
28463                         }
28464                         if (this.lastInsertClass != targetElClass){
28465                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28466                                 this.lastInsertClass = targetElClass;
28467                         }
28468                 }
28469                 return dragElClass;
28470         },
28471
28472     onNodeOut : function(n, dd, e, data){
28473                 this.removeDropIndicators(n);
28474     },
28475
28476     onNodeDrop : function(n, dd, e, data){
28477         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28478                 return false;
28479         }
28480         var pt = this.getDropPoint(e, n, dd);
28481                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28482                 if (pt == "below") { insertAt++; }
28483                 for (var i = 0; i < data.records.length; i++) {
28484                         var r = data.records[i];
28485                         var dup = this.store.getById(r.id);
28486                         if (dup && (dd != this.dragZone)) {
28487                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28488                         } else {
28489                                 if (data.copy) {
28490                                         this.store.insert(insertAt++, r.copy());
28491                                 } else {
28492                                         data.source.isDirtyFlag = true;
28493                                         r.store.remove(r);
28494                                         this.store.insert(insertAt++, r);
28495                                 }
28496                                 this.isDirtyFlag = true;
28497                         }
28498                 }
28499                 this.dragZone.cachedTarget = null;
28500                 return true;
28501     },
28502
28503     removeDropIndicators : function(n){
28504                 if(n){
28505                         Roo.fly(n).removeClass([
28506                                 "x-view-drag-insert-above",
28507                                 "x-view-drag-insert-below"]);
28508                         this.lastInsertClass = "_noclass";
28509                 }
28510     },
28511
28512 /**
28513  *      Utility method. Add a delete option to the DDView's context menu.
28514  *      @param {String} imageUrl The URL of the "delete" icon image.
28515  */
28516         setDeletable: function(imageUrl) {
28517                 if (!this.singleSelect && !this.multiSelect) {
28518                         this.singleSelect = true;
28519                 }
28520                 var c = this.getContextMenu();
28521                 this.contextMenu.on("itemclick", function(item) {
28522                         switch (item.id) {
28523                                 case "delete":
28524                                         this.remove(this.getSelectedIndexes());
28525                                         break;
28526                         }
28527                 }, this);
28528                 this.contextMenu.add({
28529                         icon: imageUrl,
28530                         id: "delete",
28531                         text: 'Delete'
28532                 });
28533         },
28534         
28535 /**     Return the context menu for this DDView. */
28536         getContextMenu: function() {
28537                 if (!this.contextMenu) {
28538 //                      Create the View's context menu
28539                         this.contextMenu = new Roo.menu.Menu({
28540                                 id: this.id + "-contextmenu"
28541                         });
28542                         this.el.on("contextmenu", this.showContextMenu, this);
28543                 }
28544                 return this.contextMenu;
28545         },
28546         
28547         disableContextMenu: function() {
28548                 if (this.contextMenu) {
28549                         this.el.un("contextmenu", this.showContextMenu, this);
28550                 }
28551         },
28552
28553         showContextMenu: function(e, item) {
28554         item = this.findItemFromChild(e.getTarget());
28555                 if (item) {
28556                         e.stopEvent();
28557                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28558                         this.contextMenu.showAt(e.getXY());
28559             }
28560     },
28561
28562 /**
28563  *      Remove {@link Roo.data.Record}s at the specified indices.
28564  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28565  */
28566     remove: function(selectedIndices) {
28567                 selectedIndices = [].concat(selectedIndices);
28568                 for (var i = 0; i < selectedIndices.length; i++) {
28569                         var rec = this.store.getAt(selectedIndices[i]);
28570                         this.store.remove(rec);
28571                 }
28572     },
28573
28574 /**
28575  *      Double click fires the event, but also, if this is draggable, and there is only one other
28576  *      related DropZone, it transfers the selected node.
28577  */
28578     onDblClick : function(e){
28579         var item = this.findItemFromChild(e.getTarget());
28580         if(item){
28581             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28582                 return false;
28583             }
28584             if (this.dragGroup) {
28585                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28586                     while (targets.indexOf(this.dropZone) > -1) {
28587                             targets.remove(this.dropZone);
28588                                 }
28589                     if (targets.length == 1) {
28590                                         this.dragZone.cachedTarget = null;
28591                         var el = Roo.get(targets[0].getEl());
28592                         var box = el.getBox(true);
28593                         targets[0].onNodeDrop(el.dom, {
28594                                 target: el.dom,
28595                                 xy: [box.x, box.y + box.height - 1]
28596                         }, null, this.getDragData(e));
28597                     }
28598                 }
28599         }
28600     },
28601     
28602     handleSelection: function(e) {
28603                 this.dragZone.cachedTarget = null;
28604         var item = this.findItemFromChild(e.getTarget());
28605         if (!item) {
28606                 this.clearSelections(true);
28607                 return;
28608         }
28609                 if (item && (this.multiSelect || this.singleSelect)){
28610                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28611                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28612                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28613                                 this.unselect(item);
28614                         } else {
28615                                 this.select(item, this.multiSelect && e.ctrlKey);
28616                                 this.lastSelection = item;
28617                         }
28618                 }
28619     },
28620
28621     onItemClick : function(item, index, e){
28622                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28623                         return false;
28624                 }
28625                 return true;
28626     },
28627
28628     unselect : function(nodeInfo, suppressEvent){
28629                 var node = this.getNode(nodeInfo);
28630                 if(node && this.isSelected(node)){
28631                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28632                                 Roo.fly(node).removeClass(this.selectedClass);
28633                                 this.selections.remove(node);
28634                                 if(!suppressEvent){
28635                                         this.fireEvent("selectionchange", this, this.selections);
28636                                 }
28637                         }
28638                 }
28639     }
28640 });
28641 /*
28642  * Based on:
28643  * Ext JS Library 1.1.1
28644  * Copyright(c) 2006-2007, Ext JS, LLC.
28645  *
28646  * Originally Released Under LGPL - original licence link has changed is not relivant.
28647  *
28648  * Fork - LGPL
28649  * <script type="text/javascript">
28650  */
28651  
28652 /**
28653  * @class Roo.LayoutManager
28654  * @extends Roo.util.Observable
28655  * Base class for layout managers.
28656  */
28657 Roo.LayoutManager = function(container, config){
28658     Roo.LayoutManager.superclass.constructor.call(this);
28659     this.el = Roo.get(container);
28660     // ie scrollbar fix
28661     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28662         document.body.scroll = "no";
28663     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28664         this.el.position('relative');
28665     }
28666     this.id = this.el.id;
28667     this.el.addClass("x-layout-container");
28668     /** false to disable window resize monitoring @type Boolean */
28669     this.monitorWindowResize = true;
28670     this.regions = {};
28671     this.addEvents({
28672         /**
28673          * @event layout
28674          * Fires when a layout is performed. 
28675          * @param {Roo.LayoutManager} this
28676          */
28677         "layout" : true,
28678         /**
28679          * @event regionresized
28680          * Fires when the user resizes a region. 
28681          * @param {Roo.LayoutRegion} region The resized region
28682          * @param {Number} newSize The new size (width for east/west, height for north/south)
28683          */
28684         "regionresized" : true,
28685         /**
28686          * @event regioncollapsed
28687          * Fires when a region is collapsed. 
28688          * @param {Roo.LayoutRegion} region The collapsed region
28689          */
28690         "regioncollapsed" : true,
28691         /**
28692          * @event regionexpanded
28693          * Fires when a region is expanded.  
28694          * @param {Roo.LayoutRegion} region The expanded region
28695          */
28696         "regionexpanded" : true
28697     });
28698     this.updating = false;
28699     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28700 };
28701
28702 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28703     /**
28704      * Returns true if this layout is currently being updated
28705      * @return {Boolean}
28706      */
28707     isUpdating : function(){
28708         return this.updating; 
28709     },
28710     
28711     /**
28712      * Suspend the LayoutManager from doing auto-layouts while
28713      * making multiple add or remove calls
28714      */
28715     beginUpdate : function(){
28716         this.updating = true;    
28717     },
28718     
28719     /**
28720      * Restore auto-layouts and optionally disable the manager from performing a layout
28721      * @param {Boolean} noLayout true to disable a layout update 
28722      */
28723     endUpdate : function(noLayout){
28724         this.updating = false;
28725         if(!noLayout){
28726             this.layout();
28727         }    
28728     },
28729     
28730     layout: function(){
28731         
28732     },
28733     
28734     onRegionResized : function(region, newSize){
28735         this.fireEvent("regionresized", region, newSize);
28736         this.layout();
28737     },
28738     
28739     onRegionCollapsed : function(region){
28740         this.fireEvent("regioncollapsed", region);
28741     },
28742     
28743     onRegionExpanded : function(region){
28744         this.fireEvent("regionexpanded", region);
28745     },
28746         
28747     /**
28748      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28749      * performs box-model adjustments.
28750      * @return {Object} The size as an object {width: (the width), height: (the height)}
28751      */
28752     getViewSize : function(){
28753         var size;
28754         if(this.el.dom != document.body){
28755             size = this.el.getSize();
28756         }else{
28757             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28758         }
28759         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28760         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28761         return size;
28762     },
28763     
28764     /**
28765      * Returns the Element this layout is bound to.
28766      * @return {Roo.Element}
28767      */
28768     getEl : function(){
28769         return this.el;
28770     },
28771     
28772     /**
28773      * Returns the specified region.
28774      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28775      * @return {Roo.LayoutRegion}
28776      */
28777     getRegion : function(target){
28778         return this.regions[target.toLowerCase()];
28779     },
28780     
28781     onWindowResize : function(){
28782         if(this.monitorWindowResize){
28783             this.layout();
28784         }
28785     }
28786 });/*
28787  * Based on:
28788  * Ext JS Library 1.1.1
28789  * Copyright(c) 2006-2007, Ext JS, LLC.
28790  *
28791  * Originally Released Under LGPL - original licence link has changed is not relivant.
28792  *
28793  * Fork - LGPL
28794  * <script type="text/javascript">
28795  */
28796 /**
28797  * @class Roo.BorderLayout
28798  * @extends Roo.LayoutManager
28799  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28800  * please see: <br><br>
28801  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
28802  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
28803  * Example:
28804  <pre><code>
28805  var layout = new Roo.BorderLayout(document.body, {
28806     north: {
28807         initialSize: 25,
28808         titlebar: false
28809     },
28810     west: {
28811         split:true,
28812         initialSize: 200,
28813         minSize: 175,
28814         maxSize: 400,
28815         titlebar: true,
28816         collapsible: true
28817     },
28818     east: {
28819         split:true,
28820         initialSize: 202,
28821         minSize: 175,
28822         maxSize: 400,
28823         titlebar: true,
28824         collapsible: true
28825     },
28826     south: {
28827         split:true,
28828         initialSize: 100,
28829         minSize: 100,
28830         maxSize: 200,
28831         titlebar: true,
28832         collapsible: true
28833     },
28834     center: {
28835         titlebar: true,
28836         autoScroll:true,
28837         resizeTabs: true,
28838         minTabWidth: 50,
28839         preferredTabWidth: 150
28840     }
28841 });
28842
28843 // shorthand
28844 var CP = Roo.ContentPanel;
28845
28846 layout.beginUpdate();
28847 layout.add("north", new CP("north", "North"));
28848 layout.add("south", new CP("south", {title: "South", closable: true}));
28849 layout.add("west", new CP("west", {title: "West"}));
28850 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28851 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28852 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28853 layout.getRegion("center").showPanel("center1");
28854 layout.endUpdate();
28855 </code></pre>
28856
28857 <b>The container the layout is rendered into can be either the body element or any other element.
28858 If it is not the body element, the container needs to either be an absolute positioned element,
28859 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28860 the container size if it is not the body element.</b>
28861
28862 * @constructor
28863 * Create a new BorderLayout
28864 * @param {String/HTMLElement/Element} container The container this layout is bound to
28865 * @param {Object} config Configuration options
28866  */
28867 Roo.BorderLayout = function(container, config){
28868     config = config || {};
28869     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28870     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28871     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28872         var target = this.factory.validRegions[i];
28873         if(config[target]){
28874             this.addRegion(target, config[target]);
28875         }
28876     }
28877 };
28878
28879 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28880     /**
28881      * Creates and adds a new region if it doesn't already exist.
28882      * @param {String} target The target region key (north, south, east, west or center).
28883      * @param {Object} config The regions config object
28884      * @return {BorderLayoutRegion} The new region
28885      */
28886     addRegion : function(target, config){
28887         if(!this.regions[target]){
28888             var r = this.factory.create(target, this, config);
28889             this.bindRegion(target, r);
28890         }
28891         return this.regions[target];
28892     },
28893
28894     // private (kinda)
28895     bindRegion : function(name, r){
28896         this.regions[name] = r;
28897         r.on("visibilitychange", this.layout, this);
28898         r.on("paneladded", this.layout, this);
28899         r.on("panelremoved", this.layout, this);
28900         r.on("invalidated", this.layout, this);
28901         r.on("resized", this.onRegionResized, this);
28902         r.on("collapsed", this.onRegionCollapsed, this);
28903         r.on("expanded", this.onRegionExpanded, this);
28904     },
28905
28906     /**
28907      * Performs a layout update.
28908      */
28909     layout : function(){
28910         if(this.updating) return;
28911         var size = this.getViewSize();
28912         var w = size.width;
28913         var h = size.height;
28914         var centerW = w;
28915         var centerH = h;
28916         var centerY = 0;
28917         var centerX = 0;
28918         //var x = 0, y = 0;
28919
28920         var rs = this.regions;
28921         var north = rs["north"];
28922         var south = rs["south"]; 
28923         var west = rs["west"];
28924         var east = rs["east"];
28925         var center = rs["center"];
28926         //if(this.hideOnLayout){ // not supported anymore
28927             //c.el.setStyle("display", "none");
28928         //}
28929         if(north && north.isVisible()){
28930             var b = north.getBox();
28931             var m = north.getMargins();
28932             b.width = w - (m.left+m.right);
28933             b.x = m.left;
28934             b.y = m.top;
28935             centerY = b.height + b.y + m.bottom;
28936             centerH -= centerY;
28937             north.updateBox(this.safeBox(b));
28938         }
28939         if(south && south.isVisible()){
28940             var b = south.getBox();
28941             var m = south.getMargins();
28942             b.width = w - (m.left+m.right);
28943             b.x = m.left;
28944             var totalHeight = (b.height + m.top + m.bottom);
28945             b.y = h - totalHeight + m.top;
28946             centerH -= totalHeight;
28947             south.updateBox(this.safeBox(b));
28948         }
28949         if(west && west.isVisible()){
28950             var b = west.getBox();
28951             var m = west.getMargins();
28952             b.height = centerH - (m.top+m.bottom);
28953             b.x = m.left;
28954             b.y = centerY + m.top;
28955             var totalWidth = (b.width + m.left + m.right);
28956             centerX += totalWidth;
28957             centerW -= totalWidth;
28958             west.updateBox(this.safeBox(b));
28959         }
28960         if(east && east.isVisible()){
28961             var b = east.getBox();
28962             var m = east.getMargins();
28963             b.height = centerH - (m.top+m.bottom);
28964             var totalWidth = (b.width + m.left + m.right);
28965             b.x = w - totalWidth + m.left;
28966             b.y = centerY + m.top;
28967             centerW -= totalWidth;
28968             east.updateBox(this.safeBox(b));
28969         }
28970         if(center){
28971             var m = center.getMargins();
28972             var centerBox = {
28973                 x: centerX + m.left,
28974                 y: centerY + m.top,
28975                 width: centerW - (m.left+m.right),
28976                 height: centerH - (m.top+m.bottom)
28977             };
28978             //if(this.hideOnLayout){
28979                 //center.el.setStyle("display", "block");
28980             //}
28981             center.updateBox(this.safeBox(centerBox));
28982         }
28983         this.el.repaint();
28984         this.fireEvent("layout", this);
28985     },
28986
28987     // private
28988     safeBox : function(box){
28989         box.width = Math.max(0, box.width);
28990         box.height = Math.max(0, box.height);
28991         return box;
28992     },
28993
28994     /**
28995      * Adds a ContentPanel (or subclass) to this layout.
28996      * @param {String} target The target region key (north, south, east, west or center).
28997      * @param {Roo.ContentPanel} panel The panel to add
28998      * @return {Roo.ContentPanel} The added panel
28999      */
29000     add : function(target, panel){
29001          
29002         target = target.toLowerCase();
29003         return this.regions[target].add(panel);
29004     },
29005
29006     /**
29007      * Remove a ContentPanel (or subclass) to this layout.
29008      * @param {String} target The target region key (north, south, east, west or center).
29009      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29010      * @return {Roo.ContentPanel} The removed panel
29011      */
29012     remove : function(target, panel){
29013         target = target.toLowerCase();
29014         return this.regions[target].remove(panel);
29015     },
29016
29017     /**
29018      * Searches all regions for a panel with the specified id
29019      * @param {String} panelId
29020      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29021      */
29022     findPanel : function(panelId){
29023         var rs = this.regions;
29024         for(var target in rs){
29025             if(typeof rs[target] != "function"){
29026                 var p = rs[target].getPanel(panelId);
29027                 if(p){
29028                     return p;
29029                 }
29030             }
29031         }
29032         return null;
29033     },
29034
29035     /**
29036      * Searches all regions for a panel with the specified id and activates (shows) it.
29037      * @param {String/ContentPanel} panelId The panels id or the panel itself
29038      * @return {Roo.ContentPanel} The shown panel or null
29039      */
29040     showPanel : function(panelId) {
29041       var rs = this.regions;
29042       for(var target in rs){
29043          var r = rs[target];
29044          if(typeof r != "function"){
29045             if(r.hasPanel(panelId)){
29046                return r.showPanel(panelId);
29047             }
29048          }
29049       }
29050       return null;
29051    },
29052
29053    /**
29054      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29055      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29056      */
29057     restoreState : function(provider){
29058         if(!provider){
29059             provider = Roo.state.Manager;
29060         }
29061         var sm = new Roo.LayoutStateManager();
29062         sm.init(this, provider);
29063     },
29064
29065     /**
29066      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29067      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29068      * a valid ContentPanel config object.  Example:
29069      * <pre><code>
29070 // Create the main layout
29071 var layout = new Roo.BorderLayout('main-ct', {
29072     west: {
29073         split:true,
29074         minSize: 175,
29075         titlebar: true
29076     },
29077     center: {
29078         title:'Components'
29079     }
29080 }, 'main-ct');
29081
29082 // Create and add multiple ContentPanels at once via configs
29083 layout.batchAdd({
29084    west: {
29085        id: 'source-files',
29086        autoCreate:true,
29087        title:'Ext Source Files',
29088        autoScroll:true,
29089        fitToFrame:true
29090    },
29091    center : {
29092        el: cview,
29093        autoScroll:true,
29094        fitToFrame:true,
29095        toolbar: tb,
29096        resizeEl:'cbody'
29097    }
29098 });
29099 </code></pre>
29100      * @param {Object} regions An object containing ContentPanel configs by region name
29101      */
29102     batchAdd : function(regions){
29103         this.beginUpdate();
29104         for(var rname in regions){
29105             var lr = this.regions[rname];
29106             if(lr){
29107                 this.addTypedPanels(lr, regions[rname]);
29108             }
29109         }
29110         this.endUpdate();
29111     },
29112
29113     // private
29114     addTypedPanels : function(lr, ps){
29115         if(typeof ps == 'string'){
29116             lr.add(new Roo.ContentPanel(ps));
29117         }
29118         else if(ps instanceof Array){
29119             for(var i =0, len = ps.length; i < len; i++){
29120                 this.addTypedPanels(lr, ps[i]);
29121             }
29122         }
29123         else if(!ps.events){ // raw config?
29124             var el = ps.el;
29125             delete ps.el; // prevent conflict
29126             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29127         }
29128         else {  // panel object assumed!
29129             lr.add(ps);
29130         }
29131     },
29132     /**
29133      * Adds a xtype elements to the layout.
29134      * <pre><code>
29135
29136 layout.addxtype({
29137        xtype : 'ContentPanel',
29138        region: 'west',
29139        items: [ .... ]
29140    }
29141 );
29142
29143 layout.addxtype({
29144         xtype : 'NestedLayoutPanel',
29145         region: 'west',
29146         layout: {
29147            center: { },
29148            west: { }   
29149         },
29150         items : [ ... list of content panels or nested layout panels.. ]
29151    }
29152 );
29153 </code></pre>
29154      * @param {Object} cfg Xtype definition of item to add.
29155      */
29156     addxtype : function(cfg)
29157     {
29158         // basically accepts a pannel...
29159         // can accept a layout region..!?!?
29160        // console.log('BorderLayout add ' + cfg.xtype)
29161         
29162         if (!cfg.xtype.match(/Panel$/)) {
29163             return false;
29164         }
29165         var ret = false;
29166         var region = cfg.region;
29167         delete cfg.region;
29168         
29169           
29170         var xitems = [];
29171         if (cfg.items) {
29172             xitems = cfg.items;
29173             delete cfg.items;
29174         }
29175         
29176         
29177         switch(cfg.xtype) 
29178         {
29179             case 'ContentPanel':  // ContentPanel (el, cfg)
29180             case 'ScrollPanel':  // ContentPanel (el, cfg)
29181                 if(cfg.autoCreate) {
29182                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29183                 } else {
29184                     var el = this.el.createChild();
29185                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29186                 }
29187                 
29188                 this.add(region, ret);
29189                 break;
29190             
29191             
29192             case 'TreePanel': // our new panel!
29193                 cfg.el = this.el.createChild();
29194                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29195                 this.add(region, ret);
29196                 break;
29197             
29198             case 'NestedLayoutPanel': 
29199                 // create a new Layout (which is  a Border Layout...
29200                 var el = this.el.createChild();
29201                 var clayout = cfg.layout;
29202                 delete cfg.layout;
29203                 clayout.items   = clayout.items  || [];
29204                 // replace this exitems with the clayout ones..
29205                 xitems = clayout.items;
29206                  
29207                 
29208                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29209                     cfg.background = false;
29210                 }
29211                 var layout = new Roo.BorderLayout(el, clayout);
29212                 
29213                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29214                 //console.log('adding nested layout panel '  + cfg.toSource());
29215                 this.add(region, ret);
29216                 
29217                 break;
29218                 
29219             case 'GridPanel': 
29220             
29221                 // needs grid and region
29222                 
29223                 //var el = this.getRegion(region).el.createChild();
29224                 var el = this.el.createChild();
29225                 // create the grid first...
29226                 
29227                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29228                 delete cfg.grid;
29229                 if (region == 'center' && this.active ) {
29230                     cfg.background = false;
29231                 }
29232                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29233                 
29234                 this.add(region, ret);
29235                 if (cfg.background) {
29236                     ret.on('activate', function(gp) {
29237                         if (!gp.grid.rendered) {
29238                             gp.grid.render();
29239                         }
29240                     });
29241                 } else {
29242                     grid.render();
29243                 }
29244                 break;
29245            
29246                
29247                 
29248                 
29249             default: 
29250                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29251                 return;
29252              // GridPanel (grid, cfg)
29253             
29254         }
29255         this.beginUpdate();
29256         // add children..
29257         Roo.each(xitems, function(i)  {
29258             ret.addxtype(i);
29259         });
29260         this.endUpdate();
29261         return ret;
29262         
29263     }
29264 });
29265
29266 /**
29267  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29268  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29269  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29270  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29271  * <pre><code>
29272 // shorthand
29273 var CP = Roo.ContentPanel;
29274
29275 var layout = Roo.BorderLayout.create({
29276     north: {
29277         initialSize: 25,
29278         titlebar: false,
29279         panels: [new CP("north", "North")]
29280     },
29281     west: {
29282         split:true,
29283         initialSize: 200,
29284         minSize: 175,
29285         maxSize: 400,
29286         titlebar: true,
29287         collapsible: true,
29288         panels: [new CP("west", {title: "West"})]
29289     },
29290     east: {
29291         split:true,
29292         initialSize: 202,
29293         minSize: 175,
29294         maxSize: 400,
29295         titlebar: true,
29296         collapsible: true,
29297         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29298     },
29299     south: {
29300         split:true,
29301         initialSize: 100,
29302         minSize: 100,
29303         maxSize: 200,
29304         titlebar: true,
29305         collapsible: true,
29306         panels: [new CP("south", {title: "South", closable: true})]
29307     },
29308     center: {
29309         titlebar: true,
29310         autoScroll:true,
29311         resizeTabs: true,
29312         minTabWidth: 50,
29313         preferredTabWidth: 150,
29314         panels: [
29315             new CP("center1", {title: "Close Me", closable: true}),
29316             new CP("center2", {title: "Center Panel", closable: false})
29317         ]
29318     }
29319 }, document.body);
29320
29321 layout.getRegion("center").showPanel("center1");
29322 </code></pre>
29323  * @param config
29324  * @param targetEl
29325  */
29326 Roo.BorderLayout.create = function(config, targetEl){
29327     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29328     layout.beginUpdate();
29329     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29330     for(var j = 0, jlen = regions.length; j < jlen; j++){
29331         var lr = regions[j];
29332         if(layout.regions[lr] && config[lr].panels){
29333             var r = layout.regions[lr];
29334             var ps = config[lr].panels;
29335             layout.addTypedPanels(r, ps);
29336         }
29337     }
29338     layout.endUpdate();
29339     return layout;
29340 };
29341
29342 // private
29343 Roo.BorderLayout.RegionFactory = {
29344     // private
29345     validRegions : ["north","south","east","west","center"],
29346
29347     // private
29348     create : function(target, mgr, config){
29349         target = target.toLowerCase();
29350         if(config.lightweight || config.basic){
29351             return new Roo.BasicLayoutRegion(mgr, config, target);
29352         }
29353         switch(target){
29354             case "north":
29355                 return new Roo.NorthLayoutRegion(mgr, config);
29356             case "south":
29357                 return new Roo.SouthLayoutRegion(mgr, config);
29358             case "east":
29359                 return new Roo.EastLayoutRegion(mgr, config);
29360             case "west":
29361                 return new Roo.WestLayoutRegion(mgr, config);
29362             case "center":
29363                 return new Roo.CenterLayoutRegion(mgr, config);
29364         }
29365         throw 'Layout region "'+target+'" not supported.';
29366     }
29367 };/*
29368  * Based on:
29369  * Ext JS Library 1.1.1
29370  * Copyright(c) 2006-2007, Ext JS, LLC.
29371  *
29372  * Originally Released Under LGPL - original licence link has changed is not relivant.
29373  *
29374  * Fork - LGPL
29375  * <script type="text/javascript">
29376  */
29377  
29378 /**
29379  * @class Roo.BasicLayoutRegion
29380  * @extends Roo.util.Observable
29381  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29382  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29383  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29384  */
29385 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29386     this.mgr = mgr;
29387     this.position  = pos;
29388     this.events = {
29389         /**
29390          * @scope Roo.BasicLayoutRegion
29391          */
29392         
29393         /**
29394          * @event beforeremove
29395          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29396          * @param {Roo.LayoutRegion} this
29397          * @param {Roo.ContentPanel} panel The panel
29398          * @param {Object} e The cancel event object
29399          */
29400         "beforeremove" : true,
29401         /**
29402          * @event invalidated
29403          * Fires when the layout for this region is changed.
29404          * @param {Roo.LayoutRegion} this
29405          */
29406         "invalidated" : true,
29407         /**
29408          * @event visibilitychange
29409          * Fires when this region is shown or hidden 
29410          * @param {Roo.LayoutRegion} this
29411          * @param {Boolean} visibility true or false
29412          */
29413         "visibilitychange" : true,
29414         /**
29415          * @event paneladded
29416          * Fires when a panel is added. 
29417          * @param {Roo.LayoutRegion} this
29418          * @param {Roo.ContentPanel} panel The panel
29419          */
29420         "paneladded" : true,
29421         /**
29422          * @event panelremoved
29423          * Fires when a panel is removed. 
29424          * @param {Roo.LayoutRegion} this
29425          * @param {Roo.ContentPanel} panel The panel
29426          */
29427         "panelremoved" : true,
29428         /**
29429          * @event collapsed
29430          * Fires when this region is collapsed.
29431          * @param {Roo.LayoutRegion} this
29432          */
29433         "collapsed" : true,
29434         /**
29435          * @event expanded
29436          * Fires when this region is expanded.
29437          * @param {Roo.LayoutRegion} this
29438          */
29439         "expanded" : true,
29440         /**
29441          * @event slideshow
29442          * Fires when this region is slid into view.
29443          * @param {Roo.LayoutRegion} this
29444          */
29445         "slideshow" : true,
29446         /**
29447          * @event slidehide
29448          * Fires when this region slides out of view. 
29449          * @param {Roo.LayoutRegion} this
29450          */
29451         "slidehide" : true,
29452         /**
29453          * @event panelactivated
29454          * Fires when a panel is activated. 
29455          * @param {Roo.LayoutRegion} this
29456          * @param {Roo.ContentPanel} panel The activated panel
29457          */
29458         "panelactivated" : true,
29459         /**
29460          * @event resized
29461          * Fires when the user resizes this region. 
29462          * @param {Roo.LayoutRegion} this
29463          * @param {Number} newSize The new size (width for east/west, height for north/south)
29464          */
29465         "resized" : true
29466     };
29467     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29468     this.panels = new Roo.util.MixedCollection();
29469     this.panels.getKey = this.getPanelId.createDelegate(this);
29470     this.box = null;
29471     this.activePanel = null;
29472     // ensure listeners are added...
29473     
29474     if (config.listeners || config.events) {
29475         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29476             listeners : config.listeners || {},
29477             events : config.events || {}
29478         });
29479     }
29480     
29481     if(skipConfig !== true){
29482         this.applyConfig(config);
29483     }
29484 };
29485
29486 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29487     getPanelId : function(p){
29488         return p.getId();
29489     },
29490     
29491     applyConfig : function(config){
29492         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29493         this.config = config;
29494         
29495     },
29496     
29497     /**
29498      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29499      * the width, for horizontal (north, south) the height.
29500      * @param {Number} newSize The new width or height
29501      */
29502     resizeTo : function(newSize){
29503         var el = this.el ? this.el :
29504                  (this.activePanel ? this.activePanel.getEl() : null);
29505         if(el){
29506             switch(this.position){
29507                 case "east":
29508                 case "west":
29509                     el.setWidth(newSize);
29510                     this.fireEvent("resized", this, newSize);
29511                 break;
29512                 case "north":
29513                 case "south":
29514                     el.setHeight(newSize);
29515                     this.fireEvent("resized", this, newSize);
29516                 break;                
29517             }
29518         }
29519     },
29520     
29521     getBox : function(){
29522         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29523     },
29524     
29525     getMargins : function(){
29526         return this.margins;
29527     },
29528     
29529     updateBox : function(box){
29530         this.box = box;
29531         var el = this.activePanel.getEl();
29532         el.dom.style.left = box.x + "px";
29533         el.dom.style.top = box.y + "px";
29534         this.activePanel.setSize(box.width, box.height);
29535     },
29536     
29537     /**
29538      * Returns the container element for this region.
29539      * @return {Roo.Element}
29540      */
29541     getEl : function(){
29542         return this.activePanel;
29543     },
29544     
29545     /**
29546      * Returns true if this region is currently visible.
29547      * @return {Boolean}
29548      */
29549     isVisible : function(){
29550         return this.activePanel ? true : false;
29551     },
29552     
29553     setActivePanel : function(panel){
29554         panel = this.getPanel(panel);
29555         if(this.activePanel && this.activePanel != panel){
29556             this.activePanel.setActiveState(false);
29557             this.activePanel.getEl().setLeftTop(-10000,-10000);
29558         }
29559         this.activePanel = panel;
29560         panel.setActiveState(true);
29561         if(this.box){
29562             panel.setSize(this.box.width, this.box.height);
29563         }
29564         this.fireEvent("panelactivated", this, panel);
29565         this.fireEvent("invalidated");
29566     },
29567     
29568     /**
29569      * Show the specified panel.
29570      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29571      * @return {Roo.ContentPanel} The shown panel or null
29572      */
29573     showPanel : function(panel){
29574         if(panel = this.getPanel(panel)){
29575             this.setActivePanel(panel);
29576         }
29577         return panel;
29578     },
29579     
29580     /**
29581      * Get the active panel for this region.
29582      * @return {Roo.ContentPanel} The active panel or null
29583      */
29584     getActivePanel : function(){
29585         return this.activePanel;
29586     },
29587     
29588     /**
29589      * Add the passed ContentPanel(s)
29590      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29591      * @return {Roo.ContentPanel} The panel added (if only one was added)
29592      */
29593     add : function(panel){
29594         if(arguments.length > 1){
29595             for(var i = 0, len = arguments.length; i < len; i++) {
29596                 this.add(arguments[i]);
29597             }
29598             return null;
29599         }
29600         if(this.hasPanel(panel)){
29601             this.showPanel(panel);
29602             return panel;
29603         }
29604         var el = panel.getEl();
29605         if(el.dom.parentNode != this.mgr.el.dom){
29606             this.mgr.el.dom.appendChild(el.dom);
29607         }
29608         if(panel.setRegion){
29609             panel.setRegion(this);
29610         }
29611         this.panels.add(panel);
29612         el.setStyle("position", "absolute");
29613         if(!panel.background){
29614             this.setActivePanel(panel);
29615             if(this.config.initialSize && this.panels.getCount()==1){
29616                 this.resizeTo(this.config.initialSize);
29617             }
29618         }
29619         this.fireEvent("paneladded", this, panel);
29620         return panel;
29621     },
29622     
29623     /**
29624      * Returns true if the panel is in this region.
29625      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29626      * @return {Boolean}
29627      */
29628     hasPanel : function(panel){
29629         if(typeof panel == "object"){ // must be panel obj
29630             panel = panel.getId();
29631         }
29632         return this.getPanel(panel) ? true : false;
29633     },
29634     
29635     /**
29636      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29637      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29638      * @param {Boolean} preservePanel Overrides the config preservePanel option
29639      * @return {Roo.ContentPanel} The panel that was removed
29640      */
29641     remove : function(panel, preservePanel){
29642         panel = this.getPanel(panel);
29643         if(!panel){
29644             return null;
29645         }
29646         var e = {};
29647         this.fireEvent("beforeremove", this, panel, e);
29648         if(e.cancel === true){
29649             return null;
29650         }
29651         var panelId = panel.getId();
29652         this.panels.removeKey(panelId);
29653         return panel;
29654     },
29655     
29656     /**
29657      * Returns the panel specified or null if it's not in this region.
29658      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29659      * @return {Roo.ContentPanel}
29660      */
29661     getPanel : function(id){
29662         if(typeof id == "object"){ // must be panel obj
29663             return id;
29664         }
29665         return this.panels.get(id);
29666     },
29667     
29668     /**
29669      * Returns this regions position (north/south/east/west/center).
29670      * @return {String} 
29671      */
29672     getPosition: function(){
29673         return this.position;    
29674     }
29675 });/*
29676  * Based on:
29677  * Ext JS Library 1.1.1
29678  * Copyright(c) 2006-2007, Ext JS, LLC.
29679  *
29680  * Originally Released Under LGPL - original licence link has changed is not relivant.
29681  *
29682  * Fork - LGPL
29683  * <script type="text/javascript">
29684  */
29685  
29686 /**
29687  * @class Roo.LayoutRegion
29688  * @extends Roo.BasicLayoutRegion
29689  * This class represents a region in a layout manager.
29690  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29691  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29692  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29693  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29694  * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
29695  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29696  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29697  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29698  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29699  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29700  * @cfg {String} title The title for the region (overrides panel titles)
29701  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29702  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29703  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29704  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29705  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29706  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29707  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29708  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29709  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29710  * @cfg {Boolean} showPin True to show a pin button
29711 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29712 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29713 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29714 * @cfg {Number} width  For East/West panels
29715 * @cfg {Number} height For North/South panels
29716 * @cfg {Boolean} split To show the splitter
29717  */
29718 Roo.LayoutRegion = function(mgr, config, pos){
29719     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29720     var dh = Roo.DomHelper;
29721     /** This region's container element 
29722     * @type Roo.Element */
29723     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29724     /** This region's title element 
29725     * @type Roo.Element */
29726
29727     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29728         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29729         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29730     ]}, true);
29731     this.titleEl.enableDisplayMode();
29732     /** This region's title text element 
29733     * @type HTMLElement */
29734     this.titleTextEl = this.titleEl.dom.firstChild;
29735     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29736     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29737     this.closeBtn.enableDisplayMode();
29738     this.closeBtn.on("click", this.closeClicked, this);
29739     this.closeBtn.hide();
29740
29741     this.createBody(config);
29742     this.visible = true;
29743     this.collapsed = false;
29744
29745     if(config.hideWhenEmpty){
29746         this.hide();
29747         this.on("paneladded", this.validateVisibility, this);
29748         this.on("panelremoved", this.validateVisibility, this);
29749     }
29750     this.applyConfig(config);
29751 };
29752
29753 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29754
29755     createBody : function(){
29756         /** This region's body element 
29757         * @type Roo.Element */
29758         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29759     },
29760
29761     applyConfig : function(c){
29762         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29763             var dh = Roo.DomHelper;
29764             if(c.titlebar !== false){
29765                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29766                 this.collapseBtn.on("click", this.collapse, this);
29767                 this.collapseBtn.enableDisplayMode();
29768
29769                 if(c.showPin === true || this.showPin){
29770                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29771                     this.stickBtn.enableDisplayMode();
29772                     this.stickBtn.on("click", this.expand, this);
29773                     this.stickBtn.hide();
29774                 }
29775             }
29776             /** This region's collapsed element
29777             * @type Roo.Element */
29778             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29779                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29780             ]}, true);
29781             if(c.floatable !== false){
29782                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29783                this.collapsedEl.on("click", this.collapseClick, this);
29784             }
29785
29786             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29787                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29788                    id: "message", unselectable: "on", style:{"float":"left"}});
29789                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29790              }
29791             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29792             this.expandBtn.on("click", this.expand, this);
29793         }
29794         if(this.collapseBtn){
29795             this.collapseBtn.setVisible(c.collapsible == true);
29796         }
29797         this.cmargins = c.cmargins || this.cmargins ||
29798                          (this.position == "west" || this.position == "east" ?
29799                              {top: 0, left: 2, right:2, bottom: 0} :
29800                              {top: 2, left: 0, right:0, bottom: 2});
29801         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29802         this.bottomTabs = c.tabPosition != "top";
29803         this.autoScroll = c.autoScroll || false;
29804         if(this.autoScroll){
29805             this.bodyEl.setStyle("overflow", "auto");
29806         }else{
29807             this.bodyEl.setStyle("overflow", "hidden");
29808         }
29809         //if(c.titlebar !== false){
29810             if((!c.titlebar && !c.title) || c.titlebar === false){
29811                 this.titleEl.hide();
29812             }else{
29813                 this.titleEl.show();
29814                 if(c.title){
29815                     this.titleTextEl.innerHTML = c.title;
29816                 }
29817             }
29818         //}
29819         this.duration = c.duration || .30;
29820         this.slideDuration = c.slideDuration || .45;
29821         this.config = c;
29822         if(c.collapsed){
29823             this.collapse(true);
29824         }
29825         if(c.hidden){
29826             this.hide();
29827         }
29828     },
29829     /**
29830      * Returns true if this region is currently visible.
29831      * @return {Boolean}
29832      */
29833     isVisible : function(){
29834         return this.visible;
29835     },
29836
29837     /**
29838      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29839      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29840      */
29841     setCollapsedTitle : function(title){
29842         title = title || "&#160;";
29843         if(this.collapsedTitleTextEl){
29844             this.collapsedTitleTextEl.innerHTML = title;
29845         }
29846     },
29847
29848     getBox : function(){
29849         var b;
29850         if(!this.collapsed){
29851             b = this.el.getBox(false, true);
29852         }else{
29853             b = this.collapsedEl.getBox(false, true);
29854         }
29855         return b;
29856     },
29857
29858     getMargins : function(){
29859         return this.collapsed ? this.cmargins : this.margins;
29860     },
29861
29862     highlight : function(){
29863         this.el.addClass("x-layout-panel-dragover");
29864     },
29865
29866     unhighlight : function(){
29867         this.el.removeClass("x-layout-panel-dragover");
29868     },
29869
29870     updateBox : function(box){
29871         this.box = box;
29872         if(!this.collapsed){
29873             this.el.dom.style.left = box.x + "px";
29874             this.el.dom.style.top = box.y + "px";
29875             this.updateBody(box.width, box.height);
29876         }else{
29877             this.collapsedEl.dom.style.left = box.x + "px";
29878             this.collapsedEl.dom.style.top = box.y + "px";
29879             this.collapsedEl.setSize(box.width, box.height);
29880         }
29881         if(this.tabs){
29882             this.tabs.autoSizeTabs();
29883         }
29884     },
29885
29886     updateBody : function(w, h){
29887         if(w !== null){
29888             this.el.setWidth(w);
29889             w -= this.el.getBorderWidth("rl");
29890             if(this.config.adjustments){
29891                 w += this.config.adjustments[0];
29892             }
29893         }
29894         if(h !== null){
29895             this.el.setHeight(h);
29896             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29897             h -= this.el.getBorderWidth("tb");
29898             if(this.config.adjustments){
29899                 h += this.config.adjustments[1];
29900             }
29901             this.bodyEl.setHeight(h);
29902             if(this.tabs){
29903                 h = this.tabs.syncHeight(h);
29904             }
29905         }
29906         if(this.panelSize){
29907             w = w !== null ? w : this.panelSize.width;
29908             h = h !== null ? h : this.panelSize.height;
29909         }
29910         if(this.activePanel){
29911             var el = this.activePanel.getEl();
29912             w = w !== null ? w : el.getWidth();
29913             h = h !== null ? h : el.getHeight();
29914             this.panelSize = {width: w, height: h};
29915             this.activePanel.setSize(w, h);
29916         }
29917         if(Roo.isIE && this.tabs){
29918             this.tabs.el.repaint();
29919         }
29920     },
29921
29922     /**
29923      * Returns the container element for this region.
29924      * @return {Roo.Element}
29925      */
29926     getEl : function(){
29927         return this.el;
29928     },
29929
29930     /**
29931      * Hides this region.
29932      */
29933     hide : function(){
29934         if(!this.collapsed){
29935             this.el.dom.style.left = "-2000px";
29936             this.el.hide();
29937         }else{
29938             this.collapsedEl.dom.style.left = "-2000px";
29939             this.collapsedEl.hide();
29940         }
29941         this.visible = false;
29942         this.fireEvent("visibilitychange", this, false);
29943     },
29944
29945     /**
29946      * Shows this region if it was previously hidden.
29947      */
29948     show : function(){
29949         if(!this.collapsed){
29950             this.el.show();
29951         }else{
29952             this.collapsedEl.show();
29953         }
29954         this.visible = true;
29955         this.fireEvent("visibilitychange", this, true);
29956     },
29957
29958     closeClicked : function(){
29959         if(this.activePanel){
29960             this.remove(this.activePanel);
29961         }
29962     },
29963
29964     collapseClick : function(e){
29965         if(this.isSlid){
29966            e.stopPropagation();
29967            this.slideIn();
29968         }else{
29969            e.stopPropagation();
29970            this.slideOut();
29971         }
29972     },
29973
29974     /**
29975      * Collapses this region.
29976      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29977      */
29978     collapse : function(skipAnim){
29979         if(this.collapsed) return;
29980         this.collapsed = true;
29981         if(this.split){
29982             this.split.el.hide();
29983         }
29984         if(this.config.animate && skipAnim !== true){
29985             this.fireEvent("invalidated", this);
29986             this.animateCollapse();
29987         }else{
29988             this.el.setLocation(-20000,-20000);
29989             this.el.hide();
29990             this.collapsedEl.show();
29991             this.fireEvent("collapsed", this);
29992             this.fireEvent("invalidated", this);
29993         }
29994     },
29995
29996     animateCollapse : function(){
29997         // overridden
29998     },
29999
30000     /**
30001      * Expands this region if it was previously collapsed.
30002      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30003      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30004      */
30005     expand : function(e, skipAnim){
30006         if(e) e.stopPropagation();
30007         if(!this.collapsed || this.el.hasActiveFx()) return;
30008         if(this.isSlid){
30009             this.afterSlideIn();
30010             skipAnim = true;
30011         }
30012         this.collapsed = false;
30013         if(this.config.animate && skipAnim !== true){
30014             this.animateExpand();
30015         }else{
30016             this.el.show();
30017             if(this.split){
30018                 this.split.el.show();
30019             }
30020             this.collapsedEl.setLocation(-2000,-2000);
30021             this.collapsedEl.hide();
30022             this.fireEvent("invalidated", this);
30023             this.fireEvent("expanded", this);
30024         }
30025     },
30026
30027     animateExpand : function(){
30028         // overridden
30029     },
30030
30031     initTabs : function(){
30032         this.bodyEl.setStyle("overflow", "hidden");
30033         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30034             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30035             disableTooltips: this.config.disableTabTips
30036         });
30037         if(this.config.hideTabs){
30038             ts.stripWrap.setDisplayed(false);
30039         }
30040         this.tabs = ts;
30041         ts.resizeTabs = this.config.resizeTabs === true;
30042         ts.minTabWidth = this.config.minTabWidth || 40;
30043         ts.maxTabWidth = this.config.maxTabWidth || 250;
30044         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30045         ts.monitorResize = false;
30046         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30047         ts.bodyEl.addClass('x-layout-tabs-body');
30048         this.panels.each(this.initPanelAsTab, this);
30049     },
30050
30051     initPanelAsTab : function(panel){
30052         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30053                     this.config.closeOnTab && panel.isClosable());
30054         if(panel.tabTip !== undefined){
30055             ti.setTooltip(panel.tabTip);
30056         }
30057         ti.on("activate", function(){
30058               this.setActivePanel(panel);
30059         }, this);
30060         if(this.config.closeOnTab){
30061             ti.on("beforeclose", function(t, e){
30062                 e.cancel = true;
30063                 this.remove(panel);
30064             }, this);
30065         }
30066         return ti;
30067     },
30068
30069     updatePanelTitle : function(panel, title){
30070         if(this.activePanel == panel){
30071             this.updateTitle(title);
30072         }
30073         if(this.tabs){
30074             var ti = this.tabs.getTab(panel.getEl().id);
30075             ti.setText(title);
30076             if(panel.tabTip !== undefined){
30077                 ti.setTooltip(panel.tabTip);
30078             }
30079         }
30080     },
30081
30082     updateTitle : function(title){
30083         if(this.titleTextEl && !this.config.title){
30084             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30085         }
30086     },
30087
30088     setActivePanel : function(panel){
30089         panel = this.getPanel(panel);
30090         if(this.activePanel && this.activePanel != panel){
30091             this.activePanel.setActiveState(false);
30092         }
30093         this.activePanel = panel;
30094         panel.setActiveState(true);
30095         if(this.panelSize){
30096             panel.setSize(this.panelSize.width, this.panelSize.height);
30097         }
30098         if(this.closeBtn){
30099             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30100         }
30101         this.updateTitle(panel.getTitle());
30102         if(this.tabs){
30103             this.fireEvent("invalidated", this);
30104         }
30105         this.fireEvent("panelactivated", this, panel);
30106     },
30107
30108     /**
30109      * Shows the specified panel.
30110      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30111      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30112      */
30113     showPanel : function(panel){
30114         if(panel = this.getPanel(panel)){
30115             if(this.tabs){
30116                 var tab = this.tabs.getTab(panel.getEl().id);
30117                 if(tab.isHidden()){
30118                     this.tabs.unhideTab(tab.id);
30119                 }
30120                 tab.activate();
30121             }else{
30122                 this.setActivePanel(panel);
30123             }
30124         }
30125         return panel;
30126     },
30127
30128     /**
30129      * Get the active panel for this region.
30130      * @return {Roo.ContentPanel} The active panel or null
30131      */
30132     getActivePanel : function(){
30133         return this.activePanel;
30134     },
30135
30136     validateVisibility : function(){
30137         if(this.panels.getCount() < 1){
30138             this.updateTitle("&#160;");
30139             this.closeBtn.hide();
30140             this.hide();
30141         }else{
30142             if(!this.isVisible()){
30143                 this.show();
30144             }
30145         }
30146     },
30147
30148     /**
30149      * Adds the passed ContentPanel(s) to this region.
30150      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30151      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30152      */
30153     add : function(panel){
30154         if(arguments.length > 1){
30155             for(var i = 0, len = arguments.length; i < len; i++) {
30156                 this.add(arguments[i]);
30157             }
30158             return null;
30159         }
30160         if(this.hasPanel(panel)){
30161             this.showPanel(panel);
30162             return panel;
30163         }
30164         panel.setRegion(this);
30165         this.panels.add(panel);
30166         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30167             this.bodyEl.dom.appendChild(panel.getEl().dom);
30168             if(panel.background !== true){
30169                 this.setActivePanel(panel);
30170             }
30171             this.fireEvent("paneladded", this, panel);
30172             return panel;
30173         }
30174         if(!this.tabs){
30175             this.initTabs();
30176         }else{
30177             this.initPanelAsTab(panel);
30178         }
30179         if(panel.background !== true){
30180             this.tabs.activate(panel.getEl().id);
30181         }
30182         this.fireEvent("paneladded", this, panel);
30183         return panel;
30184     },
30185
30186     /**
30187      * Hides the tab for the specified panel.
30188      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30189      */
30190     hidePanel : function(panel){
30191         if(this.tabs && (panel = this.getPanel(panel))){
30192             this.tabs.hideTab(panel.getEl().id);
30193         }
30194     },
30195
30196     /**
30197      * Unhides the tab for a previously hidden panel.
30198      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30199      */
30200     unhidePanel : function(panel){
30201         if(this.tabs && (panel = this.getPanel(panel))){
30202             this.tabs.unhideTab(panel.getEl().id);
30203         }
30204     },
30205
30206     clearPanels : function(){
30207         while(this.panels.getCount() > 0){
30208              this.remove(this.panels.first());
30209         }
30210     },
30211
30212     /**
30213      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30214      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30215      * @param {Boolean} preservePanel Overrides the config preservePanel option
30216      * @return {Roo.ContentPanel} The panel that was removed
30217      */
30218     remove : function(panel, preservePanel){
30219         panel = this.getPanel(panel);
30220         if(!panel){
30221             return null;
30222         }
30223         var e = {};
30224         this.fireEvent("beforeremove", this, panel, e);
30225         if(e.cancel === true){
30226             return null;
30227         }
30228         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30229         var panelId = panel.getId();
30230         this.panels.removeKey(panelId);
30231         if(preservePanel){
30232             document.body.appendChild(panel.getEl().dom);
30233         }
30234         if(this.tabs){
30235             this.tabs.removeTab(panel.getEl().id);
30236         }else if (!preservePanel){
30237             this.bodyEl.dom.removeChild(panel.getEl().dom);
30238         }
30239         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30240             var p = this.panels.first();
30241             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30242             tempEl.appendChild(p.getEl().dom);
30243             this.bodyEl.update("");
30244             this.bodyEl.dom.appendChild(p.getEl().dom);
30245             tempEl = null;
30246             this.updateTitle(p.getTitle());
30247             this.tabs = null;
30248             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30249             this.setActivePanel(p);
30250         }
30251         panel.setRegion(null);
30252         if(this.activePanel == panel){
30253             this.activePanel = null;
30254         }
30255         if(this.config.autoDestroy !== false && preservePanel !== true){
30256             try{panel.destroy();}catch(e){}
30257         }
30258         this.fireEvent("panelremoved", this, panel);
30259         return panel;
30260     },
30261
30262     /**
30263      * Returns the TabPanel component used by this region
30264      * @return {Roo.TabPanel}
30265      */
30266     getTabs : function(){
30267         return this.tabs;
30268     },
30269
30270     createTool : function(parentEl, className){
30271         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30272             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30273         btn.addClassOnOver("x-layout-tools-button-over");
30274         return btn;
30275     }
30276 });/*
30277  * Based on:
30278  * Ext JS Library 1.1.1
30279  * Copyright(c) 2006-2007, Ext JS, LLC.
30280  *
30281  * Originally Released Under LGPL - original licence link has changed is not relivant.
30282  *
30283  * Fork - LGPL
30284  * <script type="text/javascript">
30285  */
30286  
30287
30288
30289 /**
30290  * @class Roo.SplitLayoutRegion
30291  * @extends Roo.LayoutRegion
30292  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30293  */
30294 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30295     this.cursor = cursor;
30296     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30297 };
30298
30299 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30300     splitTip : "Drag to resize.",
30301     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30302     useSplitTips : false,
30303
30304     applyConfig : function(config){
30305         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30306         if(config.split){
30307             if(!this.split){
30308                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30309                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30310                 /** The SplitBar for this region 
30311                 * @type Roo.SplitBar */
30312                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30313                 this.split.on("moved", this.onSplitMove, this);
30314                 this.split.useShim = config.useShim === true;
30315                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30316                 if(this.useSplitTips){
30317                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30318                 }
30319                 if(config.collapsible){
30320                     this.split.el.on("dblclick", this.collapse,  this);
30321                 }
30322             }
30323             if(typeof config.minSize != "undefined"){
30324                 this.split.minSize = config.minSize;
30325             }
30326             if(typeof config.maxSize != "undefined"){
30327                 this.split.maxSize = config.maxSize;
30328             }
30329             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30330                 this.hideSplitter();
30331             }
30332         }
30333     },
30334
30335     getHMaxSize : function(){
30336          var cmax = this.config.maxSize || 10000;
30337          var center = this.mgr.getRegion("center");
30338          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30339     },
30340
30341     getVMaxSize : function(){
30342          var cmax = this.config.maxSize || 10000;
30343          var center = this.mgr.getRegion("center");
30344          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30345     },
30346
30347     onSplitMove : function(split, newSize){
30348         this.fireEvent("resized", this, newSize);
30349     },
30350     
30351     /** 
30352      * Returns the {@link Roo.SplitBar} for this region.
30353      * @return {Roo.SplitBar}
30354      */
30355     getSplitBar : function(){
30356         return this.split;
30357     },
30358     
30359     hide : function(){
30360         this.hideSplitter();
30361         Roo.SplitLayoutRegion.superclass.hide.call(this);
30362     },
30363
30364     hideSplitter : function(){
30365         if(this.split){
30366             this.split.el.setLocation(-2000,-2000);
30367             this.split.el.hide();
30368         }
30369     },
30370
30371     show : function(){
30372         if(this.split){
30373             this.split.el.show();
30374         }
30375         Roo.SplitLayoutRegion.superclass.show.call(this);
30376     },
30377     
30378     beforeSlide: function(){
30379         if(Roo.isGecko){// firefox overflow auto bug workaround
30380             this.bodyEl.clip();
30381             if(this.tabs) this.tabs.bodyEl.clip();
30382             if(this.activePanel){
30383                 this.activePanel.getEl().clip();
30384                 
30385                 if(this.activePanel.beforeSlide){
30386                     this.activePanel.beforeSlide();
30387                 }
30388             }
30389         }
30390     },
30391     
30392     afterSlide : function(){
30393         if(Roo.isGecko){// firefox overflow auto bug workaround
30394             this.bodyEl.unclip();
30395             if(this.tabs) this.tabs.bodyEl.unclip();
30396             if(this.activePanel){
30397                 this.activePanel.getEl().unclip();
30398                 if(this.activePanel.afterSlide){
30399                     this.activePanel.afterSlide();
30400                 }
30401             }
30402         }
30403     },
30404
30405     initAutoHide : function(){
30406         if(this.autoHide !== false){
30407             if(!this.autoHideHd){
30408                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30409                 this.autoHideHd = {
30410                     "mouseout": function(e){
30411                         if(!e.within(this.el, true)){
30412                             st.delay(500);
30413                         }
30414                     },
30415                     "mouseover" : function(e){
30416                         st.cancel();
30417                     },
30418                     scope : this
30419                 };
30420             }
30421             this.el.on(this.autoHideHd);
30422         }
30423     },
30424
30425     clearAutoHide : function(){
30426         if(this.autoHide !== false){
30427             this.el.un("mouseout", this.autoHideHd.mouseout);
30428             this.el.un("mouseover", this.autoHideHd.mouseover);
30429         }
30430     },
30431
30432     clearMonitor : function(){
30433         Roo.get(document).un("click", this.slideInIf, this);
30434     },
30435
30436     // these names are backwards but not changed for compat
30437     slideOut : function(){
30438         if(this.isSlid || this.el.hasActiveFx()){
30439             return;
30440         }
30441         this.isSlid = true;
30442         if(this.collapseBtn){
30443             this.collapseBtn.hide();
30444         }
30445         this.closeBtnState = this.closeBtn.getStyle('display');
30446         this.closeBtn.hide();
30447         if(this.stickBtn){
30448             this.stickBtn.show();
30449         }
30450         this.el.show();
30451         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30452         this.beforeSlide();
30453         this.el.setStyle("z-index", 10001);
30454         this.el.slideIn(this.getSlideAnchor(), {
30455             callback: function(){
30456                 this.afterSlide();
30457                 this.initAutoHide();
30458                 Roo.get(document).on("click", this.slideInIf, this);
30459                 this.fireEvent("slideshow", this);
30460             },
30461             scope: this,
30462             block: true
30463         });
30464     },
30465
30466     afterSlideIn : function(){
30467         this.clearAutoHide();
30468         this.isSlid = false;
30469         this.clearMonitor();
30470         this.el.setStyle("z-index", "");
30471         if(this.collapseBtn){
30472             this.collapseBtn.show();
30473         }
30474         this.closeBtn.setStyle('display', this.closeBtnState);
30475         if(this.stickBtn){
30476             this.stickBtn.hide();
30477         }
30478         this.fireEvent("slidehide", this);
30479     },
30480
30481     slideIn : function(cb){
30482         if(!this.isSlid || this.el.hasActiveFx()){
30483             Roo.callback(cb);
30484             return;
30485         }
30486         this.isSlid = false;
30487         this.beforeSlide();
30488         this.el.slideOut(this.getSlideAnchor(), {
30489             callback: function(){
30490                 this.el.setLeftTop(-10000, -10000);
30491                 this.afterSlide();
30492                 this.afterSlideIn();
30493                 Roo.callback(cb);
30494             },
30495             scope: this,
30496             block: true
30497         });
30498     },
30499     
30500     slideInIf : function(e){
30501         if(!e.within(this.el)){
30502             this.slideIn();
30503         }
30504     },
30505
30506     animateCollapse : function(){
30507         this.beforeSlide();
30508         this.el.setStyle("z-index", 20000);
30509         var anchor = this.getSlideAnchor();
30510         this.el.slideOut(anchor, {
30511             callback : function(){
30512                 this.el.setStyle("z-index", "");
30513                 this.collapsedEl.slideIn(anchor, {duration:.3});
30514                 this.afterSlide();
30515                 this.el.setLocation(-10000,-10000);
30516                 this.el.hide();
30517                 this.fireEvent("collapsed", this);
30518             },
30519             scope: this,
30520             block: true
30521         });
30522     },
30523
30524     animateExpand : function(){
30525         this.beforeSlide();
30526         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30527         this.el.setStyle("z-index", 20000);
30528         this.collapsedEl.hide({
30529             duration:.1
30530         });
30531         this.el.slideIn(this.getSlideAnchor(), {
30532             callback : function(){
30533                 this.el.setStyle("z-index", "");
30534                 this.afterSlide();
30535                 if(this.split){
30536                     this.split.el.show();
30537                 }
30538                 this.fireEvent("invalidated", this);
30539                 this.fireEvent("expanded", this);
30540             },
30541             scope: this,
30542             block: true
30543         });
30544     },
30545
30546     anchors : {
30547         "west" : "left",
30548         "east" : "right",
30549         "north" : "top",
30550         "south" : "bottom"
30551     },
30552
30553     sanchors : {
30554         "west" : "l",
30555         "east" : "r",
30556         "north" : "t",
30557         "south" : "b"
30558     },
30559
30560     canchors : {
30561         "west" : "tl-tr",
30562         "east" : "tr-tl",
30563         "north" : "tl-bl",
30564         "south" : "bl-tl"
30565     },
30566
30567     getAnchor : function(){
30568         return this.anchors[this.position];
30569     },
30570
30571     getCollapseAnchor : function(){
30572         return this.canchors[this.position];
30573     },
30574
30575     getSlideAnchor : function(){
30576         return this.sanchors[this.position];
30577     },
30578
30579     getAlignAdj : function(){
30580         var cm = this.cmargins;
30581         switch(this.position){
30582             case "west":
30583                 return [0, 0];
30584             break;
30585             case "east":
30586                 return [0, 0];
30587             break;
30588             case "north":
30589                 return [0, 0];
30590             break;
30591             case "south":
30592                 return [0, 0];
30593             break;
30594         }
30595     },
30596
30597     getExpandAdj : function(){
30598         var c = this.collapsedEl, cm = this.cmargins;
30599         switch(this.position){
30600             case "west":
30601                 return [-(cm.right+c.getWidth()+cm.left), 0];
30602             break;
30603             case "east":
30604                 return [cm.right+c.getWidth()+cm.left, 0];
30605             break;
30606             case "north":
30607                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30608             break;
30609             case "south":
30610                 return [0, cm.top+cm.bottom+c.getHeight()];
30611             break;
30612         }
30613     }
30614 });/*
30615  * Based on:
30616  * Ext JS Library 1.1.1
30617  * Copyright(c) 2006-2007, Ext JS, LLC.
30618  *
30619  * Originally Released Under LGPL - original licence link has changed is not relivant.
30620  *
30621  * Fork - LGPL
30622  * <script type="text/javascript">
30623  */
30624 /*
30625  * These classes are private internal classes
30626  */
30627 Roo.CenterLayoutRegion = function(mgr, config){
30628     Roo.LayoutRegion.call(this, mgr, config, "center");
30629     this.visible = true;
30630     this.minWidth = config.minWidth || 20;
30631     this.minHeight = config.minHeight || 20;
30632 };
30633
30634 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30635     hide : function(){
30636         // center panel can't be hidden
30637     },
30638     
30639     show : function(){
30640         // center panel can't be hidden
30641     },
30642     
30643     getMinWidth: function(){
30644         return this.minWidth;
30645     },
30646     
30647     getMinHeight: function(){
30648         return this.minHeight;
30649     }
30650 });
30651
30652
30653 Roo.NorthLayoutRegion = function(mgr, config){
30654     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30655     if(this.split){
30656         this.split.placement = Roo.SplitBar.TOP;
30657         this.split.orientation = Roo.SplitBar.VERTICAL;
30658         this.split.el.addClass("x-layout-split-v");
30659     }
30660     var size = config.initialSize || config.height;
30661     if(typeof size != "undefined"){
30662         this.el.setHeight(size);
30663     }
30664 };
30665 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30666     orientation: Roo.SplitBar.VERTICAL,
30667     getBox : function(){
30668         if(this.collapsed){
30669             return this.collapsedEl.getBox();
30670         }
30671         var box = this.el.getBox();
30672         if(this.split){
30673             box.height += this.split.el.getHeight();
30674         }
30675         return box;
30676     },
30677     
30678     updateBox : function(box){
30679         if(this.split && !this.collapsed){
30680             box.height -= this.split.el.getHeight();
30681             this.split.el.setLeft(box.x);
30682             this.split.el.setTop(box.y+box.height);
30683             this.split.el.setWidth(box.width);
30684         }
30685         if(this.collapsed){
30686             this.updateBody(box.width, null);
30687         }
30688         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30689     }
30690 });
30691
30692 Roo.SouthLayoutRegion = function(mgr, config){
30693     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30694     if(this.split){
30695         this.split.placement = Roo.SplitBar.BOTTOM;
30696         this.split.orientation = Roo.SplitBar.VERTICAL;
30697         this.split.el.addClass("x-layout-split-v");
30698     }
30699     var size = config.initialSize || config.height;
30700     if(typeof size != "undefined"){
30701         this.el.setHeight(size);
30702     }
30703 };
30704 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30705     orientation: Roo.SplitBar.VERTICAL,
30706     getBox : function(){
30707         if(this.collapsed){
30708             return this.collapsedEl.getBox();
30709         }
30710         var box = this.el.getBox();
30711         if(this.split){
30712             var sh = this.split.el.getHeight();
30713             box.height += sh;
30714             box.y -= sh;
30715         }
30716         return box;
30717     },
30718     
30719     updateBox : function(box){
30720         if(this.split && !this.collapsed){
30721             var sh = this.split.el.getHeight();
30722             box.height -= sh;
30723             box.y += sh;
30724             this.split.el.setLeft(box.x);
30725             this.split.el.setTop(box.y-sh);
30726             this.split.el.setWidth(box.width);
30727         }
30728         if(this.collapsed){
30729             this.updateBody(box.width, null);
30730         }
30731         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30732     }
30733 });
30734
30735 Roo.EastLayoutRegion = function(mgr, config){
30736     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30737     if(this.split){
30738         this.split.placement = Roo.SplitBar.RIGHT;
30739         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30740         this.split.el.addClass("x-layout-split-h");
30741     }
30742     var size = config.initialSize || config.width;
30743     if(typeof size != "undefined"){
30744         this.el.setWidth(size);
30745     }
30746 };
30747 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30748     orientation: Roo.SplitBar.HORIZONTAL,
30749     getBox : function(){
30750         if(this.collapsed){
30751             return this.collapsedEl.getBox();
30752         }
30753         var box = this.el.getBox();
30754         if(this.split){
30755             var sw = this.split.el.getWidth();
30756             box.width += sw;
30757             box.x -= sw;
30758         }
30759         return box;
30760     },
30761
30762     updateBox : function(box){
30763         if(this.split && !this.collapsed){
30764             var sw = this.split.el.getWidth();
30765             box.width -= sw;
30766             this.split.el.setLeft(box.x);
30767             this.split.el.setTop(box.y);
30768             this.split.el.setHeight(box.height);
30769             box.x += sw;
30770         }
30771         if(this.collapsed){
30772             this.updateBody(null, box.height);
30773         }
30774         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30775     }
30776 });
30777
30778 Roo.WestLayoutRegion = function(mgr, config){
30779     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30780     if(this.split){
30781         this.split.placement = Roo.SplitBar.LEFT;
30782         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30783         this.split.el.addClass("x-layout-split-h");
30784     }
30785     var size = config.initialSize || config.width;
30786     if(typeof size != "undefined"){
30787         this.el.setWidth(size);
30788     }
30789 };
30790 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30791     orientation: Roo.SplitBar.HORIZONTAL,
30792     getBox : function(){
30793         if(this.collapsed){
30794             return this.collapsedEl.getBox();
30795         }
30796         var box = this.el.getBox();
30797         if(this.split){
30798             box.width += this.split.el.getWidth();
30799         }
30800         return box;
30801     },
30802     
30803     updateBox : function(box){
30804         if(this.split && !this.collapsed){
30805             var sw = this.split.el.getWidth();
30806             box.width -= sw;
30807             this.split.el.setLeft(box.x+box.width);
30808             this.split.el.setTop(box.y);
30809             this.split.el.setHeight(box.height);
30810         }
30811         if(this.collapsed){
30812             this.updateBody(null, box.height);
30813         }
30814         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30815     }
30816 });
30817 /*
30818  * Based on:
30819  * Ext JS Library 1.1.1
30820  * Copyright(c) 2006-2007, Ext JS, LLC.
30821  *
30822  * Originally Released Under LGPL - original licence link has changed is not relivant.
30823  *
30824  * Fork - LGPL
30825  * <script type="text/javascript">
30826  */
30827  
30828  
30829 /*
30830  * Private internal class for reading and applying state
30831  */
30832 Roo.LayoutStateManager = function(layout){
30833      // default empty state
30834      this.state = {
30835         north: {},
30836         south: {},
30837         east: {},
30838         west: {}       
30839     };
30840 };
30841
30842 Roo.LayoutStateManager.prototype = {
30843     init : function(layout, provider){
30844         this.provider = provider;
30845         var state = provider.get(layout.id+"-layout-state");
30846         if(state){
30847             var wasUpdating = layout.isUpdating();
30848             if(!wasUpdating){
30849                 layout.beginUpdate();
30850             }
30851             for(var key in state){
30852                 if(typeof state[key] != "function"){
30853                     var rstate = state[key];
30854                     var r = layout.getRegion(key);
30855                     if(r && rstate){
30856                         if(rstate.size){
30857                             r.resizeTo(rstate.size);
30858                         }
30859                         if(rstate.collapsed == true){
30860                             r.collapse(true);
30861                         }else{
30862                             r.expand(null, true);
30863                         }
30864                     }
30865                 }
30866             }
30867             if(!wasUpdating){
30868                 layout.endUpdate();
30869             }
30870             this.state = state; 
30871         }
30872         this.layout = layout;
30873         layout.on("regionresized", this.onRegionResized, this);
30874         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30875         layout.on("regionexpanded", this.onRegionExpanded, this);
30876     },
30877     
30878     storeState : function(){
30879         this.provider.set(this.layout.id+"-layout-state", this.state);
30880     },
30881     
30882     onRegionResized : function(region, newSize){
30883         this.state[region.getPosition()].size = newSize;
30884         this.storeState();
30885     },
30886     
30887     onRegionCollapsed : function(region){
30888         this.state[region.getPosition()].collapsed = true;
30889         this.storeState();
30890     },
30891     
30892     onRegionExpanded : function(region){
30893         this.state[region.getPosition()].collapsed = false;
30894         this.storeState();
30895     }
30896 };/*
30897  * Based on:
30898  * Ext JS Library 1.1.1
30899  * Copyright(c) 2006-2007, Ext JS, LLC.
30900  *
30901  * Originally Released Under LGPL - original licence link has changed is not relivant.
30902  *
30903  * Fork - LGPL
30904  * <script type="text/javascript">
30905  */
30906 /**
30907  * @class Roo.ContentPanel
30908  * @extends Roo.util.Observable
30909  * A basic ContentPanel element.
30910  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30911  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30912  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
30913  * @cfg {Boolean} closable True if the panel can be closed/removed
30914  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30915  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30916  * @cfg {Toolbar} toolbar A toolbar for this panel
30917  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30918  * @cfg {String} title The title for this panel
30919  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30920  * @cfg {String} url Calls {@link #setUrl} with this value
30921  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30922  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30923  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30924  * @constructor
30925  * Create a new ContentPanel.
30926  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30927  * @param {String/Object} config A string to set only the title or a config object
30928  * @param {String} content (optional) Set the HTML content for this panel
30929  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30930  */
30931 Roo.ContentPanel = function(el, config, content){
30932     
30933      
30934     /*
30935     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30936         config = el;
30937         el = Roo.id();
30938     }
30939     if (config && config.parentLayout) { 
30940         el = config.parentLayout.el.createChild(); 
30941     }
30942     */
30943     if(el.autoCreate){ // xtype is available if this is called from factory
30944         config = el;
30945         el = Roo.id();
30946     }
30947     this.el = Roo.get(el);
30948     if(!this.el && config && config.autoCreate){
30949         if(typeof config.autoCreate == "object"){
30950             if(!config.autoCreate.id){
30951                 config.autoCreate.id = config.id||el;
30952             }
30953             this.el = Roo.DomHelper.append(document.body,
30954                         config.autoCreate, true);
30955         }else{
30956             this.el = Roo.DomHelper.append(document.body,
30957                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30958         }
30959     }
30960     this.closable = false;
30961     this.loaded = false;
30962     this.active = false;
30963     if(typeof config == "string"){
30964         this.title = config;
30965     }else{
30966         Roo.apply(this, config);
30967     }
30968     
30969     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30970         this.wrapEl = this.el.wrap();    
30971         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30972         
30973     }
30974     
30975     
30976     
30977     if(this.resizeEl){
30978         this.resizeEl = Roo.get(this.resizeEl, true);
30979     }else{
30980         this.resizeEl = this.el;
30981     }
30982     this.addEvents({
30983         /**
30984          * @event activate
30985          * Fires when this panel is activated. 
30986          * @param {Roo.ContentPanel} this
30987          */
30988         "activate" : true,
30989         /**
30990          * @event deactivate
30991          * Fires when this panel is activated. 
30992          * @param {Roo.ContentPanel} this
30993          */
30994         "deactivate" : true,
30995
30996         /**
30997          * @event resize
30998          * Fires when this panel is resized if fitToFrame is true.
30999          * @param {Roo.ContentPanel} this
31000          * @param {Number} width The width after any component adjustments
31001          * @param {Number} height The height after any component adjustments
31002          */
31003         "resize" : true
31004     });
31005     if(this.autoScroll){
31006         this.resizeEl.setStyle("overflow", "auto");
31007     } else {
31008         // fix randome scrolling
31009         this.el.on('scroll', function() {
31010             this.scrollTo('top',0); 
31011         });
31012     }
31013     content = content || this.content;
31014     if(content){
31015         this.setContent(content);
31016     }
31017     if(config && config.url){
31018         this.setUrl(this.url, this.params, this.loadOnce);
31019     }
31020     
31021     
31022     
31023     Roo.ContentPanel.superclass.constructor.call(this);
31024 };
31025
31026 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31027     tabTip:'',
31028     setRegion : function(region){
31029         this.region = region;
31030         if(region){
31031            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31032         }else{
31033            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31034         } 
31035     },
31036     
31037     /**
31038      * Returns the toolbar for this Panel if one was configured. 
31039      * @return {Roo.Toolbar} 
31040      */
31041     getToolbar : function(){
31042         return this.toolbar;
31043     },
31044     
31045     setActiveState : function(active){
31046         this.active = active;
31047         if(!active){
31048             this.fireEvent("deactivate", this);
31049         }else{
31050             this.fireEvent("activate", this);
31051         }
31052     },
31053     /**
31054      * Updates this panel's element
31055      * @param {String} content The new content
31056      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31057     */
31058     setContent : function(content, loadScripts){
31059         this.el.update(content, loadScripts);
31060     },
31061
31062     ignoreResize : function(w, h){
31063         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31064             return true;
31065         }else{
31066             this.lastSize = {width: w, height: h};
31067             return false;
31068         }
31069     },
31070     /**
31071      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31072      * @return {Roo.UpdateManager} The UpdateManager
31073      */
31074     getUpdateManager : function(){
31075         return this.el.getUpdateManager();
31076     },
31077      /**
31078      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31079      * @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:
31080 <pre><code>
31081 panel.load({
31082     url: "your-url.php",
31083     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31084     callback: yourFunction,
31085     scope: yourObject, //(optional scope)
31086     discardUrl: false,
31087     nocache: false,
31088     text: "Loading...",
31089     timeout: 30,
31090     scripts: false
31091 });
31092 </code></pre>
31093      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31094      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
31095      * @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}
31096      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31097      * @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.
31098      * @return {Roo.ContentPanel} this
31099      */
31100     load : function(){
31101         var um = this.el.getUpdateManager();
31102         um.update.apply(um, arguments);
31103         return this;
31104     },
31105
31106
31107     /**
31108      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
31109      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31110      * @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)
31111      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
31112      * @return {Roo.UpdateManager} The UpdateManager
31113      */
31114     setUrl : function(url, params, loadOnce){
31115         if(this.refreshDelegate){
31116             this.removeListener("activate", this.refreshDelegate);
31117         }
31118         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31119         this.on("activate", this.refreshDelegate);
31120         return this.el.getUpdateManager();
31121     },
31122     
31123     _handleRefresh : function(url, params, loadOnce){
31124         if(!loadOnce || !this.loaded){
31125             var updater = this.el.getUpdateManager();
31126             updater.update(url, params, this._setLoaded.createDelegate(this));
31127         }
31128     },
31129     
31130     _setLoaded : function(){
31131         this.loaded = true;
31132     }, 
31133     
31134     /**
31135      * Returns this panel's id
31136      * @return {String} 
31137      */
31138     getId : function(){
31139         return this.el.id;
31140     },
31141     
31142     /** 
31143      * Returns this panel's element - used by regiosn to add.
31144      * @return {Roo.Element} 
31145      */
31146     getEl : function(){
31147         return this.wrapEl || this.el;
31148     },
31149     
31150     adjustForComponents : function(width, height){
31151         if(this.resizeEl != this.el){
31152             width -= this.el.getFrameWidth('lr');
31153             height -= this.el.getFrameWidth('tb');
31154         }
31155         if(this.toolbar){
31156             var te = this.toolbar.getEl();
31157             height -= te.getHeight();
31158             te.setWidth(width);
31159         }
31160         if(this.adjustments){
31161             width += this.adjustments[0];
31162             height += this.adjustments[1];
31163         }
31164         return {"width": width, "height": height};
31165     },
31166     
31167     setSize : function(width, height){
31168         if(this.fitToFrame && !this.ignoreResize(width, height)){
31169             if(this.fitContainer && this.resizeEl != this.el){
31170                 this.el.setSize(width, height);
31171             }
31172             var size = this.adjustForComponents(width, height);
31173             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31174             this.fireEvent('resize', this, size.width, size.height);
31175         }
31176     },
31177     
31178     /**
31179      * Returns this panel's title
31180      * @return {String} 
31181      */
31182     getTitle : function(){
31183         return this.title;
31184     },
31185     
31186     /**
31187      * Set this panel's title
31188      * @param {String} title
31189      */
31190     setTitle : function(title){
31191         this.title = title;
31192         if(this.region){
31193             this.region.updatePanelTitle(this, title);
31194         }
31195     },
31196     
31197     /**
31198      * Returns true is this panel was configured to be closable
31199      * @return {Boolean} 
31200      */
31201     isClosable : function(){
31202         return this.closable;
31203     },
31204     
31205     beforeSlide : function(){
31206         this.el.clip();
31207         this.resizeEl.clip();
31208     },
31209     
31210     afterSlide : function(){
31211         this.el.unclip();
31212         this.resizeEl.unclip();
31213     },
31214     
31215     /**
31216      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31217      *   Will fail silently if the {@link #setUrl} method has not been called.
31218      *   This does not activate the panel, just updates its content.
31219      */
31220     refresh : function(){
31221         if(this.refreshDelegate){
31222            this.loaded = false;
31223            this.refreshDelegate();
31224         }
31225     },
31226     
31227     /**
31228      * Destroys this panel
31229      */
31230     destroy : function(){
31231         this.el.removeAllListeners();
31232         var tempEl = document.createElement("span");
31233         tempEl.appendChild(this.el.dom);
31234         tempEl.innerHTML = "";
31235         this.el.remove();
31236         this.el = null;
31237     },
31238     
31239       /**
31240      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31241      * <pre><code>
31242
31243 layout.addxtype({
31244        xtype : 'Form',
31245        items: [ .... ]
31246    }
31247 );
31248
31249 </code></pre>
31250      * @param {Object} cfg Xtype definition of item to add.
31251      */
31252     
31253     addxtype : function(cfg) {
31254         // add form..
31255         if (cfg.xtype.match(/^Form$/)) {
31256             var el = this.el.createChild();
31257
31258             this.form = new  Roo.form.Form(cfg);
31259             
31260             
31261             if ( this.form.allItems.length) this.form.render(el.dom);
31262             return this.form;
31263         }
31264         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31265             // views..
31266             cfg.el = this.el.appendChild(document.createElement("div"));
31267             // factory?
31268             var ret = new Roo[cfg.xtype](cfg);
31269             ret.render(false, ''); // render blank..
31270             return ret;
31271             
31272         }
31273         return false;
31274         
31275     }
31276 });
31277
31278 /**
31279  * @class Roo.GridPanel
31280  * @extends Roo.ContentPanel
31281  * @constructor
31282  * Create a new GridPanel.
31283  * @param {Roo.grid.Grid} grid The grid for this panel
31284  * @param {String/Object} config A string to set only the panel's title, or a config object
31285  */
31286 Roo.GridPanel = function(grid, config){
31287     
31288   
31289     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31290         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31291         
31292     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31293     
31294     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31295     
31296     if(this.toolbar){
31297         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31298     }
31299     // xtype created footer. - not sure if will work as we normally have to render first..
31300     if (this.footer && !this.footer.el && this.footer.xtype) {
31301         
31302         this.footer.container = this.grid.getView().getFooterPanel(true);
31303         this.footer.dataSource = this.grid.dataSource;
31304         this.footer = Roo.factory(this.footer, Roo);
31305         
31306     }
31307     
31308     grid.monitorWindowResize = false; // turn off autosizing
31309     grid.autoHeight = false;
31310     grid.autoWidth = false;
31311     this.grid = grid;
31312     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31313 };
31314
31315 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31316     getId : function(){
31317         return this.grid.id;
31318     },
31319     
31320     /**
31321      * Returns the grid for this panel
31322      * @return {Roo.grid.Grid} 
31323      */
31324     getGrid : function(){
31325         return this.grid;    
31326     },
31327     
31328     setSize : function(width, height){
31329         if(!this.ignoreResize(width, height)){
31330             var grid = this.grid;
31331             var size = this.adjustForComponents(width, height);
31332             grid.getGridEl().setSize(size.width, size.height);
31333             grid.autoSize();
31334         }
31335     },
31336     
31337     beforeSlide : function(){
31338         this.grid.getView().scroller.clip();
31339     },
31340     
31341     afterSlide : function(){
31342         this.grid.getView().scroller.unclip();
31343     },
31344     
31345     destroy : function(){
31346         this.grid.destroy();
31347         delete this.grid;
31348         Roo.GridPanel.superclass.destroy.call(this); 
31349     }
31350 });
31351
31352
31353 /**
31354  * @class Roo.NestedLayoutPanel
31355  * @extends Roo.ContentPanel
31356  * @constructor
31357  * Create a new NestedLayoutPanel.
31358  * 
31359  * 
31360  * @param {Roo.BorderLayout} layout The layout for this panel
31361  * @param {String/Object} config A string to set only the title or a config object
31362  */
31363 Roo.NestedLayoutPanel = function(layout, config)
31364 {
31365     // construct with only one argument..
31366     /* FIXME - implement nicer consturctors
31367     if (layout.layout) {
31368         config = layout;
31369         layout = config.layout;
31370         delete config.layout;
31371     }
31372     if (layout.xtype && !layout.getEl) {
31373         // then layout needs constructing..
31374         layout = Roo.factory(layout, Roo);
31375     }
31376     */
31377     
31378     
31379     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31380     
31381     layout.monitorWindowResize = false; // turn off autosizing
31382     this.layout = layout;
31383     this.layout.getEl().addClass("x-layout-nested-layout");
31384     
31385     
31386     
31387     
31388 };
31389
31390 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31391
31392     setSize : function(width, height){
31393         if(!this.ignoreResize(width, height)){
31394             var size = this.adjustForComponents(width, height);
31395             var el = this.layout.getEl();
31396             el.setSize(size.width, size.height);
31397             var touch = el.dom.offsetWidth;
31398             this.layout.layout();
31399             // ie requires a double layout on the first pass
31400             if(Roo.isIE && !this.initialized){
31401                 this.initialized = true;
31402                 this.layout.layout();
31403             }
31404         }
31405     },
31406     
31407     // activate all subpanels if not currently active..
31408     
31409     setActiveState : function(active){
31410         this.active = active;
31411         if(!active){
31412             this.fireEvent("deactivate", this);
31413             return;
31414         }
31415         
31416         this.fireEvent("activate", this);
31417         // not sure if this should happen before or after..
31418         if (!this.layout) {
31419             return; // should not happen..
31420         }
31421         var reg = false;
31422         for (var r in this.layout.regions) {
31423             reg = this.layout.getRegion(r);
31424             if (reg.getActivePanel()) {
31425                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31426                 reg.setActivePanel(reg.getActivePanel());
31427                 continue;
31428             }
31429             if (!reg.panels.length) {
31430                 continue;
31431             }
31432             reg.showPanel(reg.getPanel(0));
31433         }
31434         
31435         
31436         
31437         
31438     },
31439     
31440     /**
31441      * Returns the nested BorderLayout for this panel
31442      * @return {Roo.BorderLayout} 
31443      */
31444     getLayout : function(){
31445         return this.layout;
31446     },
31447     
31448      /**
31449      * Adds a xtype elements to the layout of the nested panel
31450      * <pre><code>
31451
31452 panel.addxtype({
31453        xtype : 'ContentPanel',
31454        region: 'west',
31455        items: [ .... ]
31456    }
31457 );
31458
31459 panel.addxtype({
31460         xtype : 'NestedLayoutPanel',
31461         region: 'west',
31462         layout: {
31463            center: { },
31464            west: { }   
31465         },
31466         items : [ ... list of content panels or nested layout panels.. ]
31467    }
31468 );
31469 </code></pre>
31470      * @param {Object} cfg Xtype definition of item to add.
31471      */
31472     addxtype : function(cfg) {
31473         return this.layout.addxtype(cfg);
31474     
31475     }
31476 });
31477
31478 Roo.ScrollPanel = function(el, config, content){
31479     config = config || {};
31480     config.fitToFrame = true;
31481     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31482     
31483     this.el.dom.style.overflow = "hidden";
31484     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31485     this.el.removeClass("x-layout-inactive-content");
31486     this.el.on("mousewheel", this.onWheel, this);
31487
31488     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31489     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31490     up.unselectable(); down.unselectable();
31491     up.on("click", this.scrollUp, this);
31492     down.on("click", this.scrollDown, this);
31493     up.addClassOnOver("x-scroller-btn-over");
31494     down.addClassOnOver("x-scroller-btn-over");
31495     up.addClassOnClick("x-scroller-btn-click");
31496     down.addClassOnClick("x-scroller-btn-click");
31497     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31498
31499     this.resizeEl = this.el;
31500     this.el = wrap; this.up = up; this.down = down;
31501 };
31502
31503 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31504     increment : 100,
31505     wheelIncrement : 5,
31506     scrollUp : function(){
31507         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31508     },
31509
31510     scrollDown : function(){
31511         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31512     },
31513
31514     afterScroll : function(){
31515         var el = this.resizeEl;
31516         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31517         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31518         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31519     },
31520
31521     setSize : function(){
31522         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31523         this.afterScroll();
31524     },
31525
31526     onWheel : function(e){
31527         var d = e.getWheelDelta();
31528         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31529         this.afterScroll();
31530         e.stopEvent();
31531     },
31532
31533     setContent : function(content, loadScripts){
31534         this.resizeEl.update(content, loadScripts);
31535     }
31536
31537 });
31538
31539
31540
31541
31542
31543
31544
31545
31546
31547 /**
31548  * @class Roo.TreePanel
31549  * @extends Roo.ContentPanel
31550  * @constructor
31551  * Create a new TreePanel. - defaults to fit/scoll contents.
31552  * @param {String/Object} config A string to set only the panel's title, or a config object
31553  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31554  */
31555 Roo.TreePanel = function(config){
31556     var el = config.el;
31557     var tree = config.tree;
31558     delete config.tree; 
31559     delete config.el; // hopefull!
31560     
31561     // wrapper for IE7 strict & safari scroll issue
31562     
31563     var treeEl = el.createChild();
31564     config.resizeEl = treeEl;
31565     
31566     
31567     
31568     Roo.TreePanel.superclass.constructor.call(this, el, config);
31569  
31570  
31571     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31572     //console.log(tree);
31573     this.on('activate', function()
31574     {
31575         if (this.tree.rendered) {
31576             return;
31577         }
31578         //console.log('render tree');
31579         this.tree.render();
31580     });
31581     
31582     this.on('resize',  function (cp, w, h) {
31583             this.tree.innerCt.setWidth(w);
31584             this.tree.innerCt.setHeight(h);
31585             this.tree.innerCt.setStyle('overflow-y', 'auto');
31586     });
31587
31588         
31589     
31590 };
31591
31592 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31593     fitToFrame : true,
31594     autoScroll : true
31595 });
31596
31597
31598
31599
31600
31601
31602
31603
31604
31605
31606
31607 /*
31608  * Based on:
31609  * Ext JS Library 1.1.1
31610  * Copyright(c) 2006-2007, Ext JS, LLC.
31611  *
31612  * Originally Released Under LGPL - original licence link has changed is not relivant.
31613  *
31614  * Fork - LGPL
31615  * <script type="text/javascript">
31616  */
31617  
31618
31619 /**
31620  * @class Roo.ReaderLayout
31621  * @extends Roo.BorderLayout
31622  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31623  * center region containing two nested regions (a top one for a list view and one for item preview below),
31624  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31625  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31626  * expedites the setup of the overall layout and regions for this common application style.
31627  * Example:
31628  <pre><code>
31629 var reader = new Roo.ReaderLayout();
31630 var CP = Roo.ContentPanel;  // shortcut for adding
31631
31632 reader.beginUpdate();
31633 reader.add("north", new CP("north", "North"));
31634 reader.add("west", new CP("west", {title: "West"}));
31635 reader.add("east", new CP("east", {title: "East"}));
31636
31637 reader.regions.listView.add(new CP("listView", "List"));
31638 reader.regions.preview.add(new CP("preview", "Preview"));
31639 reader.endUpdate();
31640 </code></pre>
31641 * @constructor
31642 * Create a new ReaderLayout
31643 * @param {Object} config Configuration options
31644 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31645 * document.body if omitted)
31646 */
31647 Roo.ReaderLayout = function(config, renderTo){
31648     var c = config || {size:{}};
31649     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31650         north: c.north !== false ? Roo.apply({
31651             split:false,
31652             initialSize: 32,
31653             titlebar: false
31654         }, c.north) : false,
31655         west: c.west !== false ? Roo.apply({
31656             split:true,
31657             initialSize: 200,
31658             minSize: 175,
31659             maxSize: 400,
31660             titlebar: true,
31661             collapsible: true,
31662             animate: true,
31663             margins:{left:5,right:0,bottom:5,top:5},
31664             cmargins:{left:5,right:5,bottom:5,top:5}
31665         }, c.west) : false,
31666         east: c.east !== false ? Roo.apply({
31667             split:true,
31668             initialSize: 200,
31669             minSize: 175,
31670             maxSize: 400,
31671             titlebar: true,
31672             collapsible: true,
31673             animate: true,
31674             margins:{left:0,right:5,bottom:5,top:5},
31675             cmargins:{left:5,right:5,bottom:5,top:5}
31676         }, c.east) : false,
31677         center: Roo.apply({
31678             tabPosition: 'top',
31679             autoScroll:false,
31680             closeOnTab: true,
31681             titlebar:false,
31682             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31683         }, c.center)
31684     });
31685
31686     this.el.addClass('x-reader');
31687
31688     this.beginUpdate();
31689
31690     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31691         south: c.preview !== false ? Roo.apply({
31692             split:true,
31693             initialSize: 200,
31694             minSize: 100,
31695             autoScroll:true,
31696             collapsible:true,
31697             titlebar: true,
31698             cmargins:{top:5,left:0, right:0, bottom:0}
31699         }, c.preview) : false,
31700         center: Roo.apply({
31701             autoScroll:false,
31702             titlebar:false,
31703             minHeight:200
31704         }, c.listView)
31705     });
31706     this.add('center', new Roo.NestedLayoutPanel(inner,
31707             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31708
31709     this.endUpdate();
31710
31711     this.regions.preview = inner.getRegion('south');
31712     this.regions.listView = inner.getRegion('center');
31713 };
31714
31715 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31716  * Based on:
31717  * Ext JS Library 1.1.1
31718  * Copyright(c) 2006-2007, Ext JS, LLC.
31719  *
31720  * Originally Released Under LGPL - original licence link has changed is not relivant.
31721  *
31722  * Fork - LGPL
31723  * <script type="text/javascript">
31724  */
31725  
31726 /**
31727  * @class Roo.grid.Grid
31728  * @extends Roo.util.Observable
31729  * This class represents the primary interface of a component based grid control.
31730  * <br><br>Usage:<pre><code>
31731  var grid = new Roo.grid.Grid("my-container-id", {
31732      ds: myDataStore,
31733      cm: myColModel,
31734      selModel: mySelectionModel,
31735      autoSizeColumns: true,
31736      monitorWindowResize: false,
31737      trackMouseOver: true
31738  });
31739  // set any options
31740  grid.render();
31741  * </code></pre>
31742  * <b>Common Problems:</b><br/>
31743  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31744  * element will correct this<br/>
31745  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31746  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31747  * are unpredictable.<br/>
31748  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31749  * grid to calculate dimensions/offsets.<br/>
31750   * @constructor
31751  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31752  * The container MUST have some type of size defined for the grid to fill. The container will be
31753  * automatically set to position relative if it isn't already.
31754  * @param {Object} config A config object that sets properties on this grid.
31755  */
31756 Roo.grid.Grid = function(container, config){
31757         // initialize the container
31758         this.container = Roo.get(container);
31759         this.container.update("");
31760         this.container.setStyle("overflow", "hidden");
31761     this.container.addClass('x-grid-container');
31762
31763     this.id = this.container.id;
31764
31765     Roo.apply(this, config);
31766     // check and correct shorthanded configs
31767     if(this.ds){
31768         this.dataSource = this.ds;
31769         delete this.ds;
31770     }
31771     if(this.cm){
31772         this.colModel = this.cm;
31773         delete this.cm;
31774     }
31775     if(this.sm){
31776         this.selModel = this.sm;
31777         delete this.sm;
31778     }
31779
31780     if (this.selModel) {
31781         this.selModel = Roo.factory(this.selModel, Roo.grid);
31782         this.sm = this.selModel;
31783         this.sm.xmodule = this.xmodule || false;
31784     }
31785     if (typeof(this.colModel.config) == 'undefined') {
31786         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31787         this.cm = this.colModel;
31788         this.cm.xmodule = this.xmodule || false;
31789     }
31790     if (this.dataSource) {
31791         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31792         this.ds = this.dataSource;
31793         this.ds.xmodule = this.xmodule || false;
31794         
31795     }
31796     
31797     
31798     
31799     if(this.width){
31800         this.container.setWidth(this.width);
31801     }
31802
31803     if(this.height){
31804         this.container.setHeight(this.height);
31805     }
31806     /** @private */
31807         this.addEvents({
31808             // raw events
31809             /**
31810              * @event click
31811              * The raw click event for the entire grid.
31812              * @param {Roo.EventObject} e
31813              */
31814             "click" : true,
31815             /**
31816              * @event dblclick
31817              * The raw dblclick event for the entire grid.
31818              * @param {Roo.EventObject} e
31819              */
31820             "dblclick" : true,
31821             /**
31822              * @event contextmenu
31823              * The raw contextmenu event for the entire grid.
31824              * @param {Roo.EventObject} e
31825              */
31826             "contextmenu" : true,
31827             /**
31828              * @event mousedown
31829              * The raw mousedown event for the entire grid.
31830              * @param {Roo.EventObject} e
31831              */
31832             "mousedown" : true,
31833             /**
31834              * @event mouseup
31835              * The raw mouseup event for the entire grid.
31836              * @param {Roo.EventObject} e
31837              */
31838             "mouseup" : true,
31839             /**
31840              * @event mouseover
31841              * The raw mouseover event for the entire grid.
31842              * @param {Roo.EventObject} e
31843              */
31844             "mouseover" : true,
31845             /**
31846              * @event mouseout
31847              * The raw mouseout event for the entire grid.
31848              * @param {Roo.EventObject} e
31849              */
31850             "mouseout" : true,
31851             /**
31852              * @event keypress
31853              * The raw keypress event for the entire grid.
31854              * @param {Roo.EventObject} e
31855              */
31856             "keypress" : true,
31857             /**
31858              * @event keydown
31859              * The raw keydown event for the entire grid.
31860              * @param {Roo.EventObject} e
31861              */
31862             "keydown" : true,
31863
31864             // custom events
31865
31866             /**
31867              * @event cellclick
31868              * Fires when a cell is clicked
31869              * @param {Grid} this
31870              * @param {Number} rowIndex
31871              * @param {Number} columnIndex
31872              * @param {Roo.EventObject} e
31873              */
31874             "cellclick" : true,
31875             /**
31876              * @event celldblclick
31877              * Fires when a cell is double clicked
31878              * @param {Grid} this
31879              * @param {Number} rowIndex
31880              * @param {Number} columnIndex
31881              * @param {Roo.EventObject} e
31882              */
31883             "celldblclick" : true,
31884             /**
31885              * @event rowclick
31886              * Fires when a row is clicked
31887              * @param {Grid} this
31888              * @param {Number} rowIndex
31889              * @param {Roo.EventObject} e
31890              */
31891             "rowclick" : true,
31892             /**
31893              * @event rowdblclick
31894              * Fires when a row is double clicked
31895              * @param {Grid} this
31896              * @param {Number} rowIndex
31897              * @param {Roo.EventObject} e
31898              */
31899             "rowdblclick" : true,
31900             /**
31901              * @event headerclick
31902              * Fires when a header is clicked
31903              * @param {Grid} this
31904              * @param {Number} columnIndex
31905              * @param {Roo.EventObject} e
31906              */
31907             "headerclick" : true,
31908             /**
31909              * @event headerdblclick
31910              * Fires when a header cell is double clicked
31911              * @param {Grid} this
31912              * @param {Number} columnIndex
31913              * @param {Roo.EventObject} e
31914              */
31915             "headerdblclick" : true,
31916             /**
31917              * @event rowcontextmenu
31918              * Fires when a row is right clicked
31919              * @param {Grid} this
31920              * @param {Number} rowIndex
31921              * @param {Roo.EventObject} e
31922              */
31923             "rowcontextmenu" : true,
31924             /**
31925          * @event cellcontextmenu
31926          * Fires when a cell is right clicked
31927          * @param {Grid} this
31928          * @param {Number} rowIndex
31929          * @param {Number} cellIndex
31930          * @param {Roo.EventObject} e
31931          */
31932          "cellcontextmenu" : true,
31933             /**
31934              * @event headercontextmenu
31935              * Fires when a header is right clicked
31936              * @param {Grid} this
31937              * @param {Number} columnIndex
31938              * @param {Roo.EventObject} e
31939              */
31940             "headercontextmenu" : true,
31941             /**
31942              * @event bodyscroll
31943              * Fires when the body element is scrolled
31944              * @param {Number} scrollLeft
31945              * @param {Number} scrollTop
31946              */
31947             "bodyscroll" : true,
31948             /**
31949              * @event columnresize
31950              * Fires when the user resizes a column
31951              * @param {Number} columnIndex
31952              * @param {Number} newSize
31953              */
31954             "columnresize" : true,
31955             /**
31956              * @event columnmove
31957              * Fires when the user moves a column
31958              * @param {Number} oldIndex
31959              * @param {Number} newIndex
31960              */
31961             "columnmove" : true,
31962             /**
31963              * @event startdrag
31964              * Fires when row(s) start being dragged
31965              * @param {Grid} this
31966              * @param {Roo.GridDD} dd The drag drop object
31967              * @param {event} e The raw browser event
31968              */
31969             "startdrag" : true,
31970             /**
31971              * @event enddrag
31972              * Fires when a drag operation is complete
31973              * @param {Grid} this
31974              * @param {Roo.GridDD} dd The drag drop object
31975              * @param {event} e The raw browser event
31976              */
31977             "enddrag" : true,
31978             /**
31979              * @event dragdrop
31980              * Fires when dragged row(s) are dropped on a valid DD target
31981              * @param {Grid} this
31982              * @param {Roo.GridDD} dd The drag drop object
31983              * @param {String} targetId The target drag drop object
31984              * @param {event} e The raw browser event
31985              */
31986             "dragdrop" : true,
31987             /**
31988              * @event dragover
31989              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31990              * @param {Grid} this
31991              * @param {Roo.GridDD} dd The drag drop object
31992              * @param {String} targetId The target drag drop object
31993              * @param {event} e The raw browser event
31994              */
31995             "dragover" : true,
31996             /**
31997              * @event dragenter
31998              *  Fires when the dragged row(s) first cross another DD target while being dragged
31999              * @param {Grid} this
32000              * @param {Roo.GridDD} dd The drag drop object
32001              * @param {String} targetId The target drag drop object
32002              * @param {event} e The raw browser event
32003              */
32004             "dragenter" : true,
32005             /**
32006              * @event dragout
32007              * Fires when the dragged row(s) leave another DD target while being dragged
32008              * @param {Grid} this
32009              * @param {Roo.GridDD} dd The drag drop object
32010              * @param {String} targetId The target drag drop object
32011              * @param {event} e The raw browser event
32012              */
32013             "dragout" : true,
32014         /**
32015          * @event render
32016          * Fires when the grid is rendered
32017          * @param {Grid} grid
32018          */
32019         render : true
32020     });
32021
32022     Roo.grid.Grid.superclass.constructor.call(this);
32023 };
32024 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32025     
32026     /**
32027      * @cfg {String} ddGroup - drag drop group.
32028          */
32029     
32030     /**
32031      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32032          */
32033         minColumnWidth : 25,
32034
32035     /**
32036          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32037          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32038          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32039          */
32040         autoSizeColumns : false,
32041
32042         /**
32043          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32044          */
32045         autoSizeHeaders : true,
32046
32047         /**
32048          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32049          */
32050         monitorWindowResize : true,
32051
32052         /**
32053          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32054          * rows measured to get a columns size. Default is 0 (all rows).
32055          */
32056         maxRowsToMeasure : 0,
32057
32058         /**
32059          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32060          */
32061         trackMouseOver : true,
32062
32063     /**
32064          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32065          */
32066     
32067         /**
32068          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32069          */
32070         enableDragDrop : false,
32071
32072         /**
32073          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32074          */
32075         enableColumnMove : true,
32076
32077         /**
32078          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32079          */
32080         enableColumnHide : true,
32081
32082         /**
32083          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32084          */
32085         enableRowHeightSync : false,
32086
32087         /**
32088          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32089          */
32090         stripeRows : true,
32091
32092         /**
32093          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32094          */
32095         autoHeight : false,
32096
32097     /**
32098      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
32099      */
32100     autoExpandColumn : false,
32101
32102     /**
32103     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32104     * Default is 50.
32105     */
32106     autoExpandMin : 50,
32107
32108     /**
32109     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32110     */
32111     autoExpandMax : 1000,
32112
32113     /**
32114          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32115          */
32116         view : null,
32117
32118         /**
32119      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32120          */
32121         loadMask : false,
32122     /**
32123      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32124          */
32125         dropTarget: false,
32126     // private
32127     rendered : false,
32128
32129     /**
32130     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32131     * of a fixed width. Default is false.
32132     */
32133     /**
32134     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32135     */
32136     /**
32137      * Called once after all setup has been completed and the grid is ready to be rendered.
32138      * @return {Roo.grid.Grid} this
32139      */
32140     render : function(){
32141         var c = this.container;
32142         // try to detect autoHeight/width mode
32143         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32144             this.autoHeight = true;
32145         }
32146         var view = this.getView();
32147         view.init(this);
32148
32149         c.on("click", this.onClick, this);
32150         c.on("dblclick", this.onDblClick, this);
32151         c.on("contextmenu", this.onContextMenu, this);
32152         c.on("keydown", this.onKeyDown, this);
32153
32154         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32155
32156         this.getSelectionModel().init(this);
32157
32158         view.render();
32159
32160         if(this.loadMask){
32161             this.loadMask = new Roo.LoadMask(this.container,
32162                     Roo.apply({store:this.dataSource}, this.loadMask));
32163         }
32164         
32165         
32166         if (this.toolbar && this.toolbar.xtype) {
32167             this.toolbar.container = this.getView().getHeaderPanel(true);
32168             this.toolbar = new Ext.Toolbar(this.toolbar);
32169         }
32170         if (this.footer && this.footer.xtype) {
32171             this.footer.dataSource = this.getDataSource();
32172             this.footer.container = this.getView().getFooterPanel(true);
32173             this.footer = Roo.factory(this.footer, Roo);
32174         }
32175         if (this.dropTarget && this.dropTarget.xtype) {
32176             delete this.dropTarget.xtype;
32177             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32178         }
32179         
32180         
32181         this.rendered = true;
32182         this.fireEvent('render', this);
32183         return this;
32184     },
32185
32186         /**
32187          * Reconfigures the grid to use a different Store and Column Model.
32188          * The View will be bound to the new objects and refreshed.
32189          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32190          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32191          */
32192     reconfigure : function(dataSource, colModel){
32193         if(this.loadMask){
32194             this.loadMask.destroy();
32195             this.loadMask = new Roo.LoadMask(this.container,
32196                     Roo.apply({store:dataSource}, this.loadMask));
32197         }
32198         this.view.bind(dataSource, colModel);
32199         this.dataSource = dataSource;
32200         this.colModel = colModel;
32201         this.view.refresh(true);
32202     },
32203
32204     // private
32205     onKeyDown : function(e){
32206         this.fireEvent("keydown", e);
32207     },
32208
32209     /**
32210      * Destroy this grid.
32211      * @param {Boolean} removeEl True to remove the element
32212      */
32213     destroy : function(removeEl, keepListeners){
32214         if(this.loadMask){
32215             this.loadMask.destroy();
32216         }
32217         var c = this.container;
32218         c.removeAllListeners();
32219         this.view.destroy();
32220         this.colModel.purgeListeners();
32221         if(!keepListeners){
32222             this.purgeListeners();
32223         }
32224         c.update("");
32225         if(removeEl === true){
32226             c.remove();
32227         }
32228     },
32229
32230     // private
32231     processEvent : function(name, e){
32232         this.fireEvent(name, e);
32233         var t = e.getTarget();
32234         var v = this.view;
32235         var header = v.findHeaderIndex(t);
32236         if(header !== false){
32237             this.fireEvent("header" + name, this, header, e);
32238         }else{
32239             var row = v.findRowIndex(t);
32240             var cell = v.findCellIndex(t);
32241             if(row !== false){
32242                 this.fireEvent("row" + name, this, row, e);
32243                 if(cell !== false){
32244                     this.fireEvent("cell" + name, this, row, cell, e);
32245                 }
32246             }
32247         }
32248     },
32249
32250     // private
32251     onClick : function(e){
32252         this.processEvent("click", e);
32253     },
32254
32255     // private
32256     onContextMenu : function(e, t){
32257         this.processEvent("contextmenu", e);
32258     },
32259
32260     // private
32261     onDblClick : function(e){
32262         this.processEvent("dblclick", e);
32263     },
32264
32265     // private
32266     walkCells : function(row, col, step, fn, scope){
32267         var cm = this.colModel, clen = cm.getColumnCount();
32268         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32269         if(step < 0){
32270             if(col < 0){
32271                 row--;
32272                 first = false;
32273             }
32274             while(row >= 0){
32275                 if(!first){
32276                     col = clen-1;
32277                 }
32278                 first = false;
32279                 while(col >= 0){
32280                     if(fn.call(scope || this, row, col, cm) === true){
32281                         return [row, col];
32282                     }
32283                     col--;
32284                 }
32285                 row--;
32286             }
32287         } else {
32288             if(col >= clen){
32289                 row++;
32290                 first = false;
32291             }
32292             while(row < rlen){
32293                 if(!first){
32294                     col = 0;
32295                 }
32296                 first = false;
32297                 while(col < clen){
32298                     if(fn.call(scope || this, row, col, cm) === true){
32299                         return [row, col];
32300                     }
32301                     col++;
32302                 }
32303                 row++;
32304             }
32305         }
32306         return null;
32307     },
32308
32309     // private
32310     getSelections : function(){
32311         return this.selModel.getSelections();
32312     },
32313
32314     /**
32315      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32316      * but if manual update is required this method will initiate it.
32317      */
32318     autoSize : function(){
32319         if(this.rendered){
32320             this.view.layout();
32321             if(this.view.adjustForScroll){
32322                 this.view.adjustForScroll();
32323             }
32324         }
32325     },
32326
32327     /**
32328      * Returns the grid's underlying element.
32329      * @return {Element} The element
32330      */
32331     getGridEl : function(){
32332         return this.container;
32333     },
32334
32335     // private for compatibility, overridden by editor grid
32336     stopEditing : function(){},
32337
32338     /**
32339      * Returns the grid's SelectionModel.
32340      * @return {SelectionModel}
32341      */
32342     getSelectionModel : function(){
32343         if(!this.selModel){
32344             this.selModel = new Roo.grid.RowSelectionModel();
32345         }
32346         return this.selModel;
32347     },
32348
32349     /**
32350      * Returns the grid's DataSource.
32351      * @return {DataSource}
32352      */
32353     getDataSource : function(){
32354         return this.dataSource;
32355     },
32356
32357     /**
32358      * Returns the grid's ColumnModel.
32359      * @return {ColumnModel}
32360      */
32361     getColumnModel : function(){
32362         return this.colModel;
32363     },
32364
32365     /**
32366      * Returns the grid's GridView object.
32367      * @return {GridView}
32368      */
32369     getView : function(){
32370         if(!this.view){
32371             this.view = new Roo.grid.GridView(this.viewConfig);
32372         }
32373         return this.view;
32374     },
32375     /**
32376      * Called to get grid's drag proxy text, by default returns this.ddText.
32377      * @return {String}
32378      */
32379     getDragDropText : function(){
32380         var count = this.selModel.getCount();
32381         return String.format(this.ddText, count, count == 1 ? '' : 's');
32382     }
32383 });
32384 /**
32385  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32386  * %0 is replaced with the number of selected rows.
32387  * @type String
32388  */
32389 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32390  * Based on:
32391  * Ext JS Library 1.1.1
32392  * Copyright(c) 2006-2007, Ext JS, LLC.
32393  *
32394  * Originally Released Under LGPL - original licence link has changed is not relivant.
32395  *
32396  * Fork - LGPL
32397  * <script type="text/javascript">
32398  */
32399  
32400 Roo.grid.AbstractGridView = function(){
32401         this.grid = null;
32402         
32403         this.events = {
32404             "beforerowremoved" : true,
32405             "beforerowsinserted" : true,
32406             "beforerefresh" : true,
32407             "rowremoved" : true,
32408             "rowsinserted" : true,
32409             "rowupdated" : true,
32410             "refresh" : true
32411         };
32412     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32413 };
32414
32415 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32416     rowClass : "x-grid-row",
32417     cellClass : "x-grid-cell",
32418     tdClass : "x-grid-td",
32419     hdClass : "x-grid-hd",
32420     splitClass : "x-grid-hd-split",
32421     
32422         init: function(grid){
32423         this.grid = grid;
32424                 var cid = this.grid.getGridEl().id;
32425         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32426         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32427         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32428         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32429         },
32430         
32431         getColumnRenderers : function(){
32432         var renderers = [];
32433         var cm = this.grid.colModel;
32434         var colCount = cm.getColumnCount();
32435         for(var i = 0; i < colCount; i++){
32436             renderers[i] = cm.getRenderer(i);
32437         }
32438         return renderers;
32439     },
32440     
32441     getColumnIds : function(){
32442         var ids = [];
32443         var cm = this.grid.colModel;
32444         var colCount = cm.getColumnCount();
32445         for(var i = 0; i < colCount; i++){
32446             ids[i] = cm.getColumnId(i);
32447         }
32448         return ids;
32449     },
32450     
32451     getDataIndexes : function(){
32452         if(!this.indexMap){
32453             this.indexMap = this.buildIndexMap();
32454         }
32455         return this.indexMap.colToData;
32456     },
32457     
32458     getColumnIndexByDataIndex : function(dataIndex){
32459         if(!this.indexMap){
32460             this.indexMap = this.buildIndexMap();
32461         }
32462         return this.indexMap.dataToCol[dataIndex];
32463     },
32464     
32465     /**
32466      * Set a css style for a column dynamically. 
32467      * @param {Number} colIndex The index of the column
32468      * @param {String} name The css property name
32469      * @param {String} value The css value
32470      */
32471     setCSSStyle : function(colIndex, name, value){
32472         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32473         Roo.util.CSS.updateRule(selector, name, value);
32474     },
32475     
32476     generateRules : function(cm){
32477         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32478         Roo.util.CSS.removeStyleSheet(rulesId);
32479         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32480             var cid = cm.getColumnId(i);
32481             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32482                          this.tdSelector, cid, " {\n}\n",
32483                          this.hdSelector, cid, " {\n}\n",
32484                          this.splitSelector, cid, " {\n}\n");
32485         }
32486         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32487     }
32488 });/*
32489  * Based on:
32490  * Ext JS Library 1.1.1
32491  * Copyright(c) 2006-2007, Ext JS, LLC.
32492  *
32493  * Originally Released Under LGPL - original licence link has changed is not relivant.
32494  *
32495  * Fork - LGPL
32496  * <script type="text/javascript">
32497  */
32498
32499 // private
32500 // This is a support class used internally by the Grid components
32501 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32502     this.grid = grid;
32503     this.view = grid.getView();
32504     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32505     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32506     if(hd2){
32507         this.setHandleElId(Roo.id(hd));
32508         this.setOuterHandleElId(Roo.id(hd2));
32509     }
32510     this.scroll = false;
32511 };
32512 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32513     maxDragWidth: 120,
32514     getDragData : function(e){
32515         var t = Roo.lib.Event.getTarget(e);
32516         var h = this.view.findHeaderCell(t);
32517         if(h){
32518             return {ddel: h.firstChild, header:h};
32519         }
32520         return false;
32521     },
32522
32523     onInitDrag : function(e){
32524         this.view.headersDisabled = true;
32525         var clone = this.dragData.ddel.cloneNode(true);
32526         clone.id = Roo.id();
32527         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32528         this.proxy.update(clone);
32529         return true;
32530     },
32531
32532     afterValidDrop : function(){
32533         var v = this.view;
32534         setTimeout(function(){
32535             v.headersDisabled = false;
32536         }, 50);
32537     },
32538
32539     afterInvalidDrop : function(){
32540         var v = this.view;
32541         setTimeout(function(){
32542             v.headersDisabled = false;
32543         }, 50);
32544     }
32545 });
32546 /*
32547  * Based on:
32548  * Ext JS Library 1.1.1
32549  * Copyright(c) 2006-2007, Ext JS, LLC.
32550  *
32551  * Originally Released Under LGPL - original licence link has changed is not relivant.
32552  *
32553  * Fork - LGPL
32554  * <script type="text/javascript">
32555  */
32556 // private
32557 // This is a support class used internally by the Grid components
32558 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32559     this.grid = grid;
32560     this.view = grid.getView();
32561     // split the proxies so they don't interfere with mouse events
32562     this.proxyTop = Roo.DomHelper.append(document.body, {
32563         cls:"col-move-top", html:"&#160;"
32564     }, true);
32565     this.proxyBottom = Roo.DomHelper.append(document.body, {
32566         cls:"col-move-bottom", html:"&#160;"
32567     }, true);
32568     this.proxyTop.hide = this.proxyBottom.hide = function(){
32569         this.setLeftTop(-100,-100);
32570         this.setStyle("visibility", "hidden");
32571     };
32572     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32573     // temporarily disabled
32574     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32575     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32576 };
32577 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32578     proxyOffsets : [-4, -9],
32579     fly: Roo.Element.fly,
32580
32581     getTargetFromEvent : function(e){
32582         var t = Roo.lib.Event.getTarget(e);
32583         var cindex = this.view.findCellIndex(t);
32584         if(cindex !== false){
32585             return this.view.getHeaderCell(cindex);
32586         }
32587     },
32588
32589     nextVisible : function(h){
32590         var v = this.view, cm = this.grid.colModel;
32591         h = h.nextSibling;
32592         while(h){
32593             if(!cm.isHidden(v.getCellIndex(h))){
32594                 return h;
32595             }
32596             h = h.nextSibling;
32597         }
32598         return null;
32599     },
32600
32601     prevVisible : function(h){
32602         var v = this.view, cm = this.grid.colModel;
32603         h = h.prevSibling;
32604         while(h){
32605             if(!cm.isHidden(v.getCellIndex(h))){
32606                 return h;
32607             }
32608             h = h.prevSibling;
32609         }
32610         return null;
32611     },
32612
32613     positionIndicator : function(h, n, e){
32614         var x = Roo.lib.Event.getPageX(e);
32615         var r = Roo.lib.Dom.getRegion(n.firstChild);
32616         var px, pt, py = r.top + this.proxyOffsets[1];
32617         if((r.right - x) <= (r.right-r.left)/2){
32618             px = r.right+this.view.borderWidth;
32619             pt = "after";
32620         }else{
32621             px = r.left;
32622             pt = "before";
32623         }
32624         var oldIndex = this.view.getCellIndex(h);
32625         var newIndex = this.view.getCellIndex(n);
32626
32627         if(this.grid.colModel.isFixed(newIndex)){
32628             return false;
32629         }
32630
32631         var locked = this.grid.colModel.isLocked(newIndex);
32632
32633         if(pt == "after"){
32634             newIndex++;
32635         }
32636         if(oldIndex < newIndex){
32637             newIndex--;
32638         }
32639         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32640             return false;
32641         }
32642         px +=  this.proxyOffsets[0];
32643         this.proxyTop.setLeftTop(px, py);
32644         this.proxyTop.show();
32645         if(!this.bottomOffset){
32646             this.bottomOffset = this.view.mainHd.getHeight();
32647         }
32648         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32649         this.proxyBottom.show();
32650         return pt;
32651     },
32652
32653     onNodeEnter : function(n, dd, e, data){
32654         if(data.header != n){
32655             this.positionIndicator(data.header, n, e);
32656         }
32657     },
32658
32659     onNodeOver : function(n, dd, e, data){
32660         var result = false;
32661         if(data.header != n){
32662             result = this.positionIndicator(data.header, n, e);
32663         }
32664         if(!result){
32665             this.proxyTop.hide();
32666             this.proxyBottom.hide();
32667         }
32668         return result ? this.dropAllowed : this.dropNotAllowed;
32669     },
32670
32671     onNodeOut : function(n, dd, e, data){
32672         this.proxyTop.hide();
32673         this.proxyBottom.hide();
32674     },
32675
32676     onNodeDrop : function(n, dd, e, data){
32677         var h = data.header;
32678         if(h != n){
32679             var cm = this.grid.colModel;
32680             var x = Roo.lib.Event.getPageX(e);
32681             var r = Roo.lib.Dom.getRegion(n.firstChild);
32682             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32683             var oldIndex = this.view.getCellIndex(h);
32684             var newIndex = this.view.getCellIndex(n);
32685             var locked = cm.isLocked(newIndex);
32686             if(pt == "after"){
32687                 newIndex++;
32688             }
32689             if(oldIndex < newIndex){
32690                 newIndex--;
32691             }
32692             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32693                 return false;
32694             }
32695             cm.setLocked(oldIndex, locked, true);
32696             cm.moveColumn(oldIndex, newIndex);
32697             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32698             return true;
32699         }
32700         return false;
32701     }
32702 });
32703 /*
32704  * Based on:
32705  * Ext JS Library 1.1.1
32706  * Copyright(c) 2006-2007, Ext JS, LLC.
32707  *
32708  * Originally Released Under LGPL - original licence link has changed is not relivant.
32709  *
32710  * Fork - LGPL
32711  * <script type="text/javascript">
32712  */
32713   
32714 /**
32715  * @class Roo.grid.GridView
32716  * @extends Roo.util.Observable
32717  *
32718  * @constructor
32719  * @param {Object} config
32720  */
32721 Roo.grid.GridView = function(config){
32722     Roo.grid.GridView.superclass.constructor.call(this);
32723     this.el = null;
32724
32725     Roo.apply(this, config);
32726 };
32727
32728 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32729
32730     /**
32731      * Override this function to apply custom css classes to rows during rendering
32732      * @param {Record} record The record
32733      * @param {Number} index
32734      * @method getRowClass
32735      */
32736     rowClass : "x-grid-row",
32737
32738     cellClass : "x-grid-col",
32739
32740     tdClass : "x-grid-td",
32741
32742     hdClass : "x-grid-hd",
32743
32744     splitClass : "x-grid-split",
32745
32746     sortClasses : ["sort-asc", "sort-desc"],
32747
32748     enableMoveAnim : false,
32749
32750     hlColor: "C3DAF9",
32751
32752     dh : Roo.DomHelper,
32753
32754     fly : Roo.Element.fly,
32755
32756     css : Roo.util.CSS,
32757
32758     borderWidth: 1,
32759
32760     splitOffset: 3,
32761
32762     scrollIncrement : 22,
32763
32764     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32765
32766     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32767
32768     bind : function(ds, cm){
32769         if(this.ds){
32770             this.ds.un("load", this.onLoad, this);
32771             this.ds.un("datachanged", this.onDataChange, this);
32772             this.ds.un("add", this.onAdd, this);
32773             this.ds.un("remove", this.onRemove, this);
32774             this.ds.un("update", this.onUpdate, this);
32775             this.ds.un("clear", this.onClear, this);
32776         }
32777         if(ds){
32778             ds.on("load", this.onLoad, this);
32779             ds.on("datachanged", this.onDataChange, this);
32780             ds.on("add", this.onAdd, this);
32781             ds.on("remove", this.onRemove, this);
32782             ds.on("update", this.onUpdate, this);
32783             ds.on("clear", this.onClear, this);
32784         }
32785         this.ds = ds;
32786
32787         if(this.cm){
32788             this.cm.un("widthchange", this.onColWidthChange, this);
32789             this.cm.un("headerchange", this.onHeaderChange, this);
32790             this.cm.un("hiddenchange", this.onHiddenChange, this);
32791             this.cm.un("columnmoved", this.onColumnMove, this);
32792             this.cm.un("columnlockchange", this.onColumnLock, this);
32793         }
32794         if(cm){
32795             this.generateRules(cm);
32796             cm.on("widthchange", this.onColWidthChange, this);
32797             cm.on("headerchange", this.onHeaderChange, this);
32798             cm.on("hiddenchange", this.onHiddenChange, this);
32799             cm.on("columnmoved", this.onColumnMove, this);
32800             cm.on("columnlockchange", this.onColumnLock, this);
32801         }
32802         this.cm = cm;
32803     },
32804
32805     init: function(grid){
32806                 Roo.grid.GridView.superclass.init.call(this, grid);
32807
32808                 this.bind(grid.dataSource, grid.colModel);
32809
32810             grid.on("headerclick", this.handleHeaderClick, this);
32811
32812         if(grid.trackMouseOver){
32813             grid.on("mouseover", this.onRowOver, this);
32814                 grid.on("mouseout", this.onRowOut, this);
32815             }
32816             grid.cancelTextSelection = function(){};
32817                 this.gridId = grid.id;
32818
32819                 var tpls = this.templates || {};
32820
32821                 if(!tpls.master){
32822                     tpls.master = new Roo.Template(
32823                        '<div class="x-grid" hidefocus="true">',
32824                           '<div class="x-grid-topbar"></div>',
32825                           '<div class="x-grid-scroller"><div></div></div>',
32826                           '<div class="x-grid-locked">',
32827                               '<div class="x-grid-header">{lockedHeader}</div>',
32828                               '<div class="x-grid-body">{lockedBody}</div>',
32829                           "</div>",
32830                           '<div class="x-grid-viewport">',
32831                               '<div class="x-grid-header">{header}</div>',
32832                               '<div class="x-grid-body">{body}</div>',
32833                           "</div>",
32834                           '<div class="x-grid-bottombar"></div>',
32835                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32836                           '<div class="x-grid-resize-proxy">&#160;</div>',
32837                        "</div>"
32838                     );
32839                     tpls.master.disableformats = true;
32840                 }
32841
32842                 if(!tpls.header){
32843                     tpls.header = new Roo.Template(
32844                        '<table border="0" cellspacing="0" cellpadding="0">',
32845                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32846                        "</table>{splits}"
32847                     );
32848                     tpls.header.disableformats = true;
32849                 }
32850                 tpls.header.compile();
32851
32852                 if(!tpls.hcell){
32853                     tpls.hcell = new Roo.Template(
32854                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32855                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32856                         "</div></td>"
32857                      );
32858                      tpls.hcell.disableFormats = true;
32859                 }
32860                 tpls.hcell.compile();
32861
32862                 if(!tpls.hsplit){
32863                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32864                     tpls.hsplit.disableFormats = true;
32865                 }
32866                 tpls.hsplit.compile();
32867
32868                 if(!tpls.body){
32869                     tpls.body = new Roo.Template(
32870                        '<table border="0" cellspacing="0" cellpadding="0">',
32871                        "<tbody>{rows}</tbody>",
32872                        "</table>"
32873                     );
32874                     tpls.body.disableFormats = true;
32875                 }
32876                 tpls.body.compile();
32877
32878                 if(!tpls.row){
32879                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32880                     tpls.row.disableFormats = true;
32881                 }
32882                 tpls.row.compile();
32883
32884                 if(!tpls.cell){
32885                     tpls.cell = new Roo.Template(
32886                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32887                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32888                         "</td>"
32889                     );
32890             tpls.cell.disableFormats = true;
32891         }
32892                 tpls.cell.compile();
32893
32894                 this.templates = tpls;
32895         },
32896
32897         // remap these for backwards compat
32898     onColWidthChange : function(){
32899         this.updateColumns.apply(this, arguments);
32900     },
32901     onHeaderChange : function(){
32902         this.updateHeaders.apply(this, arguments);
32903     }, 
32904     onHiddenChange : function(){
32905         this.handleHiddenChange.apply(this, arguments);
32906     },
32907     onColumnMove : function(){
32908         this.handleColumnMove.apply(this, arguments);
32909     },
32910     onColumnLock : function(){
32911         this.handleLockChange.apply(this, arguments);
32912     },
32913
32914     onDataChange : function(){
32915         this.refresh();
32916         this.updateHeaderSortState();
32917     },
32918
32919         onClear : function(){
32920         this.refresh();
32921     },
32922
32923         onUpdate : function(ds, record){
32924         this.refreshRow(record);
32925     },
32926
32927     refreshRow : function(record){
32928         var ds = this.ds, index;
32929         if(typeof record == 'number'){
32930             index = record;
32931             record = ds.getAt(index);
32932         }else{
32933             index = ds.indexOf(record);
32934         }
32935         this.insertRows(ds, index, index, true);
32936         this.onRemove(ds, record, index+1, true);
32937         this.syncRowHeights(index, index);
32938         this.layout();
32939         this.fireEvent("rowupdated", this, index, record);
32940     },
32941
32942     onAdd : function(ds, records, index){
32943         this.insertRows(ds, index, index + (records.length-1));
32944     },
32945
32946     onRemove : function(ds, record, index, isUpdate){
32947         if(isUpdate !== true){
32948             this.fireEvent("beforerowremoved", this, index, record);
32949         }
32950         var bt = this.getBodyTable(), lt = this.getLockedTable();
32951         if(bt.rows[index]){
32952             bt.firstChild.removeChild(bt.rows[index]);
32953         }
32954         if(lt.rows[index]){
32955             lt.firstChild.removeChild(lt.rows[index]);
32956         }
32957         if(isUpdate !== true){
32958             this.stripeRows(index);
32959             this.syncRowHeights(index, index);
32960             this.layout();
32961             this.fireEvent("rowremoved", this, index, record);
32962         }
32963     },
32964
32965     onLoad : function(){
32966         this.scrollToTop();
32967     },
32968
32969     /**
32970      * Scrolls the grid to the top
32971      */
32972     scrollToTop : function(){
32973         if(this.scroller){
32974             this.scroller.dom.scrollTop = 0;
32975             this.syncScroll();
32976         }
32977     },
32978
32979     /**
32980      * Gets a panel in the header of the grid that can be used for toolbars etc.
32981      * After modifying the contents of this panel a call to grid.autoSize() may be
32982      * required to register any changes in size.
32983      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32984      * @return Roo.Element
32985      */
32986     getHeaderPanel : function(doShow){
32987         if(doShow){
32988             this.headerPanel.show();
32989         }
32990         return this.headerPanel;
32991         },
32992
32993         /**
32994      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32995      * After modifying the contents of this panel a call to grid.autoSize() may be
32996      * required to register any changes in size.
32997      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32998      * @return Roo.Element
32999      */
33000     getFooterPanel : function(doShow){
33001         if(doShow){
33002             this.footerPanel.show();
33003         }
33004         return this.footerPanel;
33005         },
33006
33007         initElements : function(){
33008             var E = Roo.Element;
33009             var el = this.grid.getGridEl().dom.firstChild;
33010             var cs = el.childNodes;
33011
33012             this.el = new E(el);
33013             this.headerPanel = new E(el.firstChild);
33014             this.headerPanel.enableDisplayMode("block");
33015
33016         this.scroller = new E(cs[1]);
33017             this.scrollSizer = new E(this.scroller.dom.firstChild);
33018
33019             this.lockedWrap = new E(cs[2]);
33020             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33021             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33022
33023             this.mainWrap = new E(cs[3]);
33024             this.mainHd = new E(this.mainWrap.dom.firstChild);
33025             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33026
33027             this.footerPanel = new E(cs[4]);
33028             this.footerPanel.enableDisplayMode("block");
33029
33030         this.focusEl = new E(cs[5]);
33031         this.focusEl.swallowEvent("click", true);
33032         this.resizeProxy = new E(cs[6]);
33033
33034             this.headerSelector = String.format(
33035                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33036                this.lockedHd.id, this.mainHd.id
33037             );
33038
33039             this.splitterSelector = String.format(
33040                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33041                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33042             );
33043     },
33044     idToCssName : function(s)
33045     {
33046         return s.replace(/[^a-z0-9]+/ig, '-');
33047     },
33048
33049         getHeaderCell : function(index){
33050             return Roo.DomQuery.select(this.headerSelector)[index];
33051         },
33052
33053         getHeaderCellMeasure : function(index){
33054             return this.getHeaderCell(index).firstChild;
33055         },
33056
33057         getHeaderCellText : function(index){
33058             return this.getHeaderCell(index).firstChild.firstChild;
33059         },
33060
33061         getLockedTable : function(){
33062             return this.lockedBody.dom.firstChild;
33063         },
33064
33065         getBodyTable : function(){
33066             return this.mainBody.dom.firstChild;
33067         },
33068
33069         getLockedRow : function(index){
33070             return this.getLockedTable().rows[index];
33071         },
33072
33073         getRow : function(index){
33074             return this.getBodyTable().rows[index];
33075         },
33076
33077         getRowComposite : function(index){
33078             if(!this.rowEl){
33079                 this.rowEl = new Roo.CompositeElementLite();
33080             }
33081         var els = [], lrow, mrow;
33082         if(lrow = this.getLockedRow(index)){
33083             els.push(lrow);
33084         }
33085         if(mrow = this.getRow(index)){
33086             els.push(mrow);
33087         }
33088         this.rowEl.elements = els;
33089             return this.rowEl;
33090         },
33091
33092         getCell : function(rowIndex, colIndex){
33093             var locked = this.cm.getLockedCount();
33094             var source;
33095             if(colIndex < locked){
33096                 source = this.lockedBody.dom.firstChild;
33097             }else{
33098                 source = this.mainBody.dom.firstChild;
33099                 colIndex -= locked;
33100             }
33101         return source.rows[rowIndex].childNodes[colIndex];
33102         },
33103
33104         getCellText : function(rowIndex, colIndex){
33105             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33106         },
33107
33108         getCellBox : function(cell){
33109             var b = this.fly(cell).getBox();
33110         if(Roo.isOpera){ // opera fails to report the Y
33111             b.y = cell.offsetTop + this.mainBody.getY();
33112         }
33113         return b;
33114     },
33115
33116     getCellIndex : function(cell){
33117         var id = String(cell.className).match(this.cellRE);
33118         if(id){
33119             return parseInt(id[1], 10);
33120         }
33121         return 0;
33122     },
33123
33124     findHeaderIndex : function(n){
33125         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33126         return r ? this.getCellIndex(r) : false;
33127     },
33128
33129     findHeaderCell : function(n){
33130         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33131         return r ? r : false;
33132     },
33133
33134     findRowIndex : function(n){
33135         if(!n){
33136             return false;
33137         }
33138         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33139         return r ? r.rowIndex : false;
33140     },
33141
33142     findCellIndex : function(node){
33143         var stop = this.el.dom;
33144         while(node && node != stop){
33145             if(this.findRE.test(node.className)){
33146                 return this.getCellIndex(node);
33147             }
33148             node = node.parentNode;
33149         }
33150         return false;
33151     },
33152
33153     getColumnId : function(index){
33154             return this.cm.getColumnId(index);
33155         },
33156
33157         getSplitters : function(){
33158             if(this.splitterSelector){
33159                return Roo.DomQuery.select(this.splitterSelector);
33160             }else{
33161                 return null;
33162             }
33163         },
33164
33165         getSplitter : function(index){
33166             return this.getSplitters()[index];
33167         },
33168
33169     onRowOver : function(e, t){
33170         var row;
33171         if((row = this.findRowIndex(t)) !== false){
33172             this.getRowComposite(row).addClass("x-grid-row-over");
33173         }
33174     },
33175
33176     onRowOut : function(e, t){
33177         var row;
33178         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33179             this.getRowComposite(row).removeClass("x-grid-row-over");
33180         }
33181     },
33182
33183     renderHeaders : function(){
33184             var cm = this.cm;
33185         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33186         var cb = [], lb = [], sb = [], lsb = [], p = {};
33187         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33188             p.cellId = "x-grid-hd-0-" + i;
33189             p.splitId = "x-grid-csplit-0-" + i;
33190             p.id = cm.getColumnId(i);
33191             p.title = cm.getColumnTooltip(i) || "";
33192             p.value = cm.getColumnHeader(i) || "";
33193             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33194             if(!cm.isLocked(i)){
33195                 cb[cb.length] = ct.apply(p);
33196                 sb[sb.length] = st.apply(p);
33197             }else{
33198                 lb[lb.length] = ct.apply(p);
33199                 lsb[lsb.length] = st.apply(p);
33200             }
33201         }
33202         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33203                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33204         },
33205
33206         updateHeaders : function(){
33207         var html = this.renderHeaders();
33208         this.lockedHd.update(html[0]);
33209         this.mainHd.update(html[1]);
33210     },
33211
33212     /**
33213      * Focuses the specified row.
33214      * @param {Number} row The row index
33215      */
33216     focusRow : function(row){
33217         var x = this.scroller.dom.scrollLeft;
33218         this.focusCell(row, 0, false);
33219         this.scroller.dom.scrollLeft = x;
33220     },
33221
33222     /**
33223      * Focuses the specified cell.
33224      * @param {Number} row The row index
33225      * @param {Number} col The column index
33226      * @param {Boolean} hscroll false to disable horizontal scrolling
33227      */
33228     focusCell : function(row, col, hscroll){
33229         var el = this.ensureVisible(row, col, hscroll);
33230         this.focusEl.alignTo(el, "tl-tl");
33231         if(Roo.isGecko){
33232             this.focusEl.focus();
33233         }else{
33234             this.focusEl.focus.defer(1, this.focusEl);
33235         }
33236     },
33237
33238     /**
33239      * Scrolls the specified cell into view
33240      * @param {Number} row The row index
33241      * @param {Number} col The column index
33242      * @param {Boolean} hscroll false to disable horizontal scrolling
33243      */
33244     ensureVisible : function(row, col, hscroll){
33245         if(typeof row != "number"){
33246             row = row.rowIndex;
33247         }
33248         if(row < 0 && row >= this.ds.getCount()){
33249             return;
33250         }
33251         col = (col !== undefined ? col : 0);
33252         var cm = this.grid.colModel;
33253         while(cm.isHidden(col)){
33254             col++;
33255         }
33256
33257         var el = this.getCell(row, col);
33258         if(!el){
33259             return;
33260         }
33261         var c = this.scroller.dom;
33262
33263         var ctop = parseInt(el.offsetTop, 10);
33264         var cleft = parseInt(el.offsetLeft, 10);
33265         var cbot = ctop + el.offsetHeight;
33266         var cright = cleft + el.offsetWidth;
33267
33268         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33269         var stop = parseInt(c.scrollTop, 10);
33270         var sleft = parseInt(c.scrollLeft, 10);
33271         var sbot = stop + ch;
33272         var sright = sleft + c.clientWidth;
33273
33274         if(ctop < stop){
33275                 c.scrollTop = ctop;
33276         }else if(cbot > sbot){
33277             c.scrollTop = cbot-ch;
33278         }
33279
33280         if(hscroll !== false){
33281             if(cleft < sleft){
33282                 c.scrollLeft = cleft;
33283             }else if(cright > sright){
33284                 c.scrollLeft = cright-c.clientWidth;
33285             }
33286         }
33287         return el;
33288     },
33289
33290     updateColumns : function(){
33291         this.grid.stopEditing();
33292         var cm = this.grid.colModel, colIds = this.getColumnIds();
33293         //var totalWidth = cm.getTotalWidth();
33294         var pos = 0;
33295         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33296             //if(cm.isHidden(i)) continue;
33297             var w = cm.getColumnWidth(i);
33298             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33299             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33300         }
33301         this.updateSplitters();
33302     },
33303
33304     generateRules : function(cm){
33305         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33306         Roo.util.CSS.removeStyleSheet(rulesId);
33307         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33308             var cid = cm.getColumnId(i);
33309             var align = '';
33310             if(cm.config[i].align){
33311                 align = 'text-align:'+cm.config[i].align+';';
33312             }
33313             var hidden = '';
33314             if(cm.isHidden(i)){
33315                 hidden = 'display:none;';
33316             }
33317             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33318             ruleBuf.push(
33319                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33320                     this.hdSelector, cid, " {\n", align, width, "}\n",
33321                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33322                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33323         }
33324         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33325     },
33326
33327     updateSplitters : function(){
33328         var cm = this.cm, s = this.getSplitters();
33329         if(s){ // splitters not created yet
33330             var pos = 0, locked = true;
33331             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33332                 if(cm.isHidden(i)) continue;
33333                 var w = cm.getColumnWidth(i);
33334                 if(!cm.isLocked(i) && locked){
33335                     pos = 0;
33336                     locked = false;
33337                 }
33338                 pos += w;
33339                 s[i].style.left = (pos-this.splitOffset) + "px";
33340             }
33341         }
33342     },
33343
33344     handleHiddenChange : function(colModel, colIndex, hidden){
33345         if(hidden){
33346             this.hideColumn(colIndex);
33347         }else{
33348             this.unhideColumn(colIndex);
33349         }
33350     },
33351
33352     hideColumn : function(colIndex){
33353         var cid = this.getColumnId(colIndex);
33354         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33355         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33356         if(Roo.isSafari){
33357             this.updateHeaders();
33358         }
33359         this.updateSplitters();
33360         this.layout();
33361     },
33362
33363     unhideColumn : function(colIndex){
33364         var cid = this.getColumnId(colIndex);
33365         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33366         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33367
33368         if(Roo.isSafari){
33369             this.updateHeaders();
33370         }
33371         this.updateSplitters();
33372         this.layout();
33373     },
33374
33375     insertRows : function(dm, firstRow, lastRow, isUpdate){
33376         if(firstRow == 0 && lastRow == dm.getCount()-1){
33377             this.refresh();
33378         }else{
33379             if(!isUpdate){
33380                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33381             }
33382             var s = this.getScrollState();
33383             var markup = this.renderRows(firstRow, lastRow);
33384             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33385             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33386             this.restoreScroll(s);
33387             if(!isUpdate){
33388                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33389                 this.syncRowHeights(firstRow, lastRow);
33390                 this.stripeRows(firstRow);
33391                 this.layout();
33392             }
33393         }
33394     },
33395
33396     bufferRows : function(markup, target, index){
33397         var before = null, trows = target.rows, tbody = target.tBodies[0];
33398         if(index < trows.length){
33399             before = trows[index];
33400         }
33401         var b = document.createElement("div");
33402         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33403         var rows = b.firstChild.rows;
33404         for(var i = 0, len = rows.length; i < len; i++){
33405             if(before){
33406                 tbody.insertBefore(rows[0], before);
33407             }else{
33408                 tbody.appendChild(rows[0]);
33409             }
33410         }
33411         b.innerHTML = "";
33412         b = null;
33413     },
33414
33415     deleteRows : function(dm, firstRow, lastRow){
33416         if(dm.getRowCount()<1){
33417             this.fireEvent("beforerefresh", this);
33418             this.mainBody.update("");
33419             this.lockedBody.update("");
33420             this.fireEvent("refresh", this);
33421         }else{
33422             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33423             var bt = this.getBodyTable();
33424             var tbody = bt.firstChild;
33425             var rows = bt.rows;
33426             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33427                 tbody.removeChild(rows[firstRow]);
33428             }
33429             this.stripeRows(firstRow);
33430             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33431         }
33432     },
33433
33434     updateRows : function(dataSource, firstRow, lastRow){
33435         var s = this.getScrollState();
33436         this.refresh();
33437         this.restoreScroll(s);
33438     },
33439
33440     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33441         if(!noRefresh){
33442            this.refresh();
33443         }
33444         this.updateHeaderSortState();
33445     },
33446
33447     getScrollState : function(){
33448         var sb = this.scroller.dom;
33449         return {left: sb.scrollLeft, top: sb.scrollTop};
33450     },
33451
33452     stripeRows : function(startRow){
33453         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33454             return;
33455         }
33456         startRow = startRow || 0;
33457         var rows = this.getBodyTable().rows;
33458         var lrows = this.getLockedTable().rows;
33459         var cls = ' x-grid-row-alt ';
33460         for(var i = startRow, len = rows.length; i < len; i++){
33461             var row = rows[i], lrow = lrows[i];
33462             var isAlt = ((i+1) % 2 == 0);
33463             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33464             if(isAlt == hasAlt){
33465                 continue;
33466             }
33467             if(isAlt){
33468                 row.className += " x-grid-row-alt";
33469             }else{
33470                 row.className = row.className.replace("x-grid-row-alt", "");
33471             }
33472             if(lrow){
33473                 lrow.className = row.className;
33474             }
33475         }
33476     },
33477
33478     restoreScroll : function(state){
33479         var sb = this.scroller.dom;
33480         sb.scrollLeft = state.left;
33481         sb.scrollTop = state.top;
33482         this.syncScroll();
33483     },
33484
33485     syncScroll : function(){
33486         var sb = this.scroller.dom;
33487         var sh = this.mainHd.dom;
33488         var bs = this.mainBody.dom;
33489         var lv = this.lockedBody.dom;
33490         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33491         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33492     },
33493
33494     handleScroll : function(e){
33495         this.syncScroll();
33496         var sb = this.scroller.dom;
33497         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33498         e.stopEvent();
33499     },
33500
33501     handleWheel : function(e){
33502         var d = e.getWheelDelta();
33503         this.scroller.dom.scrollTop -= d*22;
33504         // set this here to prevent jumpy scrolling on large tables
33505         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33506         e.stopEvent();
33507     },
33508
33509     renderRows : function(startRow, endRow){
33510         // pull in all the crap needed to render rows
33511         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33512         var colCount = cm.getColumnCount();
33513
33514         if(ds.getCount() < 1){
33515             return ["", ""];
33516         }
33517
33518         // build a map for all the columns
33519         var cs = [];
33520         for(var i = 0; i < colCount; i++){
33521             var name = cm.getDataIndex(i);
33522             cs[i] = {
33523                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33524                 renderer : cm.getRenderer(i),
33525                 id : cm.getColumnId(i),
33526                 locked : cm.isLocked(i)
33527             };
33528         }
33529
33530         startRow = startRow || 0;
33531         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33532
33533         // records to render
33534         var rs = ds.getRange(startRow, endRow);
33535
33536         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33537     },
33538
33539     // As much as I hate to duplicate code, this was branched because FireFox really hates
33540     // [].join("") on strings. The performance difference was substantial enough to
33541     // branch this function
33542     doRender : Roo.isGecko ?
33543             function(cs, rs, ds, startRow, colCount, stripe){
33544                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33545                 // buffers
33546                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33547                 for(var j = 0, len = rs.length; j < len; j++){
33548                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33549                     for(var i = 0; i < colCount; i++){
33550                         c = cs[i];
33551                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33552                         p.id = c.id;
33553                         p.css = p.attr = "";
33554                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33555                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33556                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33557                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33558                         }
33559                         var markup = ct.apply(p);
33560                         if(!c.locked){
33561                             cb+= markup;
33562                         }else{
33563                             lcb+= markup;
33564                         }
33565                     }
33566                     var alt = [];
33567                     if(stripe && ((rowIndex+1) % 2 == 0)){
33568                         alt[0] = "x-grid-row-alt";
33569                     }
33570                     if(r.dirty){
33571                         alt[1] = " x-grid-dirty-row";
33572                     }
33573                     rp.cells = lcb;
33574                     if(this.getRowClass){
33575                         alt[2] = this.getRowClass(r, rowIndex);
33576                     }
33577                     rp.alt = alt.join(" ");
33578                     lbuf+= rt.apply(rp);
33579                     rp.cells = cb;
33580                     buf+=  rt.apply(rp);
33581                 }
33582                 return [lbuf, buf];
33583             } :
33584             function(cs, rs, ds, startRow, colCount, stripe){
33585                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33586                 // buffers
33587                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33588                 for(var j = 0, len = rs.length; j < len; j++){
33589                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33590                     for(var i = 0; i < colCount; i++){
33591                         c = cs[i];
33592                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33593                         p.id = c.id;
33594                         p.css = p.attr = "";
33595                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33596                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33597                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33598                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33599                         }
33600                         var markup = ct.apply(p);
33601                         if(!c.locked){
33602                             cb[cb.length] = markup;
33603                         }else{
33604                             lcb[lcb.length] = markup;
33605                         }
33606                     }
33607                     var alt = [];
33608                     if(stripe && ((rowIndex+1) % 2 == 0)){
33609                         alt[0] = "x-grid-row-alt";
33610                     }
33611                     if(r.dirty){
33612                         alt[1] = " x-grid-dirty-row";
33613                     }
33614                     rp.cells = lcb;
33615                     if(this.getRowClass){
33616                         alt[2] = this.getRowClass(r, rowIndex);
33617                     }
33618                     rp.alt = alt.join(" ");
33619                     rp.cells = lcb.join("");
33620                     lbuf[lbuf.length] = rt.apply(rp);
33621                     rp.cells = cb.join("");
33622                     buf[buf.length] =  rt.apply(rp);
33623                 }
33624                 return [lbuf.join(""), buf.join("")];
33625             },
33626
33627     renderBody : function(){
33628         var markup = this.renderRows();
33629         var bt = this.templates.body;
33630         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33631     },
33632
33633     /**
33634      * Refreshes the grid
33635      * @param {Boolean} headersToo
33636      */
33637     refresh : function(headersToo){
33638         this.fireEvent("beforerefresh", this);
33639         this.grid.stopEditing();
33640         var result = this.renderBody();
33641         this.lockedBody.update(result[0]);
33642         this.mainBody.update(result[1]);
33643         if(headersToo === true){
33644             this.updateHeaders();
33645             this.updateColumns();
33646             this.updateSplitters();
33647             this.updateHeaderSortState();
33648         }
33649         this.syncRowHeights();
33650         this.layout();
33651         this.fireEvent("refresh", this);
33652     },
33653
33654     handleColumnMove : function(cm, oldIndex, newIndex){
33655         this.indexMap = null;
33656         var s = this.getScrollState();
33657         this.refresh(true);
33658         this.restoreScroll(s);
33659         this.afterMove(newIndex);
33660     },
33661
33662     afterMove : function(colIndex){
33663         if(this.enableMoveAnim && Roo.enableFx){
33664             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33665         }
33666     },
33667
33668     updateCell : function(dm, rowIndex, dataIndex){
33669         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33670         if(typeof colIndex == "undefined"){ // not present in grid
33671             return;
33672         }
33673         var cm = this.grid.colModel;
33674         var cell = this.getCell(rowIndex, colIndex);
33675         var cellText = this.getCellText(rowIndex, colIndex);
33676
33677         var p = {
33678             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33679             id : cm.getColumnId(colIndex),
33680             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33681         };
33682         var renderer = cm.getRenderer(colIndex);
33683         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33684         if(typeof val == "undefined" || val === "") val = "&#160;";
33685         cellText.innerHTML = val;
33686         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33687         this.syncRowHeights(rowIndex, rowIndex);
33688     },
33689
33690     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33691         var maxWidth = 0;
33692         if(this.grid.autoSizeHeaders){
33693             var h = this.getHeaderCellMeasure(colIndex);
33694             maxWidth = Math.max(maxWidth, h.scrollWidth);
33695         }
33696         var tb, index;
33697         if(this.cm.isLocked(colIndex)){
33698             tb = this.getLockedTable();
33699             index = colIndex;
33700         }else{
33701             tb = this.getBodyTable();
33702             index = colIndex - this.cm.getLockedCount();
33703         }
33704         if(tb && tb.rows){
33705             var rows = tb.rows;
33706             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33707             for(var i = 0; i < stopIndex; i++){
33708                 var cell = rows[i].childNodes[index].firstChild;
33709                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33710             }
33711         }
33712         return maxWidth + /*margin for error in IE*/ 5;
33713     },
33714     /**
33715      * Autofit a column to its content.
33716      * @param {Number} colIndex
33717      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33718      */
33719      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33720          if(this.cm.isHidden(colIndex)){
33721              return; // can't calc a hidden column
33722          }
33723         if(forceMinSize){
33724             var cid = this.cm.getColumnId(colIndex);
33725             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33726            if(this.grid.autoSizeHeaders){
33727                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33728            }
33729         }
33730         var newWidth = this.calcColumnWidth(colIndex);
33731         this.cm.setColumnWidth(colIndex,
33732             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33733         if(!suppressEvent){
33734             this.grid.fireEvent("columnresize", colIndex, newWidth);
33735         }
33736     },
33737
33738     /**
33739      * Autofits all columns to their content and then expands to fit any extra space in the grid
33740      */
33741      autoSizeColumns : function(){
33742         var cm = this.grid.colModel;
33743         var colCount = cm.getColumnCount();
33744         for(var i = 0; i < colCount; i++){
33745             this.autoSizeColumn(i, true, true);
33746         }
33747         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33748             this.fitColumns();
33749         }else{
33750             this.updateColumns();
33751             this.layout();
33752         }
33753     },
33754
33755     /**
33756      * Autofits all columns to the grid's width proportionate with their current size
33757      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33758      */
33759     fitColumns : function(reserveScrollSpace){
33760         var cm = this.grid.colModel;
33761         var colCount = cm.getColumnCount();
33762         var cols = [];
33763         var width = 0;
33764         var i, w;
33765         for (i = 0; i < colCount; i++){
33766             if(!cm.isHidden(i) && !cm.isFixed(i)){
33767                 w = cm.getColumnWidth(i);
33768                 cols.push(i);
33769                 cols.push(w);
33770                 width += w;
33771             }
33772         }
33773         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33774         if(reserveScrollSpace){
33775             avail -= 17;
33776         }
33777         var frac = (avail - cm.getTotalWidth())/width;
33778         while (cols.length){
33779             w = cols.pop();
33780             i = cols.pop();
33781             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33782         }
33783         this.updateColumns();
33784         this.layout();
33785     },
33786
33787     onRowSelect : function(rowIndex){
33788         var row = this.getRowComposite(rowIndex);
33789         row.addClass("x-grid-row-selected");
33790     },
33791
33792     onRowDeselect : function(rowIndex){
33793         var row = this.getRowComposite(rowIndex);
33794         row.removeClass("x-grid-row-selected");
33795     },
33796
33797     onCellSelect : function(row, col){
33798         var cell = this.getCell(row, col);
33799         if(cell){
33800             Roo.fly(cell).addClass("x-grid-cell-selected");
33801         }
33802     },
33803
33804     onCellDeselect : function(row, col){
33805         var cell = this.getCell(row, col);
33806         if(cell){
33807             Roo.fly(cell).removeClass("x-grid-cell-selected");
33808         }
33809     },
33810
33811     updateHeaderSortState : function(){
33812         var state = this.ds.getSortState();
33813         if(!state){
33814             return;
33815         }
33816         this.sortState = state;
33817         var sortColumn = this.cm.findColumnIndex(state.field);
33818         if(sortColumn != -1){
33819             var sortDir = state.direction;
33820             var sc = this.sortClasses;
33821             var hds = this.el.select(this.headerSelector).removeClass(sc);
33822             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33823         }
33824     },
33825
33826     handleHeaderClick : function(g, index){
33827         if(this.headersDisabled){
33828             return;
33829         }
33830         var dm = g.dataSource, cm = g.colModel;
33831             if(!cm.isSortable(index)){
33832             return;
33833         }
33834             g.stopEditing();
33835         dm.sort(cm.getDataIndex(index));
33836     },
33837
33838
33839     destroy : function(){
33840         if(this.colMenu){
33841             this.colMenu.removeAll();
33842             Roo.menu.MenuMgr.unregister(this.colMenu);
33843             this.colMenu.getEl().remove();
33844             delete this.colMenu;
33845         }
33846         if(this.hmenu){
33847             this.hmenu.removeAll();
33848             Roo.menu.MenuMgr.unregister(this.hmenu);
33849             this.hmenu.getEl().remove();
33850             delete this.hmenu;
33851         }
33852         if(this.grid.enableColumnMove){
33853             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33854             if(dds){
33855                 for(var dd in dds){
33856                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33857                         var elid = dds[dd].dragElId;
33858                         dds[dd].unreg();
33859                         Roo.get(elid).remove();
33860                     } else if(dds[dd].config.isTarget){
33861                         dds[dd].proxyTop.remove();
33862                         dds[dd].proxyBottom.remove();
33863                         dds[dd].unreg();
33864                     }
33865                     if(Roo.dd.DDM.locationCache[dd]){
33866                         delete Roo.dd.DDM.locationCache[dd];
33867                     }
33868                 }
33869                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33870             }
33871         }
33872         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33873         this.bind(null, null);
33874         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33875     },
33876
33877     handleLockChange : function(){
33878         this.refresh(true);
33879     },
33880
33881     onDenyColumnLock : function(){
33882
33883     },
33884
33885     onDenyColumnHide : function(){
33886
33887     },
33888
33889     handleHdMenuClick : function(item){
33890         var index = this.hdCtxIndex;
33891         var cm = this.cm, ds = this.ds;
33892         switch(item.id){
33893             case "asc":
33894                 ds.sort(cm.getDataIndex(index), "ASC");
33895                 break;
33896             case "desc":
33897                 ds.sort(cm.getDataIndex(index), "DESC");
33898                 break;
33899             case "lock":
33900                 var lc = cm.getLockedCount();
33901                 if(cm.getColumnCount(true) <= lc+1){
33902                     this.onDenyColumnLock();
33903                     return;
33904                 }
33905                 if(lc != index){
33906                     cm.setLocked(index, true, true);
33907                     cm.moveColumn(index, lc);
33908                     this.grid.fireEvent("columnmove", index, lc);
33909                 }else{
33910                     cm.setLocked(index, true);
33911                 }
33912             break;
33913             case "unlock":
33914                 var lc = cm.getLockedCount();
33915                 if((lc-1) != index){
33916                     cm.setLocked(index, false, true);
33917                     cm.moveColumn(index, lc-1);
33918                     this.grid.fireEvent("columnmove", index, lc-1);
33919                 }else{
33920                     cm.setLocked(index, false);
33921                 }
33922             break;
33923             default:
33924                 index = cm.getIndexById(item.id.substr(4));
33925                 if(index != -1){
33926                     if(item.checked && cm.getColumnCount(true) <= 1){
33927                         this.onDenyColumnHide();
33928                         return false;
33929                     }
33930                     cm.setHidden(index, item.checked);
33931                 }
33932         }
33933         return true;
33934     },
33935
33936     beforeColMenuShow : function(){
33937         var cm = this.cm,  colCount = cm.getColumnCount();
33938         this.colMenu.removeAll();
33939         for(var i = 0; i < colCount; i++){
33940             this.colMenu.add(new Roo.menu.CheckItem({
33941                 id: "col-"+cm.getColumnId(i),
33942                 text: cm.getColumnHeader(i),
33943                 checked: !cm.isHidden(i),
33944                 hideOnClick:false
33945             }));
33946         }
33947     },
33948
33949     handleHdCtx : function(g, index, e){
33950         e.stopEvent();
33951         var hd = this.getHeaderCell(index);
33952         this.hdCtxIndex = index;
33953         var ms = this.hmenu.items, cm = this.cm;
33954         ms.get("asc").setDisabled(!cm.isSortable(index));
33955         ms.get("desc").setDisabled(!cm.isSortable(index));
33956         if(this.grid.enableColLock !== false){
33957             ms.get("lock").setDisabled(cm.isLocked(index));
33958             ms.get("unlock").setDisabled(!cm.isLocked(index));
33959         }
33960         this.hmenu.show(hd, "tl-bl");
33961     },
33962
33963     handleHdOver : function(e){
33964         var hd = this.findHeaderCell(e.getTarget());
33965         if(hd && !this.headersDisabled){
33966             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33967                this.fly(hd).addClass("x-grid-hd-over");
33968             }
33969         }
33970     },
33971
33972     handleHdOut : function(e){
33973         var hd = this.findHeaderCell(e.getTarget());
33974         if(hd){
33975             this.fly(hd).removeClass("x-grid-hd-over");
33976         }
33977     },
33978
33979     handleSplitDblClick : function(e, t){
33980         var i = this.getCellIndex(t);
33981         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33982             this.autoSizeColumn(i, true);
33983             this.layout();
33984         }
33985     },
33986
33987     render : function(){
33988
33989         var cm = this.cm;
33990         var colCount = cm.getColumnCount();
33991
33992         if(this.grid.monitorWindowResize === true){
33993             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33994         }
33995         var header = this.renderHeaders();
33996         var body = this.templates.body.apply({rows:""});
33997         var html = this.templates.master.apply({
33998             lockedBody: body,
33999             body: body,
34000             lockedHeader: header[0],
34001             header: header[1]
34002         });
34003
34004         //this.updateColumns();
34005
34006         this.grid.getGridEl().dom.innerHTML = html;
34007
34008         this.initElements();
34009         
34010         // a kludge to fix the random scolling effect in webkit
34011         this.el.on("scroll", function() {
34012             this.el.dom.scrollTop=0; // hopefully not recursive..
34013         },this);
34014
34015         this.scroller.on("scroll", this.handleScroll, this);
34016         this.lockedBody.on("mousewheel", this.handleWheel, this);
34017         this.mainBody.on("mousewheel", this.handleWheel, this);
34018
34019         this.mainHd.on("mouseover", this.handleHdOver, this);
34020         this.mainHd.on("mouseout", this.handleHdOut, this);
34021         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34022                 {delegate: "."+this.splitClass});
34023
34024         this.lockedHd.on("mouseover", this.handleHdOver, this);
34025         this.lockedHd.on("mouseout", this.handleHdOut, this);
34026         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34027                 {delegate: "."+this.splitClass});
34028
34029         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34030             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34031         }
34032
34033         this.updateSplitters();
34034
34035         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34036             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34037             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34038         }
34039
34040         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34041             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34042             this.hmenu.add(
34043                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34044                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34045             );
34046             if(this.grid.enableColLock !== false){
34047                 this.hmenu.add('-',
34048                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34049                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34050                 );
34051             }
34052             if(this.grid.enableColumnHide !== false){
34053
34054                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34055                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34056                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34057
34058                 this.hmenu.add('-',
34059                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34060                 );
34061             }
34062             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34063
34064             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34065         }
34066
34067         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34068             this.dd = new Roo.grid.GridDragZone(this.grid, {
34069                 ddGroup : this.grid.ddGroup || 'GridDD'
34070             });
34071         }
34072
34073         /*
34074         for(var i = 0; i < colCount; i++){
34075             if(cm.isHidden(i)){
34076                 this.hideColumn(i);
34077             }
34078             if(cm.config[i].align){
34079                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34080                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34081             }
34082         }*/
34083         
34084         this.updateHeaderSortState();
34085
34086         this.beforeInitialResize();
34087         this.layout(true);
34088
34089         // two part rendering gives faster view to the user
34090         this.renderPhase2.defer(1, this);
34091     },
34092
34093     renderPhase2 : function(){
34094         // render the rows now
34095         this.refresh();
34096         if(this.grid.autoSizeColumns){
34097             this.autoSizeColumns();
34098         }
34099     },
34100
34101     beforeInitialResize : function(){
34102
34103     },
34104
34105     onColumnSplitterMoved : function(i, w){
34106         this.userResized = true;
34107         var cm = this.grid.colModel;
34108         cm.setColumnWidth(i, w, true);
34109         var cid = cm.getColumnId(i);
34110         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34111         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34112         this.updateSplitters();
34113         this.layout();
34114         this.grid.fireEvent("columnresize", i, w);
34115     },
34116
34117     syncRowHeights : function(startIndex, endIndex){
34118         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34119             startIndex = startIndex || 0;
34120             var mrows = this.getBodyTable().rows;
34121             var lrows = this.getLockedTable().rows;
34122             var len = mrows.length-1;
34123             endIndex = Math.min(endIndex || len, len);
34124             for(var i = startIndex; i <= endIndex; i++){
34125                 var m = mrows[i], l = lrows[i];
34126                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34127                 m.style.height = l.style.height = h + "px";
34128             }
34129         }
34130     },
34131
34132     layout : function(initialRender, is2ndPass){
34133         var g = this.grid;
34134         var auto = g.autoHeight;
34135         var scrollOffset = 16;
34136         var c = g.getGridEl(), cm = this.cm,
34137                 expandCol = g.autoExpandColumn,
34138                 gv = this;
34139         //c.beginMeasure();
34140
34141         if(!c.dom.offsetWidth){ // display:none?
34142             if(initialRender){
34143                 this.lockedWrap.show();
34144                 this.mainWrap.show();
34145             }
34146             return;
34147         }
34148
34149         var hasLock = this.cm.isLocked(0);
34150
34151         var tbh = this.headerPanel.getHeight();
34152         var bbh = this.footerPanel.getHeight();
34153
34154         if(auto){
34155             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34156             var newHeight = ch + c.getBorderWidth("tb");
34157             if(g.maxHeight){
34158                 newHeight = Math.min(g.maxHeight, newHeight);
34159             }
34160             c.setHeight(newHeight);
34161         }
34162
34163         if(g.autoWidth){
34164             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34165         }
34166
34167         var s = this.scroller;
34168
34169         var csize = c.getSize(true);
34170
34171         this.el.setSize(csize.width, csize.height);
34172
34173         this.headerPanel.setWidth(csize.width);
34174         this.footerPanel.setWidth(csize.width);
34175
34176         var hdHeight = this.mainHd.getHeight();
34177         var vw = csize.width;
34178         var vh = csize.height - (tbh + bbh);
34179
34180         s.setSize(vw, vh);
34181
34182         var bt = this.getBodyTable();
34183         var ltWidth = hasLock ?
34184                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34185
34186         var scrollHeight = bt.offsetHeight;
34187         var scrollWidth = ltWidth + bt.offsetWidth;
34188         var vscroll = false, hscroll = false;
34189
34190         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34191
34192         var lw = this.lockedWrap, mw = this.mainWrap;
34193         var lb = this.lockedBody, mb = this.mainBody;
34194
34195         setTimeout(function(){
34196             var t = s.dom.offsetTop;
34197             var w = s.dom.clientWidth,
34198                 h = s.dom.clientHeight;
34199
34200             lw.setTop(t);
34201             lw.setSize(ltWidth, h);
34202
34203             mw.setLeftTop(ltWidth, t);
34204             mw.setSize(w-ltWidth, h);
34205
34206             lb.setHeight(h-hdHeight);
34207             mb.setHeight(h-hdHeight);
34208
34209             if(is2ndPass !== true && !gv.userResized && expandCol){
34210                 // high speed resize without full column calculation
34211                 
34212                 var ci = cm.getIndexById(expandCol);
34213                 if (ci < 0) {
34214                     ci = cm.findColumnIndex(expandCol);
34215                 }
34216                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34217                 var expandId = cm.getColumnId(ci);
34218                 var  tw = cm.getTotalWidth(false);
34219                 var currentWidth = cm.getColumnWidth(ci);
34220                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34221                 if(currentWidth != cw){
34222                     cm.setColumnWidth(ci, cw, true);
34223                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34224                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34225                     gv.updateSplitters();
34226                     gv.layout(false, true);
34227                 }
34228             }
34229
34230             if(initialRender){
34231                 lw.show();
34232                 mw.show();
34233             }
34234             //c.endMeasure();
34235         }, 10);
34236     },
34237
34238     onWindowResize : function(){
34239         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34240             return;
34241         }
34242         this.layout();
34243     },
34244
34245     appendFooter : function(parentEl){
34246         return null;
34247     },
34248
34249     sortAscText : "Sort Ascending",
34250     sortDescText : "Sort Descending",
34251     lockText : "Lock Column",
34252     unlockText : "Unlock Column",
34253     columnsText : "Columns"
34254 });
34255
34256
34257 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34258     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34259     this.proxy.el.addClass('x-grid3-col-dd');
34260 };
34261
34262 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34263     handleMouseDown : function(e){
34264
34265     },
34266
34267     callHandleMouseDown : function(e){
34268         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34269     }
34270 });
34271 /*
34272  * Based on:
34273  * Ext JS Library 1.1.1
34274  * Copyright(c) 2006-2007, Ext JS, LLC.
34275  *
34276  * Originally Released Under LGPL - original licence link has changed is not relivant.
34277  *
34278  * Fork - LGPL
34279  * <script type="text/javascript">
34280  */
34281  
34282 // private
34283 // This is a support class used internally by the Grid components
34284 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34285     this.grid = grid;
34286     this.view = grid.getView();
34287     this.proxy = this.view.resizeProxy;
34288     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34289         "gridSplitters" + this.grid.getGridEl().id, {
34290         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34291     });
34292     this.setHandleElId(Roo.id(hd));
34293     this.setOuterHandleElId(Roo.id(hd2));
34294     this.scroll = false;
34295 };
34296 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34297     fly: Roo.Element.fly,
34298
34299     b4StartDrag : function(x, y){
34300         this.view.headersDisabled = true;
34301         this.proxy.setHeight(this.view.mainWrap.getHeight());
34302         var w = this.cm.getColumnWidth(this.cellIndex);
34303         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34304         this.resetConstraints();
34305         this.setXConstraint(minw, 1000);
34306         this.setYConstraint(0, 0);
34307         this.minX = x - minw;
34308         this.maxX = x + 1000;
34309         this.startPos = x;
34310         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34311     },
34312
34313
34314     handleMouseDown : function(e){
34315         ev = Roo.EventObject.setEvent(e);
34316         var t = this.fly(ev.getTarget());
34317         if(t.hasClass("x-grid-split")){
34318             this.cellIndex = this.view.getCellIndex(t.dom);
34319             this.split = t.dom;
34320             this.cm = this.grid.colModel;
34321             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34322                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34323             }
34324         }
34325     },
34326
34327     endDrag : function(e){
34328         this.view.headersDisabled = false;
34329         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34330         var diff = endX - this.startPos;
34331         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34332     },
34333
34334     autoOffset : function(){
34335         this.setDelta(0,0);
34336     }
34337 });/*
34338  * Based on:
34339  * Ext JS Library 1.1.1
34340  * Copyright(c) 2006-2007, Ext JS, LLC.
34341  *
34342  * Originally Released Under LGPL - original licence link has changed is not relivant.
34343  *
34344  * Fork - LGPL
34345  * <script type="text/javascript">
34346  */
34347  
34348 // private
34349 // This is a support class used internally by the Grid components
34350 Roo.grid.GridDragZone = function(grid, config){
34351     this.view = grid.getView();
34352     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34353     if(this.view.lockedBody){
34354         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34355         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34356     }
34357     this.scroll = false;
34358     this.grid = grid;
34359     this.ddel = document.createElement('div');
34360     this.ddel.className = 'x-grid-dd-wrap';
34361 };
34362
34363 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34364     ddGroup : "GridDD",
34365
34366     getDragData : function(e){
34367         var t = Roo.lib.Event.getTarget(e);
34368         var rowIndex = this.view.findRowIndex(t);
34369         if(rowIndex !== false){
34370             var sm = this.grid.selModel;
34371             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34372               //  sm.mouseDown(e, t);
34373             //}
34374             if (e.hasModifier()){
34375                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34376             }
34377             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34378         }
34379         return false;
34380     },
34381
34382     onInitDrag : function(e){
34383         var data = this.dragData;
34384         this.ddel.innerHTML = this.grid.getDragDropText();
34385         this.proxy.update(this.ddel);
34386         // fire start drag?
34387     },
34388
34389     afterRepair : function(){
34390         this.dragging = false;
34391     },
34392
34393     getRepairXY : function(e, data){
34394         return false;
34395     },
34396
34397     onEndDrag : function(data, e){
34398         // fire end drag?
34399     },
34400
34401     onValidDrop : function(dd, e, id){
34402         // fire drag drop?
34403         this.hideProxy();
34404     },
34405
34406     beforeInvalidDrop : function(e, id){
34407
34408     }
34409 });/*
34410  * Based on:
34411  * Ext JS Library 1.1.1
34412  * Copyright(c) 2006-2007, Ext JS, LLC.
34413  *
34414  * Originally Released Under LGPL - original licence link has changed is not relivant.
34415  *
34416  * Fork - LGPL
34417  * <script type="text/javascript">
34418  */
34419  
34420
34421 /**
34422  * @class Roo.grid.ColumnModel
34423  * @extends Roo.util.Observable
34424  * This is the default implementation of a ColumnModel used by the Grid. It defines
34425  * the columns in the grid.
34426  * <br>Usage:<br>
34427  <pre><code>
34428  var colModel = new Roo.grid.ColumnModel([
34429         {header: "Ticker", width: 60, sortable: true, locked: true},
34430         {header: "Company Name", width: 150, sortable: true},
34431         {header: "Market Cap.", width: 100, sortable: true},
34432         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34433         {header: "Employees", width: 100, sortable: true, resizable: false}
34434  ]);
34435  </code></pre>
34436  * <p>
34437  
34438  * The config options listed for this class are options which may appear in each
34439  * individual column definition.
34440  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34441  * @constructor
34442  * @param {Object} config An Array of column config objects. See this class's
34443  * config objects for details.
34444 */
34445 Roo.grid.ColumnModel = function(config){
34446         /**
34447      * The config passed into the constructor
34448      */
34449     this.config = config;
34450     this.lookup = {};
34451
34452     // if no id, create one
34453     // if the column does not have a dataIndex mapping,
34454     // map it to the order it is in the config
34455     for(var i = 0, len = config.length; i < len; i++){
34456         var c = config[i];
34457         if(typeof c.dataIndex == "undefined"){
34458             c.dataIndex = i;
34459         }
34460         if(typeof c.renderer == "string"){
34461             c.renderer = Roo.util.Format[c.renderer];
34462         }
34463         if(typeof c.id == "undefined"){
34464             c.id = Roo.id();
34465         }
34466         if(c.editor && c.editor.xtype){
34467             c.editor  = Roo.factory(c.editor, Roo.grid);
34468         }
34469         if(c.editor && c.editor.isFormField){
34470             c.editor = new Roo.grid.GridEditor(c.editor);
34471         }
34472         this.lookup[c.id] = c;
34473     }
34474
34475     /**
34476      * The width of columns which have no width specified (defaults to 100)
34477      * @type Number
34478      */
34479     this.defaultWidth = 100;
34480
34481     /**
34482      * Default sortable of columns which have no sortable specified (defaults to false)
34483      * @type Boolean
34484      */
34485     this.defaultSortable = false;
34486
34487     this.addEvents({
34488         /**
34489              * @event widthchange
34490              * Fires when the width of a column changes.
34491              * @param {ColumnModel} this
34492              * @param {Number} columnIndex The column index
34493              * @param {Number} newWidth The new width
34494              */
34495             "widthchange": true,
34496         /**
34497              * @event headerchange
34498              * Fires when the text of a header changes.
34499              * @param {ColumnModel} this
34500              * @param {Number} columnIndex The column index
34501              * @param {Number} newText The new header text
34502              */
34503             "headerchange": true,
34504         /**
34505              * @event hiddenchange
34506              * Fires when a column is hidden or "unhidden".
34507              * @param {ColumnModel} this
34508              * @param {Number} columnIndex The column index
34509              * @param {Boolean} hidden true if hidden, false otherwise
34510              */
34511             "hiddenchange": true,
34512             /**
34513          * @event columnmoved
34514          * Fires when a column is moved.
34515          * @param {ColumnModel} this
34516          * @param {Number} oldIndex
34517          * @param {Number} newIndex
34518          */
34519         "columnmoved" : true,
34520         /**
34521          * @event columlockchange
34522          * Fires when a column's locked state is changed
34523          * @param {ColumnModel} this
34524          * @param {Number} colIndex
34525          * @param {Boolean} locked true if locked
34526          */
34527         "columnlockchange" : true
34528     });
34529     Roo.grid.ColumnModel.superclass.constructor.call(this);
34530 };
34531 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34532     /**
34533      * @cfg {String} header The header text to display in the Grid view.
34534      */
34535     /**
34536      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34537      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34538      * specified, the column's index is used as an index into the Record's data Array.
34539      */
34540     /**
34541      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34542      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34543      */
34544     /**
34545      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34546      * Defaults to the value of the {@link #defaultSortable} property.
34547      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34548      */
34549     /**
34550      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34551      */
34552     /**
34553      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34554      */
34555     /**
34556      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34557      */
34558     /**
34559      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34560      */
34561     /**
34562      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34563      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34564      * default renderer uses the raw data value.
34565      */
34566        /**
34567      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34568      */
34569     /**
34570      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34571      */
34572
34573     /**
34574      * Returns the id of the column at the specified index.
34575      * @param {Number} index The column index
34576      * @return {String} the id
34577      */
34578     getColumnId : function(index){
34579         return this.config[index].id;
34580     },
34581
34582     /**
34583      * Returns the column for a specified id.
34584      * @param {String} id The column id
34585      * @return {Object} the column
34586      */
34587     getColumnById : function(id){
34588         return this.lookup[id];
34589     },
34590
34591     
34592     /**
34593      * Returns the column for a specified dataIndex.
34594      * @param {String} dataIndex The column dataIndex
34595      * @return {Object|Boolean} the column or false if not found
34596      */
34597     getColumnByDataIndex: function(dataIndex){
34598         var index = this.findColumnIndex(dataIndex);
34599         return index > -1 ? this.config[index] : false;
34600     },
34601     
34602     /**
34603      * Returns the index for a specified column id.
34604      * @param {String} id The column id
34605      * @return {Number} the index, or -1 if not found
34606      */
34607     getIndexById : function(id){
34608         for(var i = 0, len = this.config.length; i < len; i++){
34609             if(this.config[i].id == id){
34610                 return i;
34611             }
34612         }
34613         return -1;
34614     },
34615     
34616     /**
34617      * Returns the index for a specified column dataIndex.
34618      * @param {String} dataIndex The column dataIndex
34619      * @return {Number} the index, or -1 if not found
34620      */
34621     
34622     findColumnIndex : function(dataIndex){
34623         for(var i = 0, len = this.config.length; i < len; i++){
34624             if(this.config[i].dataIndex == dataIndex){
34625                 return i;
34626             }
34627         }
34628         return -1;
34629     },
34630     
34631     
34632     moveColumn : function(oldIndex, newIndex){
34633         var c = this.config[oldIndex];
34634         this.config.splice(oldIndex, 1);
34635         this.config.splice(newIndex, 0, c);
34636         this.dataMap = null;
34637         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34638     },
34639
34640     isLocked : function(colIndex){
34641         return this.config[colIndex].locked === true;
34642     },
34643
34644     setLocked : function(colIndex, value, suppressEvent){
34645         if(this.isLocked(colIndex) == value){
34646             return;
34647         }
34648         this.config[colIndex].locked = value;
34649         if(!suppressEvent){
34650             this.fireEvent("columnlockchange", this, colIndex, value);
34651         }
34652     },
34653
34654     getTotalLockedWidth : function(){
34655         var totalWidth = 0;
34656         for(var i = 0; i < this.config.length; i++){
34657             if(this.isLocked(i) && !this.isHidden(i)){
34658                 this.totalWidth += this.getColumnWidth(i);
34659             }
34660         }
34661         return totalWidth;
34662     },
34663
34664     getLockedCount : function(){
34665         for(var i = 0, len = this.config.length; i < len; i++){
34666             if(!this.isLocked(i)){
34667                 return i;
34668             }
34669         }
34670     },
34671
34672     /**
34673      * Returns the number of columns.
34674      * @return {Number}
34675      */
34676     getColumnCount : function(visibleOnly){
34677         if(visibleOnly === true){
34678             var c = 0;
34679             for(var i = 0, len = this.config.length; i < len; i++){
34680                 if(!this.isHidden(i)){
34681                     c++;
34682                 }
34683             }
34684             return c;
34685         }
34686         return this.config.length;
34687     },
34688
34689     /**
34690      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34691      * @param {Function} fn
34692      * @param {Object} scope (optional)
34693      * @return {Array} result
34694      */
34695     getColumnsBy : function(fn, scope){
34696         var r = [];
34697         for(var i = 0, len = this.config.length; i < len; i++){
34698             var c = this.config[i];
34699             if(fn.call(scope||this, c, i) === true){
34700                 r[r.length] = c;
34701             }
34702         }
34703         return r;
34704     },
34705
34706     /**
34707      * Returns true if the specified column is sortable.
34708      * @param {Number} col The column index
34709      * @return {Boolean}
34710      */
34711     isSortable : function(col){
34712         if(typeof this.config[col].sortable == "undefined"){
34713             return this.defaultSortable;
34714         }
34715         return this.config[col].sortable;
34716     },
34717
34718     /**
34719      * Returns the rendering (formatting) function defined for the column.
34720      * @param {Number} col The column index.
34721      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34722      */
34723     getRenderer : function(col){
34724         if(!this.config[col].renderer){
34725             return Roo.grid.ColumnModel.defaultRenderer;
34726         }
34727         return this.config[col].renderer;
34728     },
34729
34730     /**
34731      * Sets the rendering (formatting) function for a column.
34732      * @param {Number} col The column index
34733      * @param {Function} fn The function to use to process the cell's raw data
34734      * to return HTML markup for the grid view. The render function is called with
34735      * the following parameters:<ul>
34736      * <li>Data value.</li>
34737      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34738      * <li>css A CSS style string to apply to the table cell.</li>
34739      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34740      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34741      * <li>Row index</li>
34742      * <li>Column index</li>
34743      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34744      */
34745     setRenderer : function(col, fn){
34746         this.config[col].renderer = fn;
34747     },
34748
34749     /**
34750      * Returns the width for the specified column.
34751      * @param {Number} col The column index
34752      * @return {Number}
34753      */
34754     getColumnWidth : function(col){
34755         return this.config[col].width || this.defaultWidth;
34756     },
34757
34758     /**
34759      * Sets the width for a column.
34760      * @param {Number} col The column index
34761      * @param {Number} width The new width
34762      */
34763     setColumnWidth : function(col, width, suppressEvent){
34764         this.config[col].width = width;
34765         this.totalWidth = null;
34766         if(!suppressEvent){
34767              this.fireEvent("widthchange", this, col, width);
34768         }
34769     },
34770
34771     /**
34772      * Returns the total width of all columns.
34773      * @param {Boolean} includeHidden True to include hidden column widths
34774      * @return {Number}
34775      */
34776     getTotalWidth : function(includeHidden){
34777         if(!this.totalWidth){
34778             this.totalWidth = 0;
34779             for(var i = 0, len = this.config.length; i < len; i++){
34780                 if(includeHidden || !this.isHidden(i)){
34781                     this.totalWidth += this.getColumnWidth(i);
34782                 }
34783             }
34784         }
34785         return this.totalWidth;
34786     },
34787
34788     /**
34789      * Returns the header for the specified column.
34790      * @param {Number} col The column index
34791      * @return {String}
34792      */
34793     getColumnHeader : function(col){
34794         return this.config[col].header;
34795     },
34796
34797     /**
34798      * Sets the header for a column.
34799      * @param {Number} col The column index
34800      * @param {String} header The new header
34801      */
34802     setColumnHeader : function(col, header){
34803         this.config[col].header = header;
34804         this.fireEvent("headerchange", this, col, header);
34805     },
34806
34807     /**
34808      * Returns the tooltip for the specified column.
34809      * @param {Number} col The column index
34810      * @return {String}
34811      */
34812     getColumnTooltip : function(col){
34813             return this.config[col].tooltip;
34814     },
34815     /**
34816      * Sets the tooltip for a column.
34817      * @param {Number} col The column index
34818      * @param {String} tooltip The new tooltip
34819      */
34820     setColumnTooltip : function(col, tooltip){
34821             this.config[col].tooltip = tooltip;
34822     },
34823
34824     /**
34825      * Returns the dataIndex for the specified column.
34826      * @param {Number} col The column index
34827      * @return {Number}
34828      */
34829     getDataIndex : function(col){
34830         return this.config[col].dataIndex;
34831     },
34832
34833     /**
34834      * Sets the dataIndex for a column.
34835      * @param {Number} col The column index
34836      * @param {Number} dataIndex The new dataIndex
34837      */
34838     setDataIndex : function(col, dataIndex){
34839         this.config[col].dataIndex = dataIndex;
34840     },
34841
34842     
34843     
34844     /**
34845      * Returns true if the cell is editable.
34846      * @param {Number} colIndex The column index
34847      * @param {Number} rowIndex The row index
34848      * @return {Boolean}
34849      */
34850     isCellEditable : function(colIndex, rowIndex){
34851         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34852     },
34853
34854     /**
34855      * Returns the editor defined for the cell/column.
34856      * return false or null to disable editing.
34857      * @param {Number} colIndex The column index
34858      * @param {Number} rowIndex The row index
34859      * @return {Object}
34860      */
34861     getCellEditor : function(colIndex, rowIndex){
34862         return this.config[colIndex].editor;
34863     },
34864
34865     /**
34866      * Sets if a column is editable.
34867      * @param {Number} col The column index
34868      * @param {Boolean} editable True if the column is editable
34869      */
34870     setEditable : function(col, editable){
34871         this.config[col].editable = editable;
34872     },
34873
34874
34875     /**
34876      * Returns true if the column is hidden.
34877      * @param {Number} colIndex The column index
34878      * @return {Boolean}
34879      */
34880     isHidden : function(colIndex){
34881         return this.config[colIndex].hidden;
34882     },
34883
34884
34885     /**
34886      * Returns true if the column width cannot be changed
34887      */
34888     isFixed : function(colIndex){
34889         return this.config[colIndex].fixed;
34890     },
34891
34892     /**
34893      * Returns true if the column can be resized
34894      * @return {Boolean}
34895      */
34896     isResizable : function(colIndex){
34897         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34898     },
34899     /**
34900      * Sets if a column is hidden.
34901      * @param {Number} colIndex The column index
34902      * @param {Boolean} hidden True if the column is hidden
34903      */
34904     setHidden : function(colIndex, hidden){
34905         this.config[colIndex].hidden = hidden;
34906         this.totalWidth = null;
34907         this.fireEvent("hiddenchange", this, colIndex, hidden);
34908     },
34909
34910     /**
34911      * Sets the editor for a column.
34912      * @param {Number} col The column index
34913      * @param {Object} editor The editor object
34914      */
34915     setEditor : function(col, editor){
34916         this.config[col].editor = editor;
34917     }
34918 });
34919
34920 Roo.grid.ColumnModel.defaultRenderer = function(value){
34921         if(typeof value == "string" && value.length < 1){
34922             return "&#160;";
34923         }
34924         return value;
34925 };
34926
34927 // Alias for backwards compatibility
34928 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34929 /*
34930  * Based on:
34931  * Ext JS Library 1.1.1
34932  * Copyright(c) 2006-2007, Ext JS, LLC.
34933  *
34934  * Originally Released Under LGPL - original licence link has changed is not relivant.
34935  *
34936  * Fork - LGPL
34937  * <script type="text/javascript">
34938  */
34939
34940 /**
34941  * @class Roo.grid.AbstractSelectionModel
34942  * @extends Roo.util.Observable
34943  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34944  * implemented by descendant classes.  This class should not be directly instantiated.
34945  * @constructor
34946  */
34947 Roo.grid.AbstractSelectionModel = function(){
34948     this.locked = false;
34949     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34950 };
34951
34952 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34953     /** @ignore Called by the grid automatically. Do not call directly. */
34954     init : function(grid){
34955         this.grid = grid;
34956         this.initEvents();
34957     },
34958
34959     /**
34960      * Locks the selections.
34961      */
34962     lock : function(){
34963         this.locked = true;
34964     },
34965
34966     /**
34967      * Unlocks the selections.
34968      */
34969     unlock : function(){
34970         this.locked = false;
34971     },
34972
34973     /**
34974      * Returns true if the selections are locked.
34975      * @return {Boolean}
34976      */
34977     isLocked : function(){
34978         return this.locked;
34979     }
34980 });/*
34981  * Based on:
34982  * Ext JS Library 1.1.1
34983  * Copyright(c) 2006-2007, Ext JS, LLC.
34984  *
34985  * Originally Released Under LGPL - original licence link has changed is not relivant.
34986  *
34987  * Fork - LGPL
34988  * <script type="text/javascript">
34989  */
34990 /**
34991  * @extends Roo.grid.AbstractSelectionModel
34992  * @class Roo.grid.RowSelectionModel
34993  * The default SelectionModel used by {@link Roo.grid.Grid}.
34994  * It supports multiple selections and keyboard selection/navigation. 
34995  * @constructor
34996  * @param {Object} config
34997  */
34998 Roo.grid.RowSelectionModel = function(config){
34999     Roo.apply(this, config);
35000     this.selections = new Roo.util.MixedCollection(false, function(o){
35001         return o.id;
35002     });
35003
35004     this.last = false;
35005     this.lastActive = false;
35006
35007     this.addEvents({
35008         /**
35009              * @event selectionchange
35010              * Fires when the selection changes
35011              * @param {SelectionModel} this
35012              */
35013             "selectionchange" : true,
35014         /**
35015              * @event afterselectionchange
35016              * Fires after the selection changes (eg. by key press or clicking)
35017              * @param {SelectionModel} this
35018              */
35019             "afterselectionchange" : true,
35020         /**
35021              * @event beforerowselect
35022              * Fires when a row is selected being selected, return false to cancel.
35023              * @param {SelectionModel} this
35024              * @param {Number} rowIndex The selected index
35025              * @param {Boolean} keepExisting False if other selections will be cleared
35026              */
35027             "beforerowselect" : true,
35028         /**
35029              * @event rowselect
35030              * Fires when a row is selected.
35031              * @param {SelectionModel} this
35032              * @param {Number} rowIndex The selected index
35033              * @param {Roo.data.Record} r The record
35034              */
35035             "rowselect" : true,
35036         /**
35037              * @event rowdeselect
35038              * Fires when a row is deselected.
35039              * @param {SelectionModel} this
35040              * @param {Number} rowIndex The selected index
35041              */
35042         "rowdeselect" : true
35043     });
35044     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35045     this.locked = false;
35046 };
35047
35048 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35049     /**
35050      * @cfg {Boolean} singleSelect
35051      * True to allow selection of only one row at a time (defaults to false)
35052      */
35053     singleSelect : false,
35054
35055     // private
35056     initEvents : function(){
35057
35058         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35059             this.grid.on("mousedown", this.handleMouseDown, this);
35060         }else{ // allow click to work like normal
35061             this.grid.on("rowclick", this.handleDragableRowClick, this);
35062         }
35063
35064         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35065             "up" : function(e){
35066                 if(!e.shiftKey){
35067                     this.selectPrevious(e.shiftKey);
35068                 }else if(this.last !== false && this.lastActive !== false){
35069                     var last = this.last;
35070                     this.selectRange(this.last,  this.lastActive-1);
35071                     this.grid.getView().focusRow(this.lastActive);
35072                     if(last !== false){
35073                         this.last = last;
35074                     }
35075                 }else{
35076                     this.selectFirstRow();
35077                 }
35078                 this.fireEvent("afterselectionchange", this);
35079             },
35080             "down" : function(e){
35081                 if(!e.shiftKey){
35082                     this.selectNext(e.shiftKey);
35083                 }else if(this.last !== false && this.lastActive !== false){
35084                     var last = this.last;
35085                     this.selectRange(this.last,  this.lastActive+1);
35086                     this.grid.getView().focusRow(this.lastActive);
35087                     if(last !== false){
35088                         this.last = last;
35089                     }
35090                 }else{
35091                     this.selectFirstRow();
35092                 }
35093                 this.fireEvent("afterselectionchange", this);
35094             },
35095             scope: this
35096         });
35097
35098         var view = this.grid.view;
35099         view.on("refresh", this.onRefresh, this);
35100         view.on("rowupdated", this.onRowUpdated, this);
35101         view.on("rowremoved", this.onRemove, this);
35102     },
35103
35104     // private
35105     onRefresh : function(){
35106         var ds = this.grid.dataSource, i, v = this.grid.view;
35107         var s = this.selections;
35108         s.each(function(r){
35109             if((i = ds.indexOfId(r.id)) != -1){
35110                 v.onRowSelect(i);
35111             }else{
35112                 s.remove(r);
35113             }
35114         });
35115     },
35116
35117     // private
35118     onRemove : function(v, index, r){
35119         this.selections.remove(r);
35120     },
35121
35122     // private
35123     onRowUpdated : function(v, index, r){
35124         if(this.isSelected(r)){
35125             v.onRowSelect(index);
35126         }
35127     },
35128
35129     /**
35130      * Select records.
35131      * @param {Array} records The records to select
35132      * @param {Boolean} keepExisting (optional) True to keep existing selections
35133      */
35134     selectRecords : function(records, keepExisting){
35135         if(!keepExisting){
35136             this.clearSelections();
35137         }
35138         var ds = this.grid.dataSource;
35139         for(var i = 0, len = records.length; i < len; i++){
35140             this.selectRow(ds.indexOf(records[i]), true);
35141         }
35142     },
35143
35144     /**
35145      * Gets the number of selected rows.
35146      * @return {Number}
35147      */
35148     getCount : function(){
35149         return this.selections.length;
35150     },
35151
35152     /**
35153      * Selects the first row in the grid.
35154      */
35155     selectFirstRow : function(){
35156         this.selectRow(0);
35157     },
35158
35159     /**
35160      * Select the last row.
35161      * @param {Boolean} keepExisting (optional) True to keep existing selections
35162      */
35163     selectLastRow : function(keepExisting){
35164         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35165     },
35166
35167     /**
35168      * Selects the row immediately following the last selected row.
35169      * @param {Boolean} keepExisting (optional) True to keep existing selections
35170      */
35171     selectNext : function(keepExisting){
35172         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35173             this.selectRow(this.last+1, keepExisting);
35174             this.grid.getView().focusRow(this.last);
35175         }
35176     },
35177
35178     /**
35179      * Selects the row that precedes the last selected row.
35180      * @param {Boolean} keepExisting (optional) True to keep existing selections
35181      */
35182     selectPrevious : function(keepExisting){
35183         if(this.last){
35184             this.selectRow(this.last-1, keepExisting);
35185             this.grid.getView().focusRow(this.last);
35186         }
35187     },
35188
35189     /**
35190      * Returns the selected records
35191      * @return {Array} Array of selected records
35192      */
35193     getSelections : function(){
35194         return [].concat(this.selections.items);
35195     },
35196
35197     /**
35198      * Returns the first selected record.
35199      * @return {Record}
35200      */
35201     getSelected : function(){
35202         return this.selections.itemAt(0);
35203     },
35204
35205
35206     /**
35207      * Clears all selections.
35208      */
35209     clearSelections : function(fast){
35210         if(this.locked) return;
35211         if(fast !== true){
35212             var ds = this.grid.dataSource;
35213             var s = this.selections;
35214             s.each(function(r){
35215                 this.deselectRow(ds.indexOfId(r.id));
35216             }, this);
35217             s.clear();
35218         }else{
35219             this.selections.clear();
35220         }
35221         this.last = false;
35222     },
35223
35224
35225     /**
35226      * Selects all rows.
35227      */
35228     selectAll : function(){
35229         if(this.locked) return;
35230         this.selections.clear();
35231         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35232             this.selectRow(i, true);
35233         }
35234     },
35235
35236     /**
35237      * Returns True if there is a selection.
35238      * @return {Boolean}
35239      */
35240     hasSelection : function(){
35241         return this.selections.length > 0;
35242     },
35243
35244     /**
35245      * Returns True if the specified row is selected.
35246      * @param {Number/Record} record The record or index of the record to check
35247      * @return {Boolean}
35248      */
35249     isSelected : function(index){
35250         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35251         return (r && this.selections.key(r.id) ? true : false);
35252     },
35253
35254     /**
35255      * Returns True if the specified record id is selected.
35256      * @param {String} id The id of record to check
35257      * @return {Boolean}
35258      */
35259     isIdSelected : function(id){
35260         return (this.selections.key(id) ? true : false);
35261     },
35262
35263     // private
35264     handleMouseDown : function(e, t){
35265         var view = this.grid.getView(), rowIndex;
35266         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35267             return;
35268         };
35269         if(e.shiftKey && this.last !== false){
35270             var last = this.last;
35271             this.selectRange(last, rowIndex, e.ctrlKey);
35272             this.last = last; // reset the last
35273             view.focusRow(rowIndex);
35274         }else{
35275             var isSelected = this.isSelected(rowIndex);
35276             if(e.button !== 0 && isSelected){
35277                 view.focusRow(rowIndex);
35278             }else if(e.ctrlKey && isSelected){
35279                 this.deselectRow(rowIndex);
35280             }else if(!isSelected){
35281                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35282                 view.focusRow(rowIndex);
35283             }
35284         }
35285         this.fireEvent("afterselectionchange", this);
35286     },
35287     // private
35288     handleDragableRowClick :  function(grid, rowIndex, e) 
35289     {
35290         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35291             this.selectRow(rowIndex, false);
35292             grid.view.focusRow(rowIndex);
35293              this.fireEvent("afterselectionchange", this);
35294         }
35295     },
35296     
35297     /**
35298      * Selects multiple rows.
35299      * @param {Array} rows Array of the indexes of the row to select
35300      * @param {Boolean} keepExisting (optional) True to keep existing selections
35301      */
35302     selectRows : function(rows, keepExisting){
35303         if(!keepExisting){
35304             this.clearSelections();
35305         }
35306         for(var i = 0, len = rows.length; i < len; i++){
35307             this.selectRow(rows[i], true);
35308         }
35309     },
35310
35311     /**
35312      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35313      * @param {Number} startRow The index of the first row in the range
35314      * @param {Number} endRow The index of the last row in the range
35315      * @param {Boolean} keepExisting (optional) True to retain existing selections
35316      */
35317     selectRange : function(startRow, endRow, keepExisting){
35318         if(this.locked) return;
35319         if(!keepExisting){
35320             this.clearSelections();
35321         }
35322         if(startRow <= endRow){
35323             for(var i = startRow; i <= endRow; i++){
35324                 this.selectRow(i, true);
35325             }
35326         }else{
35327             for(var i = startRow; i >= endRow; i--){
35328                 this.selectRow(i, true);
35329             }
35330         }
35331     },
35332
35333     /**
35334      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35335      * @param {Number} startRow The index of the first row in the range
35336      * @param {Number} endRow The index of the last row in the range
35337      */
35338     deselectRange : function(startRow, endRow, preventViewNotify){
35339         if(this.locked) return;
35340         for(var i = startRow; i <= endRow; i++){
35341             this.deselectRow(i, preventViewNotify);
35342         }
35343     },
35344
35345     /**
35346      * Selects a row.
35347      * @param {Number} row The index of the row to select
35348      * @param {Boolean} keepExisting (optional) True to keep existing selections
35349      */
35350     selectRow : function(index, keepExisting, preventViewNotify){
35351         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35352         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35353             if(!keepExisting || this.singleSelect){
35354                 this.clearSelections();
35355             }
35356             var r = this.grid.dataSource.getAt(index);
35357             this.selections.add(r);
35358             this.last = this.lastActive = index;
35359             if(!preventViewNotify){
35360                 this.grid.getView().onRowSelect(index);
35361             }
35362             this.fireEvent("rowselect", this, index, r);
35363             this.fireEvent("selectionchange", this);
35364         }
35365     },
35366
35367     /**
35368      * Deselects a row.
35369      * @param {Number} row The index of the row to deselect
35370      */
35371     deselectRow : function(index, preventViewNotify){
35372         if(this.locked) return;
35373         if(this.last == index){
35374             this.last = false;
35375         }
35376         if(this.lastActive == index){
35377             this.lastActive = false;
35378         }
35379         var r = this.grid.dataSource.getAt(index);
35380         this.selections.remove(r);
35381         if(!preventViewNotify){
35382             this.grid.getView().onRowDeselect(index);
35383         }
35384         this.fireEvent("rowdeselect", this, index);
35385         this.fireEvent("selectionchange", this);
35386     },
35387
35388     // private
35389     restoreLast : function(){
35390         if(this._last){
35391             this.last = this._last;
35392         }
35393     },
35394
35395     // private
35396     acceptsNav : function(row, col, cm){
35397         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35398     },
35399
35400     // private
35401     onEditorKey : function(field, e){
35402         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35403         if(k == e.TAB){
35404             e.stopEvent();
35405             ed.completeEdit();
35406             if(e.shiftKey){
35407                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35408             }else{
35409                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35410             }
35411         }else if(k == e.ENTER && !e.ctrlKey){
35412             e.stopEvent();
35413             ed.completeEdit();
35414             if(e.shiftKey){
35415                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35416             }else{
35417                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35418             }
35419         }else if(k == e.ESC){
35420             ed.cancelEdit();
35421         }
35422         if(newCell){
35423             g.startEditing(newCell[0], newCell[1]);
35424         }
35425     }
35426 });