roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     
76 };
77
78 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
79
80     /**
81      * The id of the element associated with this object.  This is what we
82      * refer to as the "linked element" because the size and position of
83      * this element is used to determine when the drag and drop objects have
84      * interacted.
85      * @property id
86      * @type String
87      */
88     id: null,
89
90     /**
91      * Configuration attributes passed into the constructor
92      * @property config
93      * @type object
94      */
95     config: null,
96
97     /**
98      * The id of the element that will be dragged.  By default this is same
99      * as the linked element , but could be changed to another element. Ex:
100      * Roo.dd.DDProxy
101      * @property dragElId
102      * @type String
103      * @private
104      */
105     dragElId: null,
106
107     /**
108      * the id of the element that initiates the drag operation.  By default
109      * this is the linked element, but could be changed to be a child of this
110      * element.  This lets us do things like only starting the drag when the
111      * header element within the linked html element is clicked.
112      * @property handleElId
113      * @type String
114      * @private
115      */
116     handleElId: null,
117
118     /**
119      * An associative array of HTML tags that will be ignored if clicked.
120      * @property invalidHandleTypes
121      * @type {string: string}
122      */
123     invalidHandleTypes: null,
124
125     /**
126      * An associative array of ids for elements that will be ignored if clicked
127      * @property invalidHandleIds
128      * @type {string: string}
129      */
130     invalidHandleIds: null,
131
132     /**
133      * An indexted array of css class names for elements that will be ignored
134      * if clicked.
135      * @property invalidHandleClasses
136      * @type string[]
137      */
138     invalidHandleClasses: null,
139
140     /**
141      * The linked element's absolute X position at the time the drag was
142      * started
143      * @property startPageX
144      * @type int
145      * @private
146      */
147     startPageX: 0,
148
149     /**
150      * The linked element's absolute X position at the time the drag was
151      * started
152      * @property startPageY
153      * @type int
154      * @private
155      */
156     startPageY: 0,
157
158     /**
159      * The group defines a logical collection of DragDrop objects that are
160      * related.  Instances only get events when interacting with other
161      * DragDrop object in the same group.  This lets us define multiple
162      * groups using a single DragDrop subclass if we want.
163      * @property groups
164      * @type {string: string}
165      */
166     groups: null,
167
168     /**
169      * Individual drag/drop instances can be locked.  This will prevent
170      * onmousedown start drag.
171      * @property locked
172      * @type boolean
173      * @private
174      */
175     locked: false,
176
177     /**
178      * Lock this instance
179      * @method lock
180      */
181     lock: function() { this.locked = true; },
182
183     /**
184      * Unlock this instace
185      * @method unlock
186      */
187     unlock: function() { this.locked = false; },
188
189     /**
190      * By default, all insances can be a drop target.  This can be disabled by
191      * setting isTarget to false.
192      * @method isTarget
193      * @type boolean
194      */
195     isTarget: true,
196
197     /**
198      * The padding configured for this drag and drop object for calculating
199      * the drop zone intersection with this object.
200      * @method padding
201      * @type int[]
202      */
203     padding: null,
204
205     /**
206      * Cached reference to the linked element
207      * @property _domRef
208      * @private
209      */
210     _domRef: null,
211
212     /**
213      * Internal typeof flag
214      * @property __ygDragDrop
215      * @private
216      */
217     __ygDragDrop: true,
218
219     /**
220      * Set to true when horizontal contraints are applied
221      * @property constrainX
222      * @type boolean
223      * @private
224      */
225     constrainX: false,
226
227     /**
228      * Set to true when vertical contraints are applied
229      * @property constrainY
230      * @type boolean
231      * @private
232      */
233     constrainY: false,
234
235     /**
236      * The left constraint
237      * @property minX
238      * @type int
239      * @private
240      */
241     minX: 0,
242
243     /**
244      * The right constraint
245      * @property maxX
246      * @type int
247      * @private
248      */
249     maxX: 0,
250
251     /**
252      * The up constraint
253      * @property minY
254      * @type int
255      * @type int
256      * @private
257      */
258     minY: 0,
259
260     /**
261      * The down constraint
262      * @property maxY
263      * @type int
264      * @private
265      */
266     maxY: 0,
267
268     /**
269      * Maintain offsets when we resetconstraints.  Set to true when you want
270      * the position of the element relative to its parent to stay the same
271      * when the page changes
272      *
273      * @property maintainOffset
274      * @type boolean
275      */
276     maintainOffset: false,
277
278     /**
279      * Array of pixel locations the element will snap to if we specified a
280      * horizontal graduation/interval.  This array is generated automatically
281      * when you define a tick interval.
282      * @property xTicks
283      * @type int[]
284      */
285     xTicks: null,
286
287     /**
288      * Array of pixel locations the element will snap to if we specified a
289      * vertical graduation/interval.  This array is generated automatically
290      * when you define a tick interval.
291      * @property yTicks
292      * @type int[]
293      */
294     yTicks: null,
295
296     /**
297      * By default the drag and drop instance will only respond to the primary
298      * button click (left button for a right-handed mouse).  Set to true to
299      * allow drag and drop to start with any mouse click that is propogated
300      * by the browser
301      * @property primaryButtonOnly
302      * @type boolean
303      */
304     primaryButtonOnly: true,
305
306     /**
307      * The availabe property is false until the linked dom element is accessible.
308      * @property available
309      * @type boolean
310      */
311     available: false,
312
313     /**
314      * By default, drags can only be initiated if the mousedown occurs in the
315      * region the linked element is.  This is done in part to work around a
316      * bug in some browsers that mis-report the mousedown if the previous
317      * mouseup happened outside of the window.  This property is set to true
318      * if outer handles are defined.
319      *
320      * @property hasOuterHandles
321      * @type boolean
322      * @default false
323      */
324     hasOuterHandles: false,
325
326     /**
327      * Code that executes immediately before the startDrag event
328      * @method b4StartDrag
329      * @private
330      */
331     b4StartDrag: function(x, y) { },
332
333     /**
334      * Abstract method called after a drag/drop object is clicked
335      * and the drag or mousedown time thresholds have beeen met.
336      * @method startDrag
337      * @param {int} X click location
338      * @param {int} Y click location
339      */
340     startDrag: function(x, y) { /* override this */ },
341
342     /**
343      * Code that executes immediately before the onDrag event
344      * @method b4Drag
345      * @private
346      */
347     b4Drag: function(e) { },
348
349     /**
350      * Abstract method called during the onMouseMove event while dragging an
351      * object.
352      * @method onDrag
353      * @param {Event} e the mousemove event
354      */
355     onDrag: function(e) { /* override this */ },
356
357     /**
358      * Abstract method called when this element fist begins hovering over
359      * another DragDrop obj
360      * @method onDragEnter
361      * @param {Event} e the mousemove event
362      * @param {String|DragDrop[]} id In POINT mode, the element
363      * id this is hovering over.  In INTERSECT mode, an array of one or more
364      * dragdrop items being hovered over.
365      */
366     onDragEnter: function(e, id) { /* override this */ },
367
368     /**
369      * Code that executes immediately before the onDragOver event
370      * @method b4DragOver
371      * @private
372      */
373     b4DragOver: function(e) { },
374
375     /**
376      * Abstract method called when this element is hovering over another
377      * DragDrop obj
378      * @method onDragOver
379      * @param {Event} e the mousemove event
380      * @param {String|DragDrop[]} id In POINT mode, the element
381      * id this is hovering over.  In INTERSECT mode, an array of dd items
382      * being hovered over.
383      */
384     onDragOver: function(e, id) { /* override this */ },
385
386     /**
387      * Code that executes immediately before the onDragOut event
388      * @method b4DragOut
389      * @private
390      */
391     b4DragOut: function(e) { },
392
393     /**
394      * Abstract method called when we are no longer hovering over an element
395      * @method onDragOut
396      * @param {Event} e the mousemove event
397      * @param {String|DragDrop[]} id In POINT mode, the element
398      * id this was hovering over.  In INTERSECT mode, an array of dd items
399      * that the mouse is no longer over.
400      */
401     onDragOut: function(e, id) { /* override this */ },
402
403     /**
404      * Code that executes immediately before the onDragDrop event
405      * @method b4DragDrop
406      * @private
407      */
408     b4DragDrop: function(e) { },
409
410     /**
411      * Abstract method called when this item is dropped on another DragDrop
412      * obj
413      * @method onDragDrop
414      * @param {Event} e the mouseup event
415      * @param {String|DragDrop[]} id In POINT mode, the element
416      * id this was dropped on.  In INTERSECT mode, an array of dd items this
417      * was dropped on.
418      */
419     onDragDrop: function(e, id) { /* override this */ },
420
421     /**
422      * Abstract method called when this item is dropped on an area with no
423      * drop target
424      * @method onInvalidDrop
425      * @param {Event} e the mouseup event
426      */
427     onInvalidDrop: function(e) { /* override this */ },
428
429     /**
430      * Code that executes immediately before the endDrag event
431      * @method b4EndDrag
432      * @private
433      */
434     b4EndDrag: function(e) { },
435
436     /**
437      * Fired when we are done dragging the object
438      * @method endDrag
439      * @param {Event} e the mouseup event
440      */
441     endDrag: function(e) { /* override this */ },
442
443     /**
444      * Code executed immediately before the onMouseDown event
445      * @method b4MouseDown
446      * @param {Event} e the mousedown event
447      * @private
448      */
449     b4MouseDown: function(e) {  },
450
451     /**
452      * Event handler that fires when a drag/drop obj gets a mousedown
453      * @method onMouseDown
454      * @param {Event} e the mousedown event
455      */
456     onMouseDown: function(e) { /* override this */ },
457
458     /**
459      * Event handler that fires when a drag/drop obj gets a mouseup
460      * @method onMouseUp
461      * @param {Event} e the mouseup event
462      */
463     onMouseUp: function(e) { /* override this */ },
464
465     /**
466      * Override the onAvailable method to do what is needed after the initial
467      * position was determined.
468      * @method onAvailable
469      */
470     onAvailable: function () {
471     },
472
473     /*
474      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
475      * @type Object
476      */
477     defaultPadding : {left:0, right:0, top:0, bottom:0},
478
479     /*
480      * Initializes the drag drop object's constraints to restrict movement to a certain element.
481  *
482  * Usage:
483  <pre><code>
484  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
485                 { dragElId: "existingProxyDiv" });
486  dd.startDrag = function(){
487      this.constrainTo("parent-id");
488  };
489  </code></pre>
490  * Or you can initalize it using the {@link Roo.Element} object:
491  <pre><code>
492  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
493      startDrag : function(){
494          this.constrainTo("parent-id");
495      }
496  });
497  </code></pre>
498      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
499      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
500      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
501      * an object containing the sides to pad. For example: {right:10, bottom:10}
502      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
503      */
504     constrainTo : function(constrainTo, pad, inContent){
505         if(typeof pad == "number"){
506             pad = {left: pad, right:pad, top:pad, bottom:pad};
507         }
508         pad = pad || this.defaultPadding;
509         var b = Roo.get(this.getEl()).getBox();
510         var ce = Roo.get(constrainTo);
511         var s = ce.getScroll();
512         var c, cd = ce.dom;
513         if(cd == document.body){
514             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
515         }else{
516             xy = ce.getXY();
517             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
518         }
519
520
521         var topSpace = b.y - c.y;
522         var leftSpace = b.x - c.x;
523
524         this.resetConstraints();
525         this.setXConstraint(leftSpace - (pad.left||0), // left
526                 c.width - leftSpace - b.width - (pad.right||0) //right
527         );
528         this.setYConstraint(topSpace - (pad.top||0), //top
529                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
530         );
531     },
532
533     /**
534      * Returns a reference to the linked element
535      * @method getEl
536      * @return {HTMLElement} the html element
537      */
538     getEl: function() {
539         if (!this._domRef) {
540             this._domRef = Roo.getDom(this.id);
541         }
542
543         return this._domRef;
544     },
545
546     /**
547      * Returns a reference to the actual element to drag.  By default this is
548      * the same as the html element, but it can be assigned to another
549      * element. An example of this can be found in Roo.dd.DDProxy
550      * @method getDragEl
551      * @return {HTMLElement} the html element
552      */
553     getDragEl: function() {
554         return Roo.getDom(this.dragElId);
555     },
556
557     /**
558      * Sets up the DragDrop object.  Must be called in the constructor of any
559      * Roo.dd.DragDrop subclass
560      * @method init
561      * @param id the id of the linked element
562      * @param {String} sGroup the group of related items
563      * @param {object} config configuration attributes
564      */
565     init: function(id, sGroup, config) {
566         this.initTarget(id, sGroup, config);
567         Event.on(this.id, "mousedown", this.handleMouseDown, this);
568         // Event.on(this.id, "selectstart", Event.preventDefault);
569     },
570
571     /**
572      * Initializes Targeting functionality only... the object does not
573      * get a mousedown handler.
574      * @method initTarget
575      * @param id the id of the linked element
576      * @param {String} sGroup the group of related items
577      * @param {object} config configuration attributes
578      */
579     initTarget: function(id, sGroup, config) {
580
581         // configuration attributes
582         this.config = config || {};
583
584         // create a local reference to the drag and drop manager
585         this.DDM = Roo.dd.DDM;
586         // initialize the groups array
587         this.groups = {};
588
589         // assume that we have an element reference instead of an id if the
590         // parameter is not a string
591         if (typeof id !== "string") {
592             id = Roo.id(id);
593         }
594
595         // set the id
596         this.id = id;
597
598         // add to an interaction group
599         this.addToGroup((sGroup) ? sGroup : "default");
600
601         // We don't want to register this as the handle with the manager
602         // so we just set the id rather than calling the setter.
603         this.handleElId = id;
604
605         // the linked element is the element that gets dragged by default
606         this.setDragElId(id);
607
608         // by default, clicked anchors will not start drag operations.
609         this.invalidHandleTypes = { A: "A" };
610         this.invalidHandleIds = {};
611         this.invalidHandleClasses = [];
612
613         this.applyConfig();
614
615         this.handleOnAvailable();
616     },
617
618     /**
619      * Applies the configuration parameters that were passed into the constructor.
620      * This is supposed to happen at each level through the inheritance chain.  So
621      * a DDProxy implentation will execute apply config on DDProxy, DD, and
622      * DragDrop in order to get all of the parameters that are available in
623      * each object.
624      * @method applyConfig
625      */
626     applyConfig: function() {
627
628         // configurable properties:
629         //    padding, isTarget, maintainOffset, primaryButtonOnly
630         this.padding           = this.config.padding || [0, 0, 0, 0];
631         this.isTarget          = (this.config.isTarget !== false);
632         this.maintainOffset    = (this.config.maintainOffset);
633         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
634
635     },
636
637     /**
638      * Executed when the linked element is available
639      * @method handleOnAvailable
640      * @private
641      */
642     handleOnAvailable: function() {
643         this.available = true;
644         this.resetConstraints();
645         this.onAvailable();
646     },
647
648      /**
649      * Configures the padding for the target zone in px.  Effectively expands
650      * (or reduces) the virtual object size for targeting calculations.
651      * Supports css-style shorthand; if only one parameter is passed, all sides
652      * will have that padding, and if only two are passed, the top and bottom
653      * will have the first param, the left and right the second.
654      * @method setPadding
655      * @param {int} iTop    Top pad
656      * @param {int} iRight  Right pad
657      * @param {int} iBot    Bot pad
658      * @param {int} iLeft   Left pad
659      */
660     setPadding: function(iTop, iRight, iBot, iLeft) {
661         // this.padding = [iLeft, iRight, iTop, iBot];
662         if (!iRight && 0 !== iRight) {
663             this.padding = [iTop, iTop, iTop, iTop];
664         } else if (!iBot && 0 !== iBot) {
665             this.padding = [iTop, iRight, iTop, iRight];
666         } else {
667             this.padding = [iTop, iRight, iBot, iLeft];
668         }
669     },
670
671     /**
672      * Stores the initial placement of the linked element.
673      * @method setInitialPosition
674      * @param {int} diffX   the X offset, default 0
675      * @param {int} diffY   the Y offset, default 0
676      */
677     setInitPosition: function(diffX, diffY) {
678         var el = this.getEl();
679
680         if (!this.DDM.verifyEl(el)) {
681             return;
682         }
683
684         var dx = diffX || 0;
685         var dy = diffY || 0;
686
687         var p = Dom.getXY( el );
688
689         this.initPageX = p[0] - dx;
690         this.initPageY = p[1] - dy;
691
692         this.lastPageX = p[0];
693         this.lastPageY = p[1];
694
695
696         this.setStartPosition(p);
697     },
698
699     /**
700      * Sets the start position of the element.  This is set when the obj
701      * is initialized, the reset when a drag is started.
702      * @method setStartPosition
703      * @param pos current position (from previous lookup)
704      * @private
705      */
706     setStartPosition: function(pos) {
707         var p = pos || Dom.getXY( this.getEl() );
708         this.deltaSetXY = null;
709
710         this.startPageX = p[0];
711         this.startPageY = p[1];
712     },
713
714     /**
715      * Add this instance to a group of related drag/drop objects.  All
716      * instances belong to at least one group, and can belong to as many
717      * groups as needed.
718      * @method addToGroup
719      * @param sGroup {string} the name of the group
720      */
721     addToGroup: function(sGroup) {
722         this.groups[sGroup] = true;
723         this.DDM.regDragDrop(this, sGroup);
724     },
725
726     /**
727      * Remove's this instance from the supplied interaction group
728      * @method removeFromGroup
729      * @param {string}  sGroup  The group to drop
730      */
731     removeFromGroup: function(sGroup) {
732         if (this.groups[sGroup]) {
733             delete this.groups[sGroup];
734         }
735
736         this.DDM.removeDDFromGroup(this, sGroup);
737     },
738
739     /**
740      * Allows you to specify that an element other than the linked element
741      * will be moved with the cursor during a drag
742      * @method setDragElId
743      * @param id {string} the id of the element that will be used to initiate the drag
744      */
745     setDragElId: function(id) {
746         this.dragElId = id;
747     },
748
749     /**
750      * Allows you to specify a child of the linked element that should be
751      * used to initiate the drag operation.  An example of this would be if
752      * you have a content div with text and links.  Clicking anywhere in the
753      * content area would normally start the drag operation.  Use this method
754      * to specify that an element inside of the content div is the element
755      * that starts the drag operation.
756      * @method setHandleElId
757      * @param id {string} the id of the element that will be used to
758      * initiate the drag.
759      */
760     setHandleElId: function(id) {
761         if (typeof id !== "string") {
762             id = Roo.id(id);
763         }
764         this.handleElId = id;
765         this.DDM.regHandle(this.id, id);
766     },
767
768     /**
769      * Allows you to set an element outside of the linked element as a drag
770      * handle
771      * @method setOuterHandleElId
772      * @param id the id of the element that will be used to initiate the drag
773      */
774     setOuterHandleElId: function(id) {
775         if (typeof id !== "string") {
776             id = Roo.id(id);
777         }
778         Event.on(id, "mousedown",
779                 this.handleMouseDown, this);
780         this.setHandleElId(id);
781
782         this.hasOuterHandles = true;
783     },
784
785     /**
786      * Remove all drag and drop hooks for this element
787      * @method unreg
788      */
789     unreg: function() {
790         Event.un(this.id, "mousedown",
791                 this.handleMouseDown);
792         this._domRef = null;
793         this.DDM._remove(this);
794     },
795
796     destroy : function(){
797         this.unreg();
798     },
799
800     /**
801      * Returns true if this instance is locked, or the drag drop mgr is locked
802      * (meaning that all drag/drop is disabled on the page.)
803      * @method isLocked
804      * @return {boolean} true if this obj or all drag/drop is locked, else
805      * false
806      */
807     isLocked: function() {
808         return (this.DDM.isLocked() || this.locked);
809     },
810
811     /**
812      * Fired when this object is clicked
813      * @method handleMouseDown
814      * @param {Event} e
815      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
816      * @private
817      */
818     handleMouseDown: function(e, oDD){
819         if (this.primaryButtonOnly && e.button != 0) {
820             return;
821         }
822
823         if (this.isLocked()) {
824             return;
825         }
826
827         this.DDM.refreshCache(this.groups);
828
829         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
830         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
831         } else {
832             if (this.clickValidator(e)) {
833
834                 // set the initial element position
835                 this.setStartPosition();
836
837
838                 this.b4MouseDown(e);
839                 this.onMouseDown(e);
840
841                 this.DDM.handleMouseDown(e, this);
842
843                 this.DDM.stopEvent(e);
844             } else {
845
846
847             }
848         }
849     },
850
851     clickValidator: function(e) {
852         var target = e.getTarget();
853         return ( this.isValidHandleChild(target) &&
854                     (this.id == this.handleElId ||
855                         this.DDM.handleWasClicked(target, this.id)) );
856     },
857
858     /**
859      * Allows you to specify a tag name that should not start a drag operation
860      * when clicked.  This is designed to facilitate embedding links within a
861      * drag handle that do something other than start the drag.
862      * @method addInvalidHandleType
863      * @param {string} tagName the type of element to exclude
864      */
865     addInvalidHandleType: function(tagName) {
866         var type = tagName.toUpperCase();
867         this.invalidHandleTypes[type] = type;
868     },
869
870     /**
871      * Lets you to specify an element id for a child of a drag handle
872      * that should not initiate a drag
873      * @method addInvalidHandleId
874      * @param {string} id the element id of the element you wish to ignore
875      */
876     addInvalidHandleId: function(id) {
877         if (typeof id !== "string") {
878             id = Roo.id(id);
879         }
880         this.invalidHandleIds[id] = id;
881     },
882
883     /**
884      * Lets you specify a css class of elements that will not initiate a drag
885      * @method addInvalidHandleClass
886      * @param {string} cssClass the class of the elements you wish to ignore
887      */
888     addInvalidHandleClass: function(cssClass) {
889         this.invalidHandleClasses.push(cssClass);
890     },
891
892     /**
893      * Unsets an excluded tag name set by addInvalidHandleType
894      * @method removeInvalidHandleType
895      * @param {string} tagName the type of element to unexclude
896      */
897     removeInvalidHandleType: function(tagName) {
898         var type = tagName.toUpperCase();
899         // this.invalidHandleTypes[type] = null;
900         delete this.invalidHandleTypes[type];
901     },
902
903     /**
904      * Unsets an invalid handle id
905      * @method removeInvalidHandleId
906      * @param {string} id the id of the element to re-enable
907      */
908     removeInvalidHandleId: function(id) {
909         if (typeof id !== "string") {
910             id = Roo.id(id);
911         }
912         delete this.invalidHandleIds[id];
913     },
914
915     /**
916      * Unsets an invalid css class
917      * @method removeInvalidHandleClass
918      * @param {string} cssClass the class of the element(s) you wish to
919      * re-enable
920      */
921     removeInvalidHandleClass: function(cssClass) {
922         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
923             if (this.invalidHandleClasses[i] == cssClass) {
924                 delete this.invalidHandleClasses[i];
925             }
926         }
927     },
928
929     /**
930      * Checks the tag exclusion list to see if this click should be ignored
931      * @method isValidHandleChild
932      * @param {HTMLElement} node the HTMLElement to evaluate
933      * @return {boolean} true if this is a valid tag type, false if not
934      */
935     isValidHandleChild: function(node) {
936
937         var valid = true;
938         // var n = (node.nodeName == "#text") ? node.parentNode : node;
939         var nodeName;
940         try {
941             nodeName = node.nodeName.toUpperCase();
942         } catch(e) {
943             nodeName = node.nodeName;
944         }
945         valid = valid && !this.invalidHandleTypes[nodeName];
946         valid = valid && !this.invalidHandleIds[node.id];
947
948         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
949             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
950         }
951
952
953         return valid;
954
955     },
956
957     /**
958      * Create the array of horizontal tick marks if an interval was specified
959      * in setXConstraint().
960      * @method setXTicks
961      * @private
962      */
963     setXTicks: function(iStartX, iTickSize) {
964         this.xTicks = [];
965         this.xTickSize = iTickSize;
966
967         var tickMap = {};
968
969         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
970             if (!tickMap[i]) {
971                 this.xTicks[this.xTicks.length] = i;
972                 tickMap[i] = true;
973             }
974         }
975
976         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
977             if (!tickMap[i]) {
978                 this.xTicks[this.xTicks.length] = i;
979                 tickMap[i] = true;
980             }
981         }
982
983         this.xTicks.sort(this.DDM.numericSort) ;
984     },
985
986     /**
987      * Create the array of vertical tick marks if an interval was specified in
988      * setYConstraint().
989      * @method setYTicks
990      * @private
991      */
992     setYTicks: function(iStartY, iTickSize) {
993         this.yTicks = [];
994         this.yTickSize = iTickSize;
995
996         var tickMap = {};
997
998         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
999             if (!tickMap[i]) {
1000                 this.yTicks[this.yTicks.length] = i;
1001                 tickMap[i] = true;
1002             }
1003         }
1004
1005         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1006             if (!tickMap[i]) {
1007                 this.yTicks[this.yTicks.length] = i;
1008                 tickMap[i] = true;
1009             }
1010         }
1011
1012         this.yTicks.sort(this.DDM.numericSort) ;
1013     },
1014
1015     /**
1016      * By default, the element can be dragged any place on the screen.  Use
1017      * this method to limit the horizontal travel of the element.  Pass in
1018      * 0,0 for the parameters if you want to lock the drag to the y axis.
1019      * @method setXConstraint
1020      * @param {int} iLeft the number of pixels the element can move to the left
1021      * @param {int} iRight the number of pixels the element can move to the
1022      * right
1023      * @param {int} iTickSize optional parameter for specifying that the
1024      * element
1025      * should move iTickSize pixels at a time.
1026      */
1027     setXConstraint: function(iLeft, iRight, iTickSize) {
1028         this.leftConstraint = iLeft;
1029         this.rightConstraint = iRight;
1030
1031         this.minX = this.initPageX - iLeft;
1032         this.maxX = this.initPageX + iRight;
1033         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1034
1035         this.constrainX = true;
1036     },
1037
1038     /**
1039      * Clears any constraints applied to this instance.  Also clears ticks
1040      * since they can't exist independent of a constraint at this time.
1041      * @method clearConstraints
1042      */
1043     clearConstraints: function() {
1044         this.constrainX = false;
1045         this.constrainY = false;
1046         this.clearTicks();
1047     },
1048
1049     /**
1050      * Clears any tick interval defined for this instance
1051      * @method clearTicks
1052      */
1053     clearTicks: function() {
1054         this.xTicks = null;
1055         this.yTicks = null;
1056         this.xTickSize = 0;
1057         this.yTickSize = 0;
1058     },
1059
1060     /**
1061      * By default, the element can be dragged any place on the screen.  Set
1062      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1063      * parameters if you want to lock the drag to the x axis.
1064      * @method setYConstraint
1065      * @param {int} iUp the number of pixels the element can move up
1066      * @param {int} iDown the number of pixels the element can move down
1067      * @param {int} iTickSize optional parameter for specifying that the
1068      * element should move iTickSize pixels at a time.
1069      */
1070     setYConstraint: function(iUp, iDown, iTickSize) {
1071         this.topConstraint = iUp;
1072         this.bottomConstraint = iDown;
1073
1074         this.minY = this.initPageY - iUp;
1075         this.maxY = this.initPageY + iDown;
1076         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1077
1078         this.constrainY = true;
1079
1080     },
1081
1082     /**
1083      * resetConstraints must be called if you manually reposition a dd element.
1084      * @method resetConstraints
1085      * @param {boolean} maintainOffset
1086      */
1087     resetConstraints: function() {
1088
1089
1090         // Maintain offsets if necessary
1091         if (this.initPageX || this.initPageX === 0) {
1092             // figure out how much this thing has moved
1093             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1094             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1095
1096             this.setInitPosition(dx, dy);
1097
1098         // This is the first time we have detected the element's position
1099         } else {
1100             this.setInitPosition();
1101         }
1102
1103         if (this.constrainX) {
1104             this.setXConstraint( this.leftConstraint,
1105                                  this.rightConstraint,
1106                                  this.xTickSize        );
1107         }
1108
1109         if (this.constrainY) {
1110             this.setYConstraint( this.topConstraint,
1111                                  this.bottomConstraint,
1112                                  this.yTickSize         );
1113         }
1114     },
1115
1116     /**
1117      * Normally the drag element is moved pixel by pixel, but we can specify
1118      * that it move a number of pixels at a time.  This method resolves the
1119      * location when we have it set up like this.
1120      * @method getTick
1121      * @param {int} val where we want to place the object
1122      * @param {int[]} tickArray sorted array of valid points
1123      * @return {int} the closest tick
1124      * @private
1125      */
1126     getTick: function(val, tickArray) {
1127
1128         if (!tickArray) {
1129             // If tick interval is not defined, it is effectively 1 pixel,
1130             // so we return the value passed to us.
1131             return val;
1132         } else if (tickArray[0] >= val) {
1133             // The value is lower than the first tick, so we return the first
1134             // tick.
1135             return tickArray[0];
1136         } else {
1137             for (var i=0, len=tickArray.length; i<len; ++i) {
1138                 var next = i + 1;
1139                 if (tickArray[next] && tickArray[next] >= val) {
1140                     var diff1 = val - tickArray[i];
1141                     var diff2 = tickArray[next] - val;
1142                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1143                 }
1144             }
1145
1146             // The value is larger than the last tick, so we return the last
1147             // tick.
1148             return tickArray[tickArray.length - 1];
1149         }
1150     },
1151
1152     /**
1153      * toString method
1154      * @method toString
1155      * @return {string} string representation of the dd obj
1156      */
1157     toString: function() {
1158         return ("DragDrop " + this.id);
1159     }
1160
1161 });
1162
1163 })();
1164 /*
1165  * Based on:
1166  * Ext JS Library 1.1.1
1167  * Copyright(c) 2006-2007, Ext JS, LLC.
1168  *
1169  * Originally Released Under LGPL - original licence link has changed is not relivant.
1170  *
1171  * Fork - LGPL
1172  * <script type="text/javascript">
1173  */
1174
1175
1176 /**
1177  * The drag and drop utility provides a framework for building drag and drop
1178  * applications.  In addition to enabling drag and drop for specific elements,
1179  * the drag and drop elements are tracked by the manager class, and the
1180  * interactions between the various elements are tracked during the drag and
1181  * the implementing code is notified about these important moments.
1182  */
1183
1184 // Only load the library once.  Rewriting the manager class would orphan
1185 // existing drag and drop instances.
1186 if (!Roo.dd.DragDropMgr) {
1187
1188 /**
1189  * @class Roo.dd.DragDropMgr
1190  * DragDropMgr is a singleton that tracks the element interaction for
1191  * all DragDrop items in the window.  Generally, you will not call
1192  * this class directly, but it does have helper methods that could
1193  * be useful in your DragDrop implementations.
1194  * @singleton
1195  */
1196 Roo.dd.DragDropMgr = function() {
1197
1198     var Event = Roo.EventManager;
1199
1200     return {
1201
1202         /**
1203          * Two dimensional Array of registered DragDrop objects.  The first
1204          * dimension is the DragDrop item group, the second the DragDrop
1205          * object.
1206          * @property ids
1207          * @type {string: string}
1208          * @private
1209          * @static
1210          */
1211         ids: {},
1212
1213         /**
1214          * Array of element ids defined as drag handles.  Used to determine
1215          * if the element that generated the mousedown event is actually the
1216          * handle and not the html element itself.
1217          * @property handleIds
1218          * @type {string: string}
1219          * @private
1220          * @static
1221          */
1222         handleIds: {},
1223
1224         /**
1225          * the DragDrop object that is currently being dragged
1226          * @property dragCurrent
1227          * @type DragDrop
1228          * @private
1229          * @static
1230          **/
1231         dragCurrent: null,
1232
1233         /**
1234          * the DragDrop object(s) that are being hovered over
1235          * @property dragOvers
1236          * @type Array
1237          * @private
1238          * @static
1239          */
1240         dragOvers: {},
1241
1242         /**
1243          * the X distance between the cursor and the object being dragged
1244          * @property deltaX
1245          * @type int
1246          * @private
1247          * @static
1248          */
1249         deltaX: 0,
1250
1251         /**
1252          * the Y distance between the cursor and the object being dragged
1253          * @property deltaY
1254          * @type int
1255          * @private
1256          * @static
1257          */
1258         deltaY: 0,
1259
1260         /**
1261          * Flag to determine if we should prevent the default behavior of the
1262          * events we define. By default this is true, but this can be set to
1263          * false if you need the default behavior (not recommended)
1264          * @property preventDefault
1265          * @type boolean
1266          * @static
1267          */
1268         preventDefault: true,
1269
1270         /**
1271          * Flag to determine if we should stop the propagation of the events
1272          * we generate. This is true by default but you may want to set it to
1273          * false if the html element contains other features that require the
1274          * mouse click.
1275          * @property stopPropagation
1276          * @type boolean
1277          * @static
1278          */
1279         stopPropagation: true,
1280
1281         /**
1282          * Internal flag that is set to true when drag and drop has been
1283          * intialized
1284          * @property initialized
1285          * @private
1286          * @static
1287          */
1288         initalized: false,
1289
1290         /**
1291          * All drag and drop can be disabled.
1292          * @property locked
1293          * @private
1294          * @static
1295          */
1296         locked: false,
1297
1298         /**
1299          * Called the first time an element is registered.
1300          * @method init
1301          * @private
1302          * @static
1303          */
1304         init: function() {
1305             this.initialized = true;
1306         },
1307
1308         /**
1309          * In point mode, drag and drop interaction is defined by the
1310          * location of the cursor during the drag/drop
1311          * @property POINT
1312          * @type int
1313          * @static
1314          */
1315         POINT: 0,
1316
1317         /**
1318          * In intersect mode, drag and drop interactio nis defined by the
1319          * overlap of two or more drag and drop objects.
1320          * @property INTERSECT
1321          * @type int
1322          * @static
1323          */
1324         INTERSECT: 1,
1325
1326         /**
1327          * The current drag and drop mode.  Default: POINT
1328          * @property mode
1329          * @type int
1330          * @static
1331          */
1332         mode: 0,
1333
1334         /**
1335          * Runs method on all drag and drop objects
1336          * @method _execOnAll
1337          * @private
1338          * @static
1339          */
1340         _execOnAll: function(sMethod, args) {
1341             for (var i in this.ids) {
1342                 for (var j in this.ids[i]) {
1343                     var oDD = this.ids[i][j];
1344                     if (! this.isTypeOfDD(oDD)) {
1345                         continue;
1346                     }
1347                     oDD[sMethod].apply(oDD, args);
1348                 }
1349             }
1350         },
1351
1352         /**
1353          * Drag and drop initialization.  Sets up the global event handlers
1354          * @method _onLoad
1355          * @private
1356          * @static
1357          */
1358         _onLoad: function() {
1359
1360             this.init();
1361
1362
1363             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1364             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1365             Event.on(window,   "unload",    this._onUnload, this, true);
1366             Event.on(window,   "resize",    this._onResize, this, true);
1367             // Event.on(window,   "mouseout",    this._test);
1368
1369         },
1370
1371         /**
1372          * Reset constraints on all drag and drop objs
1373          * @method _onResize
1374          * @private
1375          * @static
1376          */
1377         _onResize: function(e) {
1378             this._execOnAll("resetConstraints", []);
1379         },
1380
1381         /**
1382          * Lock all drag and drop functionality
1383          * @method lock
1384          * @static
1385          */
1386         lock: function() { this.locked = true; },
1387
1388         /**
1389          * Unlock all drag and drop functionality
1390          * @method unlock
1391          * @static
1392          */
1393         unlock: function() { this.locked = false; },
1394
1395         /**
1396          * Is drag and drop locked?
1397          * @method isLocked
1398          * @return {boolean} True if drag and drop is locked, false otherwise.
1399          * @static
1400          */
1401         isLocked: function() { return this.locked; },
1402
1403         /**
1404          * Location cache that is set for all drag drop objects when a drag is
1405          * initiated, cleared when the drag is finished.
1406          * @property locationCache
1407          * @private
1408          * @static
1409          */
1410         locationCache: {},
1411
1412         /**
1413          * Set useCache to false if you want to force object the lookup of each
1414          * drag and drop linked element constantly during a drag.
1415          * @property useCache
1416          * @type boolean
1417          * @static
1418          */
1419         useCache: true,
1420
1421         /**
1422          * The number of pixels that the mouse needs to move after the
1423          * mousedown before the drag is initiated.  Default=3;
1424          * @property clickPixelThresh
1425          * @type int
1426          * @static
1427          */
1428         clickPixelThresh: 3,
1429
1430         /**
1431          * The number of milliseconds after the mousedown event to initiate the
1432          * drag if we don't get a mouseup event. Default=1000
1433          * @property clickTimeThresh
1434          * @type int
1435          * @static
1436          */
1437         clickTimeThresh: 350,
1438
1439         /**
1440          * Flag that indicates that either the drag pixel threshold or the
1441          * mousdown time threshold has been met
1442          * @property dragThreshMet
1443          * @type boolean
1444          * @private
1445          * @static
1446          */
1447         dragThreshMet: false,
1448
1449         /**
1450          * Timeout used for the click time threshold
1451          * @property clickTimeout
1452          * @type Object
1453          * @private
1454          * @static
1455          */
1456         clickTimeout: null,
1457
1458         /**
1459          * The X position of the mousedown event stored for later use when a
1460          * drag threshold is met.
1461          * @property startX
1462          * @type int
1463          * @private
1464          * @static
1465          */
1466         startX: 0,
1467
1468         /**
1469          * The Y position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startY
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startY: 0,
1477
1478         /**
1479          * Each DragDrop instance must be registered with the DragDropMgr.
1480          * This is executed in DragDrop.init()
1481          * @method regDragDrop
1482          * @param {DragDrop} oDD the DragDrop object to register
1483          * @param {String} sGroup the name of the group this element belongs to
1484          * @static
1485          */
1486         regDragDrop: function(oDD, sGroup) {
1487             if (!this.initialized) { this.init(); }
1488
1489             if (!this.ids[sGroup]) {
1490                 this.ids[sGroup] = {};
1491             }
1492             this.ids[sGroup][oDD.id] = oDD;
1493         },
1494
1495         /**
1496          * Removes the supplied dd instance from the supplied group. Executed
1497          * by DragDrop.removeFromGroup, so don't call this function directly.
1498          * @method removeDDFromGroup
1499          * @private
1500          * @static
1501          */
1502         removeDDFromGroup: function(oDD, sGroup) {
1503             if (!this.ids[sGroup]) {
1504                 this.ids[sGroup] = {};
1505             }
1506
1507             var obj = this.ids[sGroup];
1508             if (obj && obj[oDD.id]) {
1509                 delete obj[oDD.id];
1510             }
1511         },
1512
1513         /**
1514          * Unregisters a drag and drop item.  This is executed in
1515          * DragDrop.unreg, use that method instead of calling this directly.
1516          * @method _remove
1517          * @private
1518          * @static
1519          */
1520         _remove: function(oDD) {
1521             for (var g in oDD.groups) {
1522                 if (g && this.ids[g][oDD.id]) {
1523                     delete this.ids[g][oDD.id];
1524                 }
1525             }
1526             delete this.handleIds[oDD.id];
1527         },
1528
1529         /**
1530          * Each DragDrop handle element must be registered.  This is done
1531          * automatically when executing DragDrop.setHandleElId()
1532          * @method regHandle
1533          * @param {String} sDDId the DragDrop id this element is a handle for
1534          * @param {String} sHandleId the id of the element that is the drag
1535          * handle
1536          * @static
1537          */
1538         regHandle: function(sDDId, sHandleId) {
1539             if (!this.handleIds[sDDId]) {
1540                 this.handleIds[sDDId] = {};
1541             }
1542             this.handleIds[sDDId][sHandleId] = sHandleId;
1543         },
1544
1545         /**
1546          * Utility function to determine if a given element has been
1547          * registered as a drag drop item.
1548          * @method isDragDrop
1549          * @param {String} id the element id to check
1550          * @return {boolean} true if this element is a DragDrop item,
1551          * false otherwise
1552          * @static
1553          */
1554         isDragDrop: function(id) {
1555             return ( this.getDDById(id) ) ? true : false;
1556         },
1557
1558         /**
1559          * Returns the drag and drop instances that are in all groups the
1560          * passed in instance belongs to.
1561          * @method getRelated
1562          * @param {DragDrop} p_oDD the obj to get related data for
1563          * @param {boolean} bTargetsOnly if true, only return targetable objs
1564          * @return {DragDrop[]} the related instances
1565          * @static
1566          */
1567         getRelated: function(p_oDD, bTargetsOnly) {
1568             var oDDs = [];
1569             for (var i in p_oDD.groups) {
1570                 for (j in this.ids[i]) {
1571                     var dd = this.ids[i][j];
1572                     if (! this.isTypeOfDD(dd)) {
1573                         continue;
1574                     }
1575                     if (!bTargetsOnly || dd.isTarget) {
1576                         oDDs[oDDs.length] = dd;
1577                     }
1578                 }
1579             }
1580
1581             return oDDs;
1582         },
1583
1584         /**
1585          * Returns true if the specified dd target is a legal target for
1586          * the specifice drag obj
1587          * @method isLegalTarget
1588          * @param {DragDrop} the drag obj
1589          * @param {DragDrop} the target
1590          * @return {boolean} true if the target is a legal target for the
1591          * dd obj
1592          * @static
1593          */
1594         isLegalTarget: function (oDD, oTargetDD) {
1595             var targets = this.getRelated(oDD, true);
1596             for (var i=0, len=targets.length;i<len;++i) {
1597                 if (targets[i].id == oTargetDD.id) {
1598                     return true;
1599                 }
1600             }
1601
1602             return false;
1603         },
1604
1605         /**
1606          * My goal is to be able to transparently determine if an object is
1607          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1608          * returns "object", oDD.constructor.toString() always returns
1609          * "DragDrop" and not the name of the subclass.  So for now it just
1610          * evaluates a well-known variable in DragDrop.
1611          * @method isTypeOfDD
1612          * @param {Object} the object to evaluate
1613          * @return {boolean} true if typeof oDD = DragDrop
1614          * @static
1615          */
1616         isTypeOfDD: function (oDD) {
1617             return (oDD && oDD.__ygDragDrop);
1618         },
1619
1620         /**
1621          * Utility function to determine if a given element has been
1622          * registered as a drag drop handle for the given Drag Drop object.
1623          * @method isHandle
1624          * @param {String} id the element id to check
1625          * @return {boolean} true if this element is a DragDrop handle, false
1626          * otherwise
1627          * @static
1628          */
1629         isHandle: function(sDDId, sHandleId) {
1630             return ( this.handleIds[sDDId] &&
1631                             this.handleIds[sDDId][sHandleId] );
1632         },
1633
1634         /**
1635          * Returns the DragDrop instance for a given id
1636          * @method getDDById
1637          * @param {String} id the id of the DragDrop object
1638          * @return {DragDrop} the drag drop object, null if it is not found
1639          * @static
1640          */
1641         getDDById: function(id) {
1642             for (var i in this.ids) {
1643                 if (this.ids[i][id]) {
1644                     return this.ids[i][id];
1645                 }
1646             }
1647             return null;
1648         },
1649
1650         /**
1651          * Fired after a registered DragDrop object gets the mousedown event.
1652          * Sets up the events required to track the object being dragged
1653          * @method handleMouseDown
1654          * @param {Event} e the event
1655          * @param oDD the DragDrop object being dragged
1656          * @private
1657          * @static
1658          */
1659         handleMouseDown: function(e, oDD) {
1660             if(Roo.QuickTips){
1661                 Roo.QuickTips.disable();
1662             }
1663             this.currentTarget = e.getTarget();
1664
1665             this.dragCurrent = oDD;
1666
1667             var el = oDD.getEl();
1668
1669             // track start position
1670             this.startX = e.getPageX();
1671             this.startY = e.getPageY();
1672
1673             this.deltaX = this.startX - el.offsetLeft;
1674             this.deltaY = this.startY - el.offsetTop;
1675
1676             this.dragThreshMet = false;
1677
1678             this.clickTimeout = setTimeout(
1679                     function() {
1680                         var DDM = Roo.dd.DDM;
1681                         DDM.startDrag(DDM.startX, DDM.startY);
1682                     },
1683                     this.clickTimeThresh );
1684         },
1685
1686         /**
1687          * Fired when either the drag pixel threshol or the mousedown hold
1688          * time threshold has been met.
1689          * @method startDrag
1690          * @param x {int} the X position of the original mousedown
1691          * @param y {int} the Y position of the original mousedown
1692          * @static
1693          */
1694         startDrag: function(x, y) {
1695             clearTimeout(this.clickTimeout);
1696             if (this.dragCurrent) {
1697                 this.dragCurrent.b4StartDrag(x, y);
1698                 this.dragCurrent.startDrag(x, y);
1699             }
1700             this.dragThreshMet = true;
1701         },
1702
1703         /**
1704          * Internal function to handle the mouseup event.  Will be invoked
1705          * from the context of the document.
1706          * @method handleMouseUp
1707          * @param {Event} e the event
1708          * @private
1709          * @static
1710          */
1711         handleMouseUp: function(e) {
1712
1713             if(Roo.QuickTips){
1714                 Roo.QuickTips.enable();
1715             }
1716             if (! this.dragCurrent) {
1717                 return;
1718             }
1719
1720             clearTimeout(this.clickTimeout);
1721
1722             if (this.dragThreshMet) {
1723                 this.fireEvents(e, true);
1724             } else {
1725             }
1726
1727             this.stopDrag(e);
1728
1729             this.stopEvent(e);
1730         },
1731
1732         /**
1733          * Utility to stop event propagation and event default, if these
1734          * features are turned on.
1735          * @method stopEvent
1736          * @param {Event} e the event as returned by this.getEvent()
1737          * @static
1738          */
1739         stopEvent: function(e){
1740             if(this.stopPropagation) {
1741                 e.stopPropagation();
1742             }
1743
1744             if (this.preventDefault) {
1745                 e.preventDefault();
1746             }
1747         },
1748
1749         /**
1750          * Internal function to clean up event handlers after the drag
1751          * operation is complete
1752          * @method stopDrag
1753          * @param {Event} e the event
1754          * @private
1755          * @static
1756          */
1757         stopDrag: function(e) {
1758             // Fire the drag end event for the item that was dragged
1759             if (this.dragCurrent) {
1760                 if (this.dragThreshMet) {
1761                     this.dragCurrent.b4EndDrag(e);
1762                     this.dragCurrent.endDrag(e);
1763                 }
1764
1765                 this.dragCurrent.onMouseUp(e);
1766             }
1767
1768             this.dragCurrent = null;
1769             this.dragOvers = {};
1770         },
1771
1772         /**
1773          * Internal function to handle the mousemove event.  Will be invoked
1774          * from the context of the html element.
1775          *
1776          * @TODO figure out what we can do about mouse events lost when the
1777          * user drags objects beyond the window boundary.  Currently we can
1778          * detect this in internet explorer by verifying that the mouse is
1779          * down during the mousemove event.  Firefox doesn't give us the
1780          * button state on the mousemove event.
1781          * @method handleMouseMove
1782          * @param {Event} e the event
1783          * @private
1784          * @static
1785          */
1786         handleMouseMove: function(e) {
1787             if (! this.dragCurrent) {
1788                 return true;
1789             }
1790
1791             // var button = e.which || e.button;
1792
1793             // check for IE mouseup outside of page boundary
1794             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1795                 this.stopEvent(e);
1796                 return this.handleMouseUp(e);
1797             }
1798
1799             if (!this.dragThreshMet) {
1800                 var diffX = Math.abs(this.startX - e.getPageX());
1801                 var diffY = Math.abs(this.startY - e.getPageY());
1802                 if (diffX > this.clickPixelThresh ||
1803                             diffY > this.clickPixelThresh) {
1804                     this.startDrag(this.startX, this.startY);
1805                 }
1806             }
1807
1808             if (this.dragThreshMet) {
1809                 this.dragCurrent.b4Drag(e);
1810                 this.dragCurrent.onDrag(e);
1811                 if(!this.dragCurrent.moveOnly){
1812                     this.fireEvents(e, false);
1813                 }
1814             }
1815
1816             this.stopEvent(e);
1817
1818             return true;
1819         },
1820
1821         /**
1822          * Iterates over all of the DragDrop elements to find ones we are
1823          * hovering over or dropping on
1824          * @method fireEvents
1825          * @param {Event} e the event
1826          * @param {boolean} isDrop is this a drop op or a mouseover op?
1827          * @private
1828          * @static
1829          */
1830         fireEvents: function(e, isDrop) {
1831             var dc = this.dragCurrent;
1832
1833             // If the user did the mouse up outside of the window, we could
1834             // get here even though we have ended the drag.
1835             if (!dc || dc.isLocked()) {
1836                 return;
1837             }
1838
1839             var pt = e.getPoint();
1840
1841             // cache the previous dragOver array
1842             var oldOvers = [];
1843
1844             var outEvts   = [];
1845             var overEvts  = [];
1846             var dropEvts  = [];
1847             var enterEvts = [];
1848
1849             // Check to see if the object(s) we were hovering over is no longer
1850             // being hovered over so we can fire the onDragOut event
1851             for (var i in this.dragOvers) {
1852
1853                 var ddo = this.dragOvers[i];
1854
1855                 if (! this.isTypeOfDD(ddo)) {
1856                     continue;
1857                 }
1858
1859                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1860                     outEvts.push( ddo );
1861                 }
1862
1863                 oldOvers[i] = true;
1864                 delete this.dragOvers[i];
1865             }
1866
1867             for (var sGroup in dc.groups) {
1868
1869                 if ("string" != typeof sGroup) {
1870                     continue;
1871                 }
1872
1873                 for (i in this.ids[sGroup]) {
1874                     var oDD = this.ids[sGroup][i];
1875                     if (! this.isTypeOfDD(oDD)) {
1876                         continue;
1877                     }
1878
1879                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1880                         if (this.isOverTarget(pt, oDD, this.mode)) {
1881                             // look for drop interactions
1882                             if (isDrop) {
1883                                 dropEvts.push( oDD );
1884                             // look for drag enter and drag over interactions
1885                             } else {
1886
1887                                 // initial drag over: dragEnter fires
1888                                 if (!oldOvers[oDD.id]) {
1889                                     enterEvts.push( oDD );
1890                                 // subsequent drag overs: dragOver fires
1891                                 } else {
1892                                     overEvts.push( oDD );
1893                                 }
1894
1895                                 this.dragOvers[oDD.id] = oDD;
1896                             }
1897                         }
1898                     }
1899                 }
1900             }
1901
1902             if (this.mode) {
1903                 if (outEvts.length) {
1904                     dc.b4DragOut(e, outEvts);
1905                     dc.onDragOut(e, outEvts);
1906                 }
1907
1908                 if (enterEvts.length) {
1909                     dc.onDragEnter(e, enterEvts);
1910                 }
1911
1912                 if (overEvts.length) {
1913                     dc.b4DragOver(e, overEvts);
1914                     dc.onDragOver(e, overEvts);
1915                 }
1916
1917                 if (dropEvts.length) {
1918                     dc.b4DragDrop(e, dropEvts);
1919                     dc.onDragDrop(e, dropEvts);
1920                 }
1921
1922             } else {
1923                 // fire dragout events
1924                 var len = 0;
1925                 for (i=0, len=outEvts.length; i<len; ++i) {
1926                     dc.b4DragOut(e, outEvts[i].id);
1927                     dc.onDragOut(e, outEvts[i].id);
1928                 }
1929
1930                 // fire enter events
1931                 for (i=0,len=enterEvts.length; i<len; ++i) {
1932                     // dc.b4DragEnter(e, oDD.id);
1933                     dc.onDragEnter(e, enterEvts[i].id);
1934                 }
1935
1936                 // fire over events
1937                 for (i=0,len=overEvts.length; i<len; ++i) {
1938                     dc.b4DragOver(e, overEvts[i].id);
1939                     dc.onDragOver(e, overEvts[i].id);
1940                 }
1941
1942                 // fire drop events
1943                 for (i=0, len=dropEvts.length; i<len; ++i) {
1944                     dc.b4DragDrop(e, dropEvts[i].id);
1945                     dc.onDragDrop(e, dropEvts[i].id);
1946                 }
1947
1948             }
1949
1950             // notify about a drop that did not find a target
1951             if (isDrop && !dropEvts.length) {
1952                 dc.onInvalidDrop(e);
1953             }
1954
1955         },
1956
1957         /**
1958          * Helper function for getting the best match from the list of drag
1959          * and drop objects returned by the drag and drop events when we are
1960          * in INTERSECT mode.  It returns either the first object that the
1961          * cursor is over, or the object that has the greatest overlap with
1962          * the dragged element.
1963          * @method getBestMatch
1964          * @param  {DragDrop[]} dds The array of drag and drop objects
1965          * targeted
1966          * @return {DragDrop}       The best single match
1967          * @static
1968          */
1969         getBestMatch: function(dds) {
1970             var winner = null;
1971             // Return null if the input is not what we expect
1972             //if (!dds || !dds.length || dds.length == 0) {
1973                // winner = null;
1974             // If there is only one item, it wins
1975             //} else if (dds.length == 1) {
1976
1977             var len = dds.length;
1978
1979             if (len == 1) {
1980                 winner = dds[0];
1981             } else {
1982                 // Loop through the targeted items
1983                 for (var i=0; i<len; ++i) {
1984                     var dd = dds[i];
1985                     // If the cursor is over the object, it wins.  If the
1986                     // cursor is over multiple matches, the first one we come
1987                     // to wins.
1988                     if (dd.cursorIsOver) {
1989                         winner = dd;
1990                         break;
1991                     // Otherwise the object with the most overlap wins
1992                     } else {
1993                         if (!winner ||
1994                             winner.overlap.getArea() < dd.overlap.getArea()) {
1995                             winner = dd;
1996                         }
1997                     }
1998                 }
1999             }
2000
2001             return winner;
2002         },
2003
2004         /**
2005          * Refreshes the cache of the top-left and bottom-right points of the
2006          * drag and drop objects in the specified group(s).  This is in the
2007          * format that is stored in the drag and drop instance, so typical
2008          * usage is:
2009          * <code>
2010          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2011          * </code>
2012          * Alternatively:
2013          * <code>
2014          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2015          * </code>
2016          * @TODO this really should be an indexed array.  Alternatively this
2017          * method could accept both.
2018          * @method refreshCache
2019          * @param {Object} groups an associative array of groups to refresh
2020          * @static
2021          */
2022         refreshCache: function(groups) {
2023             for (var sGroup in groups) {
2024                 if ("string" != typeof sGroup) {
2025                     continue;
2026                 }
2027                 for (var i in this.ids[sGroup]) {
2028                     var oDD = this.ids[sGroup][i];
2029
2030                     if (this.isTypeOfDD(oDD)) {
2031                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2032                         var loc = this.getLocation(oDD);
2033                         if (loc) {
2034                             this.locationCache[oDD.id] = loc;
2035                         } else {
2036                             delete this.locationCache[oDD.id];
2037                             // this will unregister the drag and drop object if
2038                             // the element is not in a usable state
2039                             // oDD.unreg();
2040                         }
2041                     }
2042                 }
2043             }
2044         },
2045
2046         /**
2047          * This checks to make sure an element exists and is in the DOM.  The
2048          * main purpose is to handle cases where innerHTML is used to remove
2049          * drag and drop objects from the DOM.  IE provides an 'unspecified
2050          * error' when trying to access the offsetParent of such an element
2051          * @method verifyEl
2052          * @param {HTMLElement} el the element to check
2053          * @return {boolean} true if the element looks usable
2054          * @static
2055          */
2056         verifyEl: function(el) {
2057             if (el) {
2058                 var parent;
2059                 if(Roo.isIE){
2060                     try{
2061                         parent = el.offsetParent;
2062                     }catch(e){}
2063                 }else{
2064                     parent = el.offsetParent;
2065                 }
2066                 if (parent) {
2067                     return true;
2068                 }
2069             }
2070
2071             return false;
2072         },
2073
2074         /**
2075          * Returns a Region object containing the drag and drop element's position
2076          * and size, including the padding configured for it
2077          * @method getLocation
2078          * @param {DragDrop} oDD the drag and drop object to get the
2079          *                       location for
2080          * @return {Roo.lib.Region} a Region object representing the total area
2081          *                             the element occupies, including any padding
2082          *                             the instance is configured for.
2083          * @static
2084          */
2085         getLocation: function(oDD) {
2086             if (! this.isTypeOfDD(oDD)) {
2087                 return null;
2088             }
2089
2090             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2091
2092             try {
2093                 pos= Roo.lib.Dom.getXY(el);
2094             } catch (e) { }
2095
2096             if (!pos) {
2097                 return null;
2098             }
2099
2100             x1 = pos[0];
2101             x2 = x1 + el.offsetWidth;
2102             y1 = pos[1];
2103             y2 = y1 + el.offsetHeight;
2104
2105             t = y1 - oDD.padding[0];
2106             r = x2 + oDD.padding[1];
2107             b = y2 + oDD.padding[2];
2108             l = x1 - oDD.padding[3];
2109
2110             return new Roo.lib.Region( t, r, b, l );
2111         },
2112
2113         /**
2114          * Checks the cursor location to see if it over the target
2115          * @method isOverTarget
2116          * @param {Roo.lib.Point} pt The point to evaluate
2117          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2118          * @return {boolean} true if the mouse is over the target
2119          * @private
2120          * @static
2121          */
2122         isOverTarget: function(pt, oTarget, intersect) {
2123             // use cache if available
2124             var loc = this.locationCache[oTarget.id];
2125             if (!loc || !this.useCache) {
2126                 loc = this.getLocation(oTarget);
2127                 this.locationCache[oTarget.id] = loc;
2128
2129             }
2130
2131             if (!loc) {
2132                 return false;
2133             }
2134
2135             oTarget.cursorIsOver = loc.contains( pt );
2136
2137             // DragDrop is using this as a sanity check for the initial mousedown
2138             // in this case we are done.  In POINT mode, if the drag obj has no
2139             // contraints, we are also done. Otherwise we need to evaluate the
2140             // location of the target as related to the actual location of the
2141             // dragged element.
2142             var dc = this.dragCurrent;
2143             if (!dc || !dc.getTargetCoord ||
2144                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2145                 return oTarget.cursorIsOver;
2146             }
2147
2148             oTarget.overlap = null;
2149
2150             // Get the current location of the drag element, this is the
2151             // location of the mouse event less the delta that represents
2152             // where the original mousedown happened on the element.  We
2153             // need to consider constraints and ticks as well.
2154             var pos = dc.getTargetCoord(pt.x, pt.y);
2155
2156             var el = dc.getDragEl();
2157             var curRegion = new Roo.lib.Region( pos.y,
2158                                                    pos.x + el.offsetWidth,
2159                                                    pos.y + el.offsetHeight,
2160                                                    pos.x );
2161
2162             var overlap = curRegion.intersect(loc);
2163
2164             if (overlap) {
2165                 oTarget.overlap = overlap;
2166                 return (intersect) ? true : oTarget.cursorIsOver;
2167             } else {
2168                 return false;
2169             }
2170         },
2171
2172         /**
2173          * unload event handler
2174          * @method _onUnload
2175          * @private
2176          * @static
2177          */
2178         _onUnload: function(e, me) {
2179             Roo.dd.DragDropMgr.unregAll();
2180         },
2181
2182         /**
2183          * Cleans up the drag and drop events and objects.
2184          * @method unregAll
2185          * @private
2186          * @static
2187          */
2188         unregAll: function() {
2189
2190             if (this.dragCurrent) {
2191                 this.stopDrag();
2192                 this.dragCurrent = null;
2193             }
2194
2195             this._execOnAll("unreg", []);
2196
2197             for (i in this.elementCache) {
2198                 delete this.elementCache[i];
2199             }
2200
2201             this.elementCache = {};
2202             this.ids = {};
2203         },
2204
2205         /**
2206          * A cache of DOM elements
2207          * @property elementCache
2208          * @private
2209          * @static
2210          */
2211         elementCache: {},
2212
2213         /**
2214          * Get the wrapper for the DOM element specified
2215          * @method getElWrapper
2216          * @param {String} id the id of the element to get
2217          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2218          * @private
2219          * @deprecated This wrapper isn't that useful
2220          * @static
2221          */
2222         getElWrapper: function(id) {
2223             var oWrapper = this.elementCache[id];
2224             if (!oWrapper || !oWrapper.el) {
2225                 oWrapper = this.elementCache[id] =
2226                     new this.ElementWrapper(Roo.getDom(id));
2227             }
2228             return oWrapper;
2229         },
2230
2231         /**
2232          * Returns the actual DOM element
2233          * @method getElement
2234          * @param {String} id the id of the elment to get
2235          * @return {Object} The element
2236          * @deprecated use Roo.getDom instead
2237          * @static
2238          */
2239         getElement: function(id) {
2240             return Roo.getDom(id);
2241         },
2242
2243         /**
2244          * Returns the style property for the DOM element (i.e.,
2245          * document.getElById(id).style)
2246          * @method getCss
2247          * @param {String} id the id of the elment to get
2248          * @return {Object} The style property of the element
2249          * @deprecated use Roo.getDom instead
2250          * @static
2251          */
2252         getCss: function(id) {
2253             var el = Roo.getDom(id);
2254             return (el) ? el.style : null;
2255         },
2256
2257         /**
2258          * Inner class for cached elements
2259          * @class DragDropMgr.ElementWrapper
2260          * @for DragDropMgr
2261          * @private
2262          * @deprecated
2263          */
2264         ElementWrapper: function(el) {
2265                 /**
2266                  * The element
2267                  * @property el
2268                  */
2269                 this.el = el || null;
2270                 /**
2271                  * The element id
2272                  * @property id
2273                  */
2274                 this.id = this.el && el.id;
2275                 /**
2276                  * A reference to the style property
2277                  * @property css
2278                  */
2279                 this.css = this.el && el.style;
2280             },
2281
2282         /**
2283          * Returns the X position of an html element
2284          * @method getPosX
2285          * @param el the element for which to get the position
2286          * @return {int} the X coordinate
2287          * @for DragDropMgr
2288          * @deprecated use Roo.lib.Dom.getX instead
2289          * @static
2290          */
2291         getPosX: function(el) {
2292             return Roo.lib.Dom.getX(el);
2293         },
2294
2295         /**
2296          * Returns the Y position of an html element
2297          * @method getPosY
2298          * @param el the element for which to get the position
2299          * @return {int} the Y coordinate
2300          * @deprecated use Roo.lib.Dom.getY instead
2301          * @static
2302          */
2303         getPosY: function(el) {
2304             return Roo.lib.Dom.getY(el);
2305         },
2306
2307         /**
2308          * Swap two nodes.  In IE, we use the native method, for others we
2309          * emulate the IE behavior
2310          * @method swapNode
2311          * @param n1 the first node to swap
2312          * @param n2 the other node to swap
2313          * @static
2314          */
2315         swapNode: function(n1, n2) {
2316             if (n1.swapNode) {
2317                 n1.swapNode(n2);
2318             } else {
2319                 var p = n2.parentNode;
2320                 var s = n2.nextSibling;
2321
2322                 if (s == n1) {
2323                     p.insertBefore(n1, n2);
2324                 } else if (n2 == n1.nextSibling) {
2325                     p.insertBefore(n2, n1);
2326                 } else {
2327                     n1.parentNode.replaceChild(n2, n1);
2328                     p.insertBefore(n1, s);
2329                 }
2330             }
2331         },
2332
2333         /**
2334          * Returns the current scroll position
2335          * @method getScroll
2336          * @private
2337          * @static
2338          */
2339         getScroll: function () {
2340             var t, l, dde=document.documentElement, db=document.body;
2341             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2342                 t = dde.scrollTop;
2343                 l = dde.scrollLeft;
2344             } else if (db) {
2345                 t = db.scrollTop;
2346                 l = db.scrollLeft;
2347             } else {
2348
2349             }
2350             return { top: t, left: l };
2351         },
2352
2353         /**
2354          * Returns the specified element style property
2355          * @method getStyle
2356          * @param {HTMLElement} el          the element
2357          * @param {string}      styleProp   the style property
2358          * @return {string} The value of the style property
2359          * @deprecated use Roo.lib.Dom.getStyle
2360          * @static
2361          */
2362         getStyle: function(el, styleProp) {
2363             return Roo.fly(el).getStyle(styleProp);
2364         },
2365
2366         /**
2367          * Gets the scrollTop
2368          * @method getScrollTop
2369          * @return {int} the document's scrollTop
2370          * @static
2371          */
2372         getScrollTop: function () { return this.getScroll().top; },
2373
2374         /**
2375          * Gets the scrollLeft
2376          * @method getScrollLeft
2377          * @return {int} the document's scrollTop
2378          * @static
2379          */
2380         getScrollLeft: function () { return this.getScroll().left; },
2381
2382         /**
2383          * Sets the x/y position of an element to the location of the
2384          * target element.
2385          * @method moveToEl
2386          * @param {HTMLElement} moveEl      The element to move
2387          * @param {HTMLElement} targetEl    The position reference element
2388          * @static
2389          */
2390         moveToEl: function (moveEl, targetEl) {
2391             var aCoord = Roo.lib.Dom.getXY(targetEl);
2392             Roo.lib.Dom.setXY(moveEl, aCoord);
2393         },
2394
2395         /**
2396          * Numeric array sort function
2397          * @method numericSort
2398          * @static
2399          */
2400         numericSort: function(a, b) { return (a - b); },
2401
2402         /**
2403          * Internal counter
2404          * @property _timeoutCount
2405          * @private
2406          * @static
2407          */
2408         _timeoutCount: 0,
2409
2410         /**
2411          * Trying to make the load order less important.  Without this we get
2412          * an error if this file is loaded before the Event Utility.
2413          * @method _addListeners
2414          * @private
2415          * @static
2416          */
2417         _addListeners: function() {
2418             var DDM = Roo.dd.DDM;
2419             if ( Roo.lib.Event && document ) {
2420                 DDM._onLoad();
2421             } else {
2422                 if (DDM._timeoutCount > 2000) {
2423                 } else {
2424                     setTimeout(DDM._addListeners, 10);
2425                     if (document && document.body) {
2426                         DDM._timeoutCount += 1;
2427                     }
2428                 }
2429             }
2430         },
2431
2432         /**
2433          * Recursively searches the immediate parent and all child nodes for
2434          * the handle element in order to determine wheter or not it was
2435          * clicked.
2436          * @method handleWasClicked
2437          * @param node the html element to inspect
2438          * @static
2439          */
2440         handleWasClicked: function(node, id) {
2441             if (this.isHandle(id, node.id)) {
2442                 return true;
2443             } else {
2444                 // check to see if this is a text node child of the one we want
2445                 var p = node.parentNode;
2446
2447                 while (p) {
2448                     if (this.isHandle(id, p.id)) {
2449                         return true;
2450                     } else {
2451                         p = p.parentNode;
2452                     }
2453                 }
2454             }
2455
2456             return false;
2457         }
2458
2459     };
2460
2461 }();
2462
2463 // shorter alias, save a few bytes
2464 Roo.dd.DDM = Roo.dd.DragDropMgr;
2465 Roo.dd.DDM._addListeners();
2466
2467 }/*
2468  * Based on:
2469  * Ext JS Library 1.1.1
2470  * Copyright(c) 2006-2007, Ext JS, LLC.
2471  *
2472  * Originally Released Under LGPL - original licence link has changed is not relivant.
2473  *
2474  * Fork - LGPL
2475  * <script type="text/javascript">
2476  */
2477
2478 /**
2479  * @class Roo.dd.DD
2480  * A DragDrop implementation where the linked element follows the
2481  * mouse cursor during a drag.
2482  * @extends Roo.dd.DragDrop
2483  * @constructor
2484  * @param {String} id the id of the linked element
2485  * @param {String} sGroup the group of related DragDrop items
2486  * @param {object} config an object containing configurable attributes
2487  *                Valid properties for DD:
2488  *                    scroll
2489  */
2490 Roo.dd.DD = function(id, sGroup, config) {
2491     if (id) {
2492         this.init(id, sGroup, config);
2493     }
2494 };
2495
2496 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2497
2498     /**
2499      * When set to true, the utility automatically tries to scroll the browser
2500      * window wehn a drag and drop element is dragged near the viewport boundary.
2501      * Defaults to true.
2502      * @property scroll
2503      * @type boolean
2504      */
2505     scroll: true,
2506
2507     /**
2508      * Sets the pointer offset to the distance between the linked element's top
2509      * left corner and the location the element was clicked
2510      * @method autoOffset
2511      * @param {int} iPageX the X coordinate of the click
2512      * @param {int} iPageY the Y coordinate of the click
2513      */
2514     autoOffset: function(iPageX, iPageY) {
2515         var x = iPageX - this.startPageX;
2516         var y = iPageY - this.startPageY;
2517         this.setDelta(x, y);
2518     },
2519
2520     /**
2521      * Sets the pointer offset.  You can call this directly to force the
2522      * offset to be in a particular location (e.g., pass in 0,0 to set it
2523      * to the center of the object)
2524      * @method setDelta
2525      * @param {int} iDeltaX the distance from the left
2526      * @param {int} iDeltaY the distance from the top
2527      */
2528     setDelta: function(iDeltaX, iDeltaY) {
2529         this.deltaX = iDeltaX;
2530         this.deltaY = iDeltaY;
2531     },
2532
2533     /**
2534      * Sets the drag element to the location of the mousedown or click event,
2535      * maintaining the cursor location relative to the location on the element
2536      * that was clicked.  Override this if you want to place the element in a
2537      * location other than where the cursor is.
2538      * @method setDragElPos
2539      * @param {int} iPageX the X coordinate of the mousedown or drag event
2540      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2541      */
2542     setDragElPos: function(iPageX, iPageY) {
2543         // the first time we do this, we are going to check to make sure
2544         // the element has css positioning
2545
2546         var el = this.getDragEl();
2547         this.alignElWithMouse(el, iPageX, iPageY);
2548     },
2549
2550     /**
2551      * Sets the element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method alignElWithMouse
2556      * @param {HTMLElement} el the element to move
2557      * @param {int} iPageX the X coordinate of the mousedown or drag event
2558      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2559      */
2560     alignElWithMouse: function(el, iPageX, iPageY) {
2561         var oCoord = this.getTargetCoord(iPageX, iPageY);
2562         var fly = el.dom ? el : Roo.fly(el);
2563         if (!this.deltaSetXY) {
2564             var aCoord = [oCoord.x, oCoord.y];
2565             fly.setXY(aCoord);
2566             var newLeft = fly.getLeft(true);
2567             var newTop  = fly.getTop(true);
2568             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2569         } else {
2570             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2571         }
2572
2573         this.cachePosition(oCoord.x, oCoord.y);
2574         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2575         return oCoord;
2576     },
2577
2578     /**
2579      * Saves the most recent position so that we can reset the constraints and
2580      * tick marks on-demand.  We need to know this so that we can calculate the
2581      * number of pixels the element is offset from its original position.
2582      * @method cachePosition
2583      * @param iPageX the current x position (optional, this just makes it so we
2584      * don't have to look it up again)
2585      * @param iPageY the current y position (optional, this just makes it so we
2586      * don't have to look it up again)
2587      */
2588     cachePosition: function(iPageX, iPageY) {
2589         if (iPageX) {
2590             this.lastPageX = iPageX;
2591             this.lastPageY = iPageY;
2592         } else {
2593             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2594             this.lastPageX = aCoord[0];
2595             this.lastPageY = aCoord[1];
2596         }
2597     },
2598
2599     /**
2600      * Auto-scroll the window if the dragged object has been moved beyond the
2601      * visible window boundary.
2602      * @method autoScroll
2603      * @param {int} x the drag element's x position
2604      * @param {int} y the drag element's y position
2605      * @param {int} h the height of the drag element
2606      * @param {int} w the width of the drag element
2607      * @private
2608      */
2609     autoScroll: function(x, y, h, w) {
2610
2611         if (this.scroll) {
2612             // The client height
2613             var clientH = Roo.lib.Dom.getViewWidth();
2614
2615             // The client width
2616             var clientW = Roo.lib.Dom.getViewHeight();
2617
2618             // The amt scrolled down
2619             var st = this.DDM.getScrollTop();
2620
2621             // The amt scrolled right
2622             var sl = this.DDM.getScrollLeft();
2623
2624             // Location of the bottom of the element
2625             var bot = h + y;
2626
2627             // Location of the right of the element
2628             var right = w + x;
2629
2630             // The distance from the cursor to the bottom of the visible area,
2631             // adjusted so that we don't scroll if the cursor is beyond the
2632             // element drag constraints
2633             var toBot = (clientH + st - y - this.deltaY);
2634
2635             // The distance from the cursor to the right of the visible area
2636             var toRight = (clientW + sl - x - this.deltaX);
2637
2638
2639             // How close to the edge the cursor must be before we scroll
2640             // var thresh = (document.all) ? 100 : 40;
2641             var thresh = 40;
2642
2643             // How many pixels to scroll per autoscroll op.  This helps to reduce
2644             // clunky scrolling. IE is more sensitive about this ... it needs this
2645             // value to be higher.
2646             var scrAmt = (document.all) ? 80 : 30;
2647
2648             // Scroll down if we are near the bottom of the visible page and the
2649             // obj extends below the crease
2650             if ( bot > clientH && toBot < thresh ) {
2651                 window.scrollTo(sl, st + scrAmt);
2652             }
2653
2654             // Scroll up if the window is scrolled down and the top of the object
2655             // goes above the top border
2656             if ( y < st && st > 0 && y - st < thresh ) {
2657                 window.scrollTo(sl, st - scrAmt);
2658             }
2659
2660             // Scroll right if the obj is beyond the right border and the cursor is
2661             // near the border.
2662             if ( right > clientW && toRight < thresh ) {
2663                 window.scrollTo(sl + scrAmt, st);
2664             }
2665
2666             // Scroll left if the window has been scrolled to the right and the obj
2667             // extends past the left border
2668             if ( x < sl && sl > 0 && x - sl < thresh ) {
2669                 window.scrollTo(sl - scrAmt, st);
2670             }
2671         }
2672     },
2673
2674     /**
2675      * Finds the location the element should be placed if we want to move
2676      * it to where the mouse location less the click offset would place us.
2677      * @method getTargetCoord
2678      * @param {int} iPageX the X coordinate of the click
2679      * @param {int} iPageY the Y coordinate of the click
2680      * @return an object that contains the coordinates (Object.x and Object.y)
2681      * @private
2682      */
2683     getTargetCoord: function(iPageX, iPageY) {
2684
2685
2686         var x = iPageX - this.deltaX;
2687         var y = iPageY - this.deltaY;
2688
2689         if (this.constrainX) {
2690             if (x < this.minX) { x = this.minX; }
2691             if (x > this.maxX) { x = this.maxX; }
2692         }
2693
2694         if (this.constrainY) {
2695             if (y < this.minY) { y = this.minY; }
2696             if (y > this.maxY) { y = this.maxY; }
2697         }
2698
2699         x = this.getTick(x, this.xTicks);
2700         y = this.getTick(y, this.yTicks);
2701
2702
2703         return {x:x, y:y};
2704     },
2705
2706     /*
2707      * Sets up config options specific to this class. Overrides
2708      * Roo.dd.DragDrop, but all versions of this method through the
2709      * inheritance chain are called
2710      */
2711     applyConfig: function() {
2712         Roo.dd.DD.superclass.applyConfig.call(this);
2713         this.scroll = (this.config.scroll !== false);
2714     },
2715
2716     /*
2717      * Event that fires prior to the onMouseDown event.  Overrides
2718      * Roo.dd.DragDrop.
2719      */
2720     b4MouseDown: function(e) {
2721         // this.resetConstraints();
2722         this.autoOffset(e.getPageX(),
2723                             e.getPageY());
2724     },
2725
2726     /*
2727      * Event that fires prior to the onDrag event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4Drag: function(e) {
2731         this.setDragElPos(e.getPageX(),
2732                             e.getPageY());
2733     },
2734
2735     toString: function() {
2736         return ("DD " + this.id);
2737     }
2738
2739     //////////////////////////////////////////////////////////////////////////
2740     // Debugging ygDragDrop events that can be overridden
2741     //////////////////////////////////////////////////////////////////////////
2742     /*
2743     startDrag: function(x, y) {
2744     },
2745
2746     onDrag: function(e) {
2747     },
2748
2749     onDragEnter: function(e, id) {
2750     },
2751
2752     onDragOver: function(e, id) {
2753     },
2754
2755     onDragOut: function(e, id) {
2756     },
2757
2758     onDragDrop: function(e, id) {
2759     },
2760
2761     endDrag: function(e) {
2762     }
2763
2764     */
2765
2766 });/*
2767  * Based on:
2768  * Ext JS Library 1.1.1
2769  * Copyright(c) 2006-2007, Ext JS, LLC.
2770  *
2771  * Originally Released Under LGPL - original licence link has changed is not relivant.
2772  *
2773  * Fork - LGPL
2774  * <script type="text/javascript">
2775  */
2776
2777 /**
2778  * @class Roo.dd.DDProxy
2779  * A DragDrop implementation that inserts an empty, bordered div into
2780  * the document that follows the cursor during drag operations.  At the time of
2781  * the click, the frame div is resized to the dimensions of the linked html
2782  * element, and moved to the exact location of the linked element.
2783  *
2784  * References to the "frame" element refer to the single proxy element that
2785  * was created to be dragged in place of all DDProxy elements on the
2786  * page.
2787  *
2788  * @extends Roo.dd.DD
2789  * @constructor
2790  * @param {String} id the id of the linked html element
2791  * @param {String} sGroup the group of related DragDrop objects
2792  * @param {object} config an object containing configurable attributes
2793  *                Valid properties for DDProxy in addition to those in DragDrop:
2794  *                   resizeFrame, centerFrame, dragElId
2795  */
2796 Roo.dd.DDProxy = function(id, sGroup, config) {
2797     if (id) {
2798         this.init(id, sGroup, config);
2799         this.initFrame();
2800     }
2801 };
2802
2803 /**
2804  * The default drag frame div id
2805  * @property Roo.dd.DDProxy.dragElId
2806  * @type String
2807  * @static
2808  */
2809 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2810
2811 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2812
2813     /**
2814      * By default we resize the drag frame to be the same size as the element
2815      * we want to drag (this is to get the frame effect).  We can turn it off
2816      * if we want a different behavior.
2817      * @property resizeFrame
2818      * @type boolean
2819      */
2820     resizeFrame: true,
2821
2822     /**
2823      * By default the frame is positioned exactly where the drag element is, so
2824      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2825      * you do not have constraints on the obj is to have the drag frame centered
2826      * around the cursor.  Set centerFrame to true for this effect.
2827      * @property centerFrame
2828      * @type boolean
2829      */
2830     centerFrame: false,
2831
2832     /**
2833      * Creates the proxy element if it does not yet exist
2834      * @method createFrame
2835      */
2836     createFrame: function() {
2837         var self = this;
2838         var body = document.body;
2839
2840         if (!body || !body.firstChild) {
2841             setTimeout( function() { self.createFrame(); }, 50 );
2842             return;
2843         }
2844
2845         var div = this.getDragEl();
2846
2847         if (!div) {
2848             div    = document.createElement("div");
2849             div.id = this.dragElId;
2850             var s  = div.style;
2851
2852             s.position   = "absolute";
2853             s.visibility = "hidden";
2854             s.cursor     = "move";
2855             s.border     = "2px solid #aaa";
2856             s.zIndex     = 999;
2857
2858             // appendChild can blow up IE if invoked prior to the window load event
2859             // while rendering a table.  It is possible there are other scenarios
2860             // that would cause this to happen as well.
2861             body.insertBefore(div, body.firstChild);
2862         }
2863     },
2864
2865     /**
2866      * Initialization for the drag frame element.  Must be called in the
2867      * constructor of all subclasses
2868      * @method initFrame
2869      */
2870     initFrame: function() {
2871         this.createFrame();
2872     },
2873
2874     applyConfig: function() {
2875         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2876
2877         this.resizeFrame = (this.config.resizeFrame !== false);
2878         this.centerFrame = (this.config.centerFrame);
2879         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2880     },
2881
2882     /**
2883      * Resizes the drag frame to the dimensions of the clicked object, positions
2884      * it over the object, and finally displays it
2885      * @method showFrame
2886      * @param {int} iPageX X click position
2887      * @param {int} iPageY Y click position
2888      * @private
2889      */
2890     showFrame: function(iPageX, iPageY) {
2891         var el = this.getEl();
2892         var dragEl = this.getDragEl();
2893         var s = dragEl.style;
2894
2895         this._resizeProxy();
2896
2897         if (this.centerFrame) {
2898             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2899                            Math.round(parseInt(s.height, 10)/2) );
2900         }
2901
2902         this.setDragElPos(iPageX, iPageY);
2903
2904         Roo.fly(dragEl).show();
2905     },
2906
2907     /**
2908      * The proxy is automatically resized to the dimensions of the linked
2909      * element when a drag is initiated, unless resizeFrame is set to false
2910      * @method _resizeProxy
2911      * @private
2912      */
2913     _resizeProxy: function() {
2914         if (this.resizeFrame) {
2915             var el = this.getEl();
2916             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2917         }
2918     },
2919
2920     // overrides Roo.dd.DragDrop
2921     b4MouseDown: function(e) {
2922         var x = e.getPageX();
2923         var y = e.getPageY();
2924         this.autoOffset(x, y);
2925         this.setDragElPos(x, y);
2926     },
2927
2928     // overrides Roo.dd.DragDrop
2929     b4StartDrag: function(x, y) {
2930         // show the drag frame
2931         this.showFrame(x, y);
2932     },
2933
2934     // overrides Roo.dd.DragDrop
2935     b4EndDrag: function(e) {
2936         Roo.fly(this.getDragEl()).hide();
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     // By default we try to move the element to the last location of the frame.
2941     // This is so that the default behavior mirrors that of Roo.dd.DD.
2942     endDrag: function(e) {
2943
2944         var lel = this.getEl();
2945         var del = this.getDragEl();
2946
2947         // Show the drag frame briefly so we can get its position
2948         del.style.visibility = "";
2949
2950         this.beforeMove();
2951         // Hide the linked element before the move to get around a Safari
2952         // rendering bug.
2953         lel.style.visibility = "hidden";
2954         Roo.dd.DDM.moveToEl(lel, del);
2955         del.style.visibility = "hidden";
2956         lel.style.visibility = "";
2957
2958         this.afterDrag();
2959     },
2960
2961     beforeMove : function(){
2962
2963     },
2964
2965     afterDrag : function(){
2966
2967     },
2968
2969     toString: function() {
2970         return ("DDProxy " + this.id);
2971     }
2972
2973 });
2974 /*
2975  * Based on:
2976  * Ext JS Library 1.1.1
2977  * Copyright(c) 2006-2007, Ext JS, LLC.
2978  *
2979  * Originally Released Under LGPL - original licence link has changed is not relivant.
2980  *
2981  * Fork - LGPL
2982  * <script type="text/javascript">
2983  */
2984
2985  /**
2986  * @class Roo.dd.DDTarget
2987  * A DragDrop implementation that does not move, but can be a drop
2988  * target.  You would get the same result by simply omitting implementation
2989  * for the event callbacks, but this way we reduce the processing cost of the
2990  * event listener and the callbacks.
2991  * @extends Roo.dd.DragDrop
2992  * @constructor
2993  * @param {String} id the id of the element that is a drop target
2994  * @param {String} sGroup the group of related DragDrop objects
2995  * @param {object} config an object containing configurable attributes
2996  *                 Valid properties for DDTarget in addition to those in
2997  *                 DragDrop:
2998  *                    none
2999  */
3000 Roo.dd.DDTarget = function(id, sGroup, config) {
3001     if (id) {
3002         this.initTarget(id, sGroup, config);
3003     }
3004     if (config.listeners || config.events) { 
3005        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3006             listeners : config.listeners || {}, 
3007             events : config.events || {} 
3008         });    
3009     }
3010 };
3011
3012 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3013 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3014     toString: function() {
3015         return ("DDTarget " + this.id);
3016     }
3017 });
3018 /*
3019  * Based on:
3020  * Ext JS Library 1.1.1
3021  * Copyright(c) 2006-2007, Ext JS, LLC.
3022  *
3023  * Originally Released Under LGPL - original licence link has changed is not relivant.
3024  *
3025  * Fork - LGPL
3026  * <script type="text/javascript">
3027  */
3028  
3029
3030 /**
3031  * @class Roo.dd.ScrollManager
3032  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3033  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3034  * @singleton
3035  */
3036 Roo.dd.ScrollManager = function(){
3037     var ddm = Roo.dd.DragDropMgr;
3038     var els = {};
3039     var dragEl = null;
3040     var proc = {};
3041     
3042     var onStop = function(e){
3043         dragEl = null;
3044         clearProc();
3045     };
3046     
3047     var triggerRefresh = function(){
3048         if(ddm.dragCurrent){
3049              ddm.refreshCache(ddm.dragCurrent.groups);
3050         }
3051     };
3052     
3053     var doScroll = function(){
3054         if(ddm.dragCurrent){
3055             var dds = Roo.dd.ScrollManager;
3056             if(!dds.animate){
3057                 if(proc.el.scroll(proc.dir, dds.increment)){
3058                     triggerRefresh();
3059                 }
3060             }else{
3061                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3062             }
3063         }
3064     };
3065     
3066     var clearProc = function(){
3067         if(proc.id){
3068             clearInterval(proc.id);
3069         }
3070         proc.id = 0;
3071         proc.el = null;
3072         proc.dir = "";
3073     };
3074     
3075     var startProc = function(el, dir){
3076         clearProc();
3077         proc.el = el;
3078         proc.dir = dir;
3079         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3080     };
3081     
3082     var onFire = function(e, isDrop){
3083         if(isDrop || !ddm.dragCurrent){ return; }
3084         var dds = Roo.dd.ScrollManager;
3085         if(!dragEl || dragEl != ddm.dragCurrent){
3086             dragEl = ddm.dragCurrent;
3087             // refresh regions on drag start
3088             dds.refreshCache();
3089         }
3090         
3091         var xy = Roo.lib.Event.getXY(e);
3092         var pt = new Roo.lib.Point(xy[0], xy[1]);
3093         for(var id in els){
3094             var el = els[id], r = el._region;
3095             if(r && r.contains(pt) && el.isScrollable()){
3096                 if(r.bottom - pt.y <= dds.thresh){
3097                     if(proc.el != el){
3098                         startProc(el, "down");
3099                     }
3100                     return;
3101                 }else if(r.right - pt.x <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "left");
3104                     }
3105                     return;
3106                 }else if(pt.y - r.top <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "up");
3109                     }
3110                     return;
3111                 }else if(pt.x - r.left <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "right");
3114                     }
3115                     return;
3116                 }
3117             }
3118         }
3119         clearProc();
3120     };
3121     
3122     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3123     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3124     
3125     return {
3126         /**
3127          * Registers new overflow element(s) to auto scroll
3128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3129          */
3130         register : function(el){
3131             if(el instanceof Array){
3132                 for(var i = 0, len = el.length; i < len; i++) {
3133                         this.register(el[i]);
3134                 }
3135             }else{
3136                 el = Roo.get(el);
3137                 els[el.id] = el;
3138             }
3139         },
3140         
3141         /**
3142          * Unregisters overflow element(s) so they are no longer scrolled
3143          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3144          */
3145         unregister : function(el){
3146             if(el instanceof Array){
3147                 for(var i = 0, len = el.length; i < len; i++) {
3148                         this.unregister(el[i]);
3149                 }
3150             }else{
3151                 el = Roo.get(el);
3152                 delete els[el.id];
3153             }
3154         },
3155         
3156         /**
3157          * The number of pixels from the edge of a container the pointer needs to be to 
3158          * trigger scrolling (defaults to 25)
3159          * @type Number
3160          */
3161         thresh : 25,
3162         
3163         /**
3164          * The number of pixels to scroll in each scroll increment (defaults to 50)
3165          * @type Number
3166          */
3167         increment : 100,
3168         
3169         /**
3170          * The frequency of scrolls in milliseconds (defaults to 500)
3171          * @type Number
3172          */
3173         frequency : 500,
3174         
3175         /**
3176          * True to animate the scroll (defaults to true)
3177          * @type Boolean
3178          */
3179         animate: true,
3180         
3181         /**
3182          * The animation duration in seconds - 
3183          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3184          * @type Number
3185          */
3186         animDuration: .4,
3187         
3188         /**
3189          * Manually trigger a cache refresh.
3190          */
3191         refreshCache : function(){
3192             for(var id in els){
3193                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3194                     els[id]._region = els[id].getRegion();
3195                 }
3196             }
3197         }
3198     };
3199 }();/*
3200  * Based on:
3201  * Ext JS Library 1.1.1
3202  * Copyright(c) 2006-2007, Ext JS, LLC.
3203  *
3204  * Originally Released Under LGPL - original licence link has changed is not relivant.
3205  *
3206  * Fork - LGPL
3207  * <script type="text/javascript">
3208  */
3209  
3210
3211 /**
3212  * @class Roo.dd.Registry
3213  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3214  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3215  * @singleton
3216  */
3217 Roo.dd.Registry = function(){
3218     var elements = {}; 
3219     var handles = {}; 
3220     var autoIdSeed = 0;
3221
3222     var getId = function(el, autogen){
3223         if(typeof el == "string"){
3224             return el;
3225         }
3226         var id = el.id;
3227         if(!id && autogen !== false){
3228             id = "roodd-" + (++autoIdSeed);
3229             el.id = id;
3230         }
3231         return id;
3232     };
3233     
3234     return {
3235     /**
3236      * Register a drag drop element
3237      * @param {String|HTMLElement} element The id or DOM node to register
3238      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3239      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3240      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3241      * populated in the data object (if applicable):
3242      * <pre>
3243 Value      Description<br />
3244 ---------  ------------------------------------------<br />
3245 handles    Array of DOM nodes that trigger dragging<br />
3246            for the element being registered<br />
3247 isHandle   True if the element passed in triggers<br />
3248            dragging itself, else false
3249 </pre>
3250      */
3251         register : function(el, data){
3252             data = data || {};
3253             if(typeof el == "string"){
3254                 el = document.getElementById(el);
3255             }
3256             data.ddel = el;
3257             elements[getId(el)] = data;
3258             if(data.isHandle !== false){
3259                 handles[data.ddel.id] = data;
3260             }
3261             if(data.handles){
3262                 var hs = data.handles;
3263                 for(var i = 0, len = hs.length; i < len; i++){
3264                         handles[getId(hs[i])] = data;
3265                 }
3266             }
3267         },
3268
3269     /**
3270      * Unregister a drag drop element
3271      * @param {String|HTMLElement}  element The id or DOM node to unregister
3272      */
3273         unregister : function(el){
3274             var id = getId(el, false);
3275             var data = elements[id];
3276             if(data){
3277                 delete elements[id];
3278                 if(data.handles){
3279                     var hs = data.handles;
3280                     for(var i = 0, len = hs.length; i < len; i++){
3281                         delete handles[getId(hs[i], false)];
3282                     }
3283                 }
3284             }
3285         },
3286
3287     /**
3288      * Returns the handle registered for a DOM Node by id
3289      * @param {String|HTMLElement} id The DOM node or id to look up
3290      * @return {Object} handle The custom handle data
3291      */
3292         getHandle : function(id){
3293             if(typeof id != "string"){ // must be element?
3294                 id = id.id;
3295             }
3296             return handles[id];
3297         },
3298
3299     /**
3300      * Returns the handle that is registered for the DOM node that is the target of the event
3301      * @param {Event} e The event
3302      * @return {Object} handle The custom handle data
3303      */
3304         getHandleFromEvent : function(e){
3305             var t = Roo.lib.Event.getTarget(e);
3306             return t ? handles[t.id] : null;
3307         },
3308
3309     /**
3310      * Returns a custom data object that is registered for a DOM node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} data The custom data
3313      */
3314         getTarget : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return elements[id];
3319         },
3320
3321     /**
3322      * Returns a custom data object that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} data The custom data
3325      */
3326         getTargetFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? elements[t.id] || handles[t.id] : null;
3329         }
3330     };
3331 }();/*
3332  * Based on:
3333  * Ext JS Library 1.1.1
3334  * Copyright(c) 2006-2007, Ext JS, LLC.
3335  *
3336  * Originally Released Under LGPL - original licence link has changed is not relivant.
3337  *
3338  * Fork - LGPL
3339  * <script type="text/javascript">
3340  */
3341  
3342
3343 /**
3344  * @class Roo.dd.StatusProxy
3345  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3346  * default drag proxy used by all Roo.dd components.
3347  * @constructor
3348  * @param {Object} config
3349  */
3350 Roo.dd.StatusProxy = function(config){
3351     Roo.apply(this, config);
3352     this.id = this.id || Roo.id();
3353     this.el = new Roo.Layer({
3354         dh: {
3355             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3356                 {tag: "div", cls: "x-dd-drop-icon"},
3357                 {tag: "div", cls: "x-dd-drag-ghost"}
3358             ]
3359         }, 
3360         shadow: !config || config.shadow !== false
3361     });
3362     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3363     this.dropStatus = this.dropNotAllowed;
3364 };
3365
3366 Roo.dd.StatusProxy.prototype = {
3367     /**
3368      * @cfg {String} dropAllowed
3369      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3370      */
3371     dropAllowed : "x-dd-drop-ok",
3372     /**
3373      * @cfg {String} dropNotAllowed
3374      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3375      */
3376     dropNotAllowed : "x-dd-drop-nodrop",
3377
3378     /**
3379      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3380      * over the current target element.
3381      * @param {String} cssClass The css class for the new drop status indicator image
3382      */
3383     setStatus : function(cssClass){
3384         cssClass = cssClass || this.dropNotAllowed;
3385         if(this.dropStatus != cssClass){
3386             this.el.replaceClass(this.dropStatus, cssClass);
3387             this.dropStatus = cssClass;
3388         }
3389     },
3390
3391     /**
3392      * Resets the status indicator to the default dropNotAllowed value
3393      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3394      */
3395     reset : function(clearGhost){
3396         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3397         this.dropStatus = this.dropNotAllowed;
3398         if(clearGhost){
3399             this.ghost.update("");
3400         }
3401     },
3402
3403     /**
3404      * Updates the contents of the ghost element
3405      * @param {String} html The html that will replace the current innerHTML of the ghost element
3406      */
3407     update : function(html){
3408         if(typeof html == "string"){
3409             this.ghost.update(html);
3410         }else{
3411             this.ghost.update("");
3412             html.style.margin = "0";
3413             this.ghost.dom.appendChild(html);
3414         }
3415         // ensure float = none set?? cant remember why though.
3416         var el = this.ghost.dom.firstChild;
3417                 if(el){
3418                         Roo.fly(el).setStyle('float', 'none');
3419                 }
3420     },
3421     
3422     /**
3423      * Returns the underlying proxy {@link Roo.Layer}
3424      * @return {Roo.Layer} el
3425     */
3426     getEl : function(){
3427         return this.el;
3428     },
3429
3430     /**
3431      * Returns the ghost element
3432      * @return {Roo.Element} el
3433      */
3434     getGhost : function(){
3435         return this.ghost;
3436     },
3437
3438     /**
3439      * Hides the proxy
3440      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3441      */
3442     hide : function(clear){
3443         this.el.hide();
3444         if(clear){
3445             this.reset(true);
3446         }
3447     },
3448
3449     /**
3450      * Stops the repair animation if it's currently running
3451      */
3452     stop : function(){
3453         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3454             this.anim.stop();
3455         }
3456     },
3457
3458     /**
3459      * Displays this proxy
3460      */
3461     show : function(){
3462         this.el.show();
3463     },
3464
3465     /**
3466      * Force the Layer to sync its shadow and shim positions to the element
3467      */
3468     sync : function(){
3469         this.el.sync();
3470     },
3471
3472     /**
3473      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3474      * invalid drop operation by the item being dragged.
3475      * @param {Array} xy The XY position of the element ([x, y])
3476      * @param {Function} callback The function to call after the repair is complete
3477      * @param {Object} scope The scope in which to execute the callback
3478      */
3479     repair : function(xy, callback, scope){
3480         this.callback = callback;
3481         this.scope = scope;
3482         if(xy && this.animRepair !== false){
3483             this.el.addClass("x-dd-drag-repair");
3484             this.el.hideUnders(true);
3485             this.anim = this.el.shift({
3486                 duration: this.repairDuration || .5,
3487                 easing: 'easeOut',
3488                 xy: xy,
3489                 stopFx: true,
3490                 callback: this.afterRepair,
3491                 scope: this
3492             });
3493         }else{
3494             this.afterRepair();
3495         }
3496     },
3497
3498     // private
3499     afterRepair : function(){
3500         this.hide(true);
3501         if(typeof this.callback == "function"){
3502             this.callback.call(this.scope || this);
3503         }
3504         this.callback = null;
3505         this.scope = null;
3506     }
3507 };/*
3508  * Based on:
3509  * Ext JS Library 1.1.1
3510  * Copyright(c) 2006-2007, Ext JS, LLC.
3511  *
3512  * Originally Released Under LGPL - original licence link has changed is not relivant.
3513  *
3514  * Fork - LGPL
3515  * <script type="text/javascript">
3516  */
3517
3518 /**
3519  * @class Roo.dd.DragSource
3520  * @extends Roo.dd.DDProxy
3521  * A simple class that provides the basic implementation needed to make any element draggable.
3522  * @constructor
3523  * @param {String/HTMLElement/Element} el The container element
3524  * @param {Object} config
3525  */
3526 Roo.dd.DragSource = function(el, config){
3527     this.el = Roo.get(el);
3528     this.dragData = {};
3529     
3530     Roo.apply(this, config);
3531     
3532     if(!this.proxy){
3533         this.proxy = new Roo.dd.StatusProxy();
3534     }
3535
3536     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3537           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3538     
3539     this.dragging = false;
3540 };
3541
3542 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3543     /**
3544      * @cfg {String} dropAllowed
3545      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3546      */
3547     dropAllowed : "x-dd-drop-ok",
3548     /**
3549      * @cfg {String} dropNotAllowed
3550      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3551      */
3552     dropNotAllowed : "x-dd-drop-nodrop",
3553
3554     /**
3555      * Returns the data object associated with this drag source
3556      * @return {Object} data An object containing arbitrary data
3557      */
3558     getDragData : function(e){
3559         return this.dragData;
3560     },
3561
3562     // private
3563     onDragEnter : function(e, id){
3564         var target = Roo.dd.DragDropMgr.getDDById(id);
3565         this.cachedTarget = target;
3566         if(this.beforeDragEnter(target, e, id) !== false){
3567             if(target.isNotifyTarget){
3568                 var status = target.notifyEnter(this, e, this.dragData);
3569                 this.proxy.setStatus(status);
3570             }else{
3571                 this.proxy.setStatus(this.dropAllowed);
3572             }
3573             
3574             if(this.afterDragEnter){
3575                 /**
3576                  * An empty function by default, but provided so that you can perform a custom action
3577                  * when the dragged item enters the drop target by providing an implementation.
3578                  * @param {Roo.dd.DragDrop} target The drop target
3579                  * @param {Event} e The event object
3580                  * @param {String} id The id of the dragged element
3581                  * @method afterDragEnter
3582                  */
3583                 this.afterDragEnter(target, e, id);
3584             }
3585         }
3586     },
3587
3588     /**
3589      * An empty function by default, but provided so that you can perform a custom action
3590      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3591      * @param {Roo.dd.DragDrop} target The drop target
3592      * @param {Event} e The event object
3593      * @param {String} id The id of the dragged element
3594      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3595      */
3596     beforeDragEnter : function(target, e, id){
3597         return true;
3598     },
3599
3600     // private
3601     alignElWithMouse: function() {
3602         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3603         this.proxy.sync();
3604     },
3605
3606     // private
3607     onDragOver : function(e, id){
3608         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3609         if(this.beforeDragOver(target, e, id) !== false){
3610             if(target.isNotifyTarget){
3611                 var status = target.notifyOver(this, e, this.dragData);
3612                 this.proxy.setStatus(status);
3613             }
3614
3615             if(this.afterDragOver){
3616                 /**
3617                  * An empty function by default, but provided so that you can perform a custom action
3618                  * while the dragged item is over the drop target by providing an implementation.
3619                  * @param {Roo.dd.DragDrop} target The drop target
3620                  * @param {Event} e The event object
3621                  * @param {String} id The id of the dragged element
3622                  * @method afterDragOver
3623                  */
3624                 this.afterDragOver(target, e, id);
3625             }
3626         }
3627     },
3628
3629     /**
3630      * An empty function by default, but provided so that you can perform a custom action
3631      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3632      * @param {Roo.dd.DragDrop} target The drop target
3633      * @param {Event} e The event object
3634      * @param {String} id The id of the dragged element
3635      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3636      */
3637     beforeDragOver : function(target, e, id){
3638         return true;
3639     },
3640
3641     // private
3642     onDragOut : function(e, id){
3643         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3644         if(this.beforeDragOut(target, e, id) !== false){
3645             if(target.isNotifyTarget){
3646                 target.notifyOut(this, e, this.dragData);
3647             }
3648             this.proxy.reset();
3649             if(this.afterDragOut){
3650                 /**
3651                  * An empty function by default, but provided so that you can perform a custom action
3652                  * after the dragged item is dragged out of the target without dropping.
3653                  * @param {Roo.dd.DragDrop} target The drop target
3654                  * @param {Event} e The event object
3655                  * @param {String} id The id of the dragged element
3656                  * @method afterDragOut
3657                  */
3658                 this.afterDragOut(target, e, id);
3659             }
3660         }
3661         this.cachedTarget = null;
3662     },
3663
3664     /**
3665      * An empty function by default, but provided so that you can perform a custom action before the dragged
3666      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3667      * @param {Roo.dd.DragDrop} target The drop target
3668      * @param {Event} e The event object
3669      * @param {String} id The id of the dragged element
3670      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3671      */
3672     beforeDragOut : function(target, e, id){
3673         return true;
3674     },
3675     
3676     // private
3677     onDragDrop : function(e, id){
3678         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3679         if(this.beforeDragDrop(target, e, id) !== false){
3680             if(target.isNotifyTarget){
3681                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3682                     this.onValidDrop(target, e, id);
3683                 }else{
3684                     this.onInvalidDrop(target, e, id);
3685                 }
3686             }else{
3687                 this.onValidDrop(target, e, id);
3688             }
3689             
3690             if(this.afterDragDrop){
3691                 /**
3692                  * An empty function by default, but provided so that you can perform a custom action
3693                  * after a valid drag drop has occurred by providing an implementation.
3694                  * @param {Roo.dd.DragDrop} target The drop target
3695                  * @param {Event} e The event object
3696                  * @param {String} id The id of the dropped element
3697                  * @method afterDragDrop
3698                  */
3699                 this.afterDragDrop(target, e, id);
3700             }
3701         }
3702         delete this.cachedTarget;
3703     },
3704
3705     /**
3706      * An empty function by default, but provided so that you can perform a custom action before the dragged
3707      * item is dropped onto the target and optionally cancel the onDragDrop.
3708      * @param {Roo.dd.DragDrop} target The drop target
3709      * @param {Event} e The event object
3710      * @param {String} id The id of the dragged element
3711      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3712      */
3713     beforeDragDrop : function(target, e, id){
3714         return true;
3715     },
3716
3717     // private
3718     onValidDrop : function(target, e, id){
3719         this.hideProxy();
3720         if(this.afterValidDrop){
3721             /**
3722              * An empty function by default, but provided so that you can perform a custom action
3723              * after a valid drop has occurred by providing an implementation.
3724              * @param {Object} target The target DD 
3725              * @param {Event} e The event object
3726              * @param {String} id The id of the dropped element
3727              * @method afterInvalidDrop
3728              */
3729             this.afterValidDrop(target, e, id);
3730         }
3731     },
3732
3733     // private
3734     getRepairXY : function(e, data){
3735         return this.el.getXY();  
3736     },
3737
3738     // private
3739     onInvalidDrop : function(target, e, id){
3740         this.beforeInvalidDrop(target, e, id);
3741         if(this.cachedTarget){
3742             if(this.cachedTarget.isNotifyTarget){
3743                 this.cachedTarget.notifyOut(this, e, this.dragData);
3744             }
3745             this.cacheTarget = null;
3746         }
3747         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3748
3749         if(this.afterInvalidDrop){
3750             /**
3751              * An empty function by default, but provided so that you can perform a custom action
3752              * after an invalid drop has occurred by providing an implementation.
3753              * @param {Event} e The event object
3754              * @param {String} id The id of the dropped element
3755              * @method afterInvalidDrop
3756              */
3757             this.afterInvalidDrop(e, id);
3758         }
3759     },
3760
3761     // private
3762     afterRepair : function(){
3763         if(Roo.enableFx){
3764             this.el.highlight(this.hlColor || "c3daf9");
3765         }
3766         this.dragging = false;
3767     },
3768
3769     /**
3770      * An empty function by default, but provided so that you can perform a custom action after an invalid
3771      * drop has occurred.
3772      * @param {Roo.dd.DragDrop} target The drop target
3773      * @param {Event} e The event object
3774      * @param {String} id The id of the dragged element
3775      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3776      */
3777     beforeInvalidDrop : function(target, e, id){
3778         return true;
3779     },
3780
3781     // private
3782     handleMouseDown : function(e){
3783         if(this.dragging) {
3784             return;
3785         }
3786         var data = this.getDragData(e);
3787         if(data && this.onBeforeDrag(data, e) !== false){
3788             this.dragData = data;
3789             this.proxy.stop();
3790             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3791         } 
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action before the initial
3796      * drag event begins and optionally cancel it.
3797      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3798      * @param {Event} e The event object
3799      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3800      */
3801     onBeforeDrag : function(data, e){
3802         return true;
3803     },
3804
3805     /**
3806      * An empty function by default, but provided so that you can perform a custom action once the initial
3807      * drag event has begun.  The drag cannot be canceled from this function.
3808      * @param {Number} x The x position of the click on the dragged object
3809      * @param {Number} y The y position of the click on the dragged object
3810      */
3811     onStartDrag : Roo.emptyFn,
3812
3813     // private - YUI override
3814     startDrag : function(x, y){
3815         this.proxy.reset();
3816         this.dragging = true;
3817         this.proxy.update("");
3818         this.onInitDrag(x, y);
3819         this.proxy.show();
3820     },
3821
3822     // private
3823     onInitDrag : function(x, y){
3824         var clone = this.el.dom.cloneNode(true);
3825         clone.id = Roo.id(); // prevent duplicate ids
3826         this.proxy.update(clone);
3827         this.onStartDrag(x, y);
3828         return true;
3829     },
3830
3831     /**
3832      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3833      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3834      */
3835     getProxy : function(){
3836         return this.proxy;  
3837     },
3838
3839     /**
3840      * Hides the drag source's {@link Roo.dd.StatusProxy}
3841      */
3842     hideProxy : function(){
3843         this.proxy.hide();  
3844         this.proxy.reset(true);
3845         this.dragging = false;
3846     },
3847
3848     // private
3849     triggerCacheRefresh : function(){
3850         Roo.dd.DDM.refreshCache(this.groups);
3851     },
3852
3853     // private - override to prevent hiding
3854     b4EndDrag: function(e) {
3855     },
3856
3857     // private - override to prevent moving
3858     endDrag : function(e){
3859         this.onEndDrag(this.dragData, e);
3860     },
3861
3862     // private
3863     onEndDrag : function(data, e){
3864     },
3865     
3866     // private - pin to cursor
3867     autoOffset : function(x, y) {
3868         this.setDelta(-12, -20);
3869     }    
3870 });/*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880
3881
3882 /**
3883  * @class Roo.dd.DropTarget
3884  * @extends Roo.dd.DDTarget
3885  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3886  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3887  * @constructor
3888  * @param {String/HTMLElement/Element} el The container element
3889  * @param {Object} config
3890  */
3891 Roo.dd.DropTarget = function(el, config){
3892     this.el = Roo.get(el);
3893     
3894     var listeners = false; ;
3895     if (config && config.listeners) {
3896         listeners= config.listeners;
3897         delete config.listeners;
3898     }
3899     Roo.apply(this, config);
3900     
3901     if(this.containerScroll){
3902         Roo.dd.ScrollManager.register(this.el);
3903     }
3904     this.addEvents( {
3905          /**
3906          * @scope Roo.dd.DropTarget
3907          */
3908          
3909          /**
3910          * @event enter
3911          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3912          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3913          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3914          * 
3915          * IMPORTANT : it should set this.overClass and this.dropAllowed
3916          * 
3917          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3918          * @param {Event} e The event
3919          * @param {Object} data An object containing arbitrary data supplied by the drag source
3920          */
3921         "enter" : true,
3922         
3923          /**
3924          * @event over
3925          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3926          * This method will be called on every mouse movement while the drag source is over the drop target.
3927          * This default implementation simply returns the dropAllowed config value.
3928          * 
3929          * IMPORTANT : it should set this.dropAllowed
3930          * 
3931          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3932          * @param {Event} e The event
3933          * @param {Object} data An object containing arbitrary data supplied by the drag source
3934          
3935          */
3936         "over" : true,
3937         /**
3938          * @event out
3939          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3940          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3941          * overClass (if any) from the drop element.
3942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3943          * @param {Event} e The event
3944          * @param {Object} data An object containing arbitrary data supplied by the drag source
3945          */
3946          "out" : true,
3947          
3948         /**
3949          * @event drop
3950          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3951          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3952          * implementation that does something to process the drop event and returns true so that the drag source's
3953          * repair action does not run.
3954          * 
3955          * IMPORTANT : it should set this.success
3956          * 
3957          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3958          * @param {Event} e The event
3959          * @param {Object} data An object containing arbitrary data supplied by the drag source
3960         */
3961          "drop" : true
3962     });
3963             
3964      
3965     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3966         this.el.dom, 
3967         this.ddGroup || this.group,
3968         {
3969             isTarget: true,
3970             listeners : listeners || {} 
3971            
3972         
3973         }
3974     );
3975
3976 };
3977
3978 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3979     /**
3980      * @cfg {String} overClass
3981      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3982      */
3983      /**
3984      * @cfg {String} ddGroup
3985      * The drag drop group to handle drop events for
3986      */
3987      
3988     /**
3989      * @cfg {String} dropAllowed
3990      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3991      */
3992     dropAllowed : "x-dd-drop-ok",
3993     /**
3994      * @cfg {String} dropNotAllowed
3995      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3996      */
3997     dropNotAllowed : "x-dd-drop-nodrop",
3998     /**
3999      * @cfg {boolean} success
4000      * set this after drop listener.. 
4001      */
4002     success : false,
4003     /**
4004      * @cfg {boolean|String} valid true/false or string (add/sub/ok/nodrop)
4005      * if the drop point is valid for over/enter..
4006      */
4007     valid : false,
4008     // private
4009     isTarget : true,
4010
4011     // private
4012     isNotifyTarget : true,
4013     
4014     /**
4015      * @hide
4016      */
4017     notifyEnter : function(dd, e, data){
4018         this.valid = true;
4019         this.fireEvent('enter', this, dd, e, data);
4020         if(this.overClass){
4021             this.el.addClass(this.overClass);
4022         }
4023         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4024             this.valid ? this.dropAllowed : this.dropNotAllowed
4025         );
4026     },
4027
4028     /**
4029      * @hide
4030      */
4031     notifyOver : function(dd, e, data){
4032         this.valid = true;
4033         this.fireEvent('over', this, dd, e, data);
4034         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4035             this.valid ? this.dropAllowed : this.dropNotAllowed
4036         );
4037     },
4038
4039     /**
4040      * @hide
4041      */
4042     notifyOut : function(dd, e, data){
4043         this.fireEvent('out', this, dd, e, data);
4044         if(this.overClass){
4045             this.el.removeClass(this.overClass);
4046         }
4047     },
4048
4049     /**
4050      * @hide
4051      */
4052     notifyDrop : function(dd, e, data){
4053         this.success = false;
4054         this.fireEvent('drop', this, dd, e, data);
4055         return this.success;
4056     }
4057 });/*
4058  * Based on:
4059  * Ext JS Library 1.1.1
4060  * Copyright(c) 2006-2007, Ext JS, LLC.
4061  *
4062  * Originally Released Under LGPL - original licence link has changed is not relivant.
4063  *
4064  * Fork - LGPL
4065  * <script type="text/javascript">
4066  */
4067
4068
4069 /**
4070  * @class Roo.dd.DragZone
4071  * @extends Roo.dd.DragSource
4072  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4073  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4074  * @constructor
4075  * @param {String/HTMLElement/Element} el The container element
4076  * @param {Object} config
4077  */
4078 Roo.dd.DragZone = function(el, config){
4079     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4080     if(this.containerScroll){
4081         Roo.dd.ScrollManager.register(this.el);
4082     }
4083 };
4084
4085 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4086     /**
4087      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4088      * for auto scrolling during drag operations.
4089      */
4090     /**
4091      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4092      * method after a failed drop (defaults to "c3daf9" - light blue)
4093      */
4094
4095     /**
4096      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4097      * for a valid target to drag based on the mouse down. Override this method
4098      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4099      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4100      * @param {EventObject} e The mouse down event
4101      * @return {Object} The dragData
4102      */
4103     getDragData : function(e){
4104         return Roo.dd.Registry.getHandleFromEvent(e);
4105     },
4106     
4107     /**
4108      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4109      * this.dragData.ddel
4110      * @param {Number} x The x position of the click on the dragged object
4111      * @param {Number} y The y position of the click on the dragged object
4112      * @return {Boolean} true to continue the drag, false to cancel
4113      */
4114     onInitDrag : function(x, y){
4115         this.proxy.update(this.dragData.ddel.cloneNode(true));
4116         this.onStartDrag(x, y);
4117         return true;
4118     },
4119     
4120     /**
4121      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4122      */
4123     afterRepair : function(){
4124         if(Roo.enableFx){
4125             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4126         }
4127         this.dragging = false;
4128     },
4129
4130     /**
4131      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4132      * the XY of this.dragData.ddel
4133      * @param {EventObject} e The mouse up event
4134      * @return {Array} The xy location (e.g. [100, 200])
4135      */
4136     getRepairXY : function(e){
4137         return Roo.Element.fly(this.dragData.ddel).getXY();  
4138     }
4139 });/*
4140  * Based on:
4141  * Ext JS Library 1.1.1
4142  * Copyright(c) 2006-2007, Ext JS, LLC.
4143  *
4144  * Originally Released Under LGPL - original licence link has changed is not relivant.
4145  *
4146  * Fork - LGPL
4147  * <script type="text/javascript">
4148  */
4149 /**
4150  * @class Roo.dd.DropZone
4151  * @extends Roo.dd.DropTarget
4152  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4153  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4154  * @constructor
4155  * @param {String/HTMLElement/Element} el The container element
4156  * @param {Object} config
4157  */
4158 Roo.dd.DropZone = function(el, config){
4159     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4160 };
4161
4162 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4163     /**
4164      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4165      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4166      * provide your own custom lookup.
4167      * @param {Event} e The event
4168      * @return {Object} data The custom data
4169      */
4170     getTargetFromEvent : function(e){
4171         return Roo.dd.Registry.getTargetFromEvent(e);
4172     },
4173
4174     /**
4175      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4176      * that it has registered.  This method has no default implementation and should be overridden to provide
4177      * node-specific processing if necessary.
4178      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4179      * {@link #getTargetFromEvent} for this node)
4180      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4181      * @param {Event} e The event
4182      * @param {Object} data An object containing arbitrary data supplied by the drag source
4183      */
4184     onNodeEnter : function(n, dd, e, data){
4185         
4186     },
4187
4188     /**
4189      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4190      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4191      * overridden to provide the proper feedback.
4192      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4193      * {@link #getTargetFromEvent} for this node)
4194      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4195      * @param {Event} e The event
4196      * @param {Object} data An object containing arbitrary data supplied by the drag source
4197      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4198      * underlying {@link Roo.dd.StatusProxy} can be updated
4199      */
4200     onNodeOver : function(n, dd, e, data){
4201         return this.dropAllowed;
4202     },
4203
4204     /**
4205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4206      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4207      * node-specific processing if necessary.
4208      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4209      * {@link #getTargetFromEvent} for this node)
4210      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4211      * @param {Event} e The event
4212      * @param {Object} data An object containing arbitrary data supplied by the drag source
4213      */
4214     onNodeOut : function(n, dd, e, data){
4215         
4216     },
4217
4218     /**
4219      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4220      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4221      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4222      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4223      * {@link #getTargetFromEvent} for this node)
4224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4225      * @param {Event} e The event
4226      * @param {Object} data An object containing arbitrary data supplied by the drag source
4227      * @return {Boolean} True if the drop was valid, else false
4228      */
4229     onNodeDrop : function(n, dd, e, data){
4230         return false;
4231     },
4232
4233     /**
4234      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4235      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4236      * it should be overridden to provide the proper feedback if necessary.
4237      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4238      * @param {Event} e The event
4239      * @param {Object} data An object containing arbitrary data supplied by the drag source
4240      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4241      * underlying {@link Roo.dd.StatusProxy} can be updated
4242      */
4243     onContainerOver : function(dd, e, data){
4244         return this.dropNotAllowed;
4245     },
4246
4247     /**
4248      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4249      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4250      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4251      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4253      * @param {Event} e The event
4254      * @param {Object} data An object containing arbitrary data supplied by the drag source
4255      * @return {Boolean} True if the drop was valid, else false
4256      */
4257     onContainerDrop : function(dd, e, data){
4258         return false;
4259     },
4260
4261     /**
4262      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4263      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4264      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4265      * you should override this method and provide a custom implementation.
4266      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4267      * @param {Event} e The event
4268      * @param {Object} data An object containing arbitrary data supplied by the drag source
4269      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4270      * underlying {@link Roo.dd.StatusProxy} can be updated
4271      */
4272     notifyEnter : function(dd, e, data){
4273         return this.dropNotAllowed;
4274     },
4275
4276     /**
4277      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4278      * This method will be called on every mouse movement while the drag source is over the drop zone.
4279      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4280      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4281      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4282      * registered node, it will call {@link #onContainerOver}.
4283      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4284      * @param {Event} e The event
4285      * @param {Object} data An object containing arbitrary data supplied by the drag source
4286      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4287      * underlying {@link Roo.dd.StatusProxy} can be updated
4288      */
4289     notifyOver : function(dd, e, data){
4290         var n = this.getTargetFromEvent(e);
4291         if(!n){ // not over valid drop target
4292             if(this.lastOverNode){
4293                 this.onNodeOut(this.lastOverNode, dd, e, data);
4294                 this.lastOverNode = null;
4295             }
4296             return this.onContainerOver(dd, e, data);
4297         }
4298         if(this.lastOverNode != n){
4299             if(this.lastOverNode){
4300                 this.onNodeOut(this.lastOverNode, dd, e, data);
4301             }
4302             this.onNodeEnter(n, dd, e, data);
4303             this.lastOverNode = n;
4304         }
4305         return this.onNodeOver(n, dd, e, data);
4306     },
4307
4308     /**
4309      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4310      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4311      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4312      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4313      * @param {Event} e The event
4314      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4315      */
4316     notifyOut : function(dd, e, data){
4317         if(this.lastOverNode){
4318             this.onNodeOut(this.lastOverNode, dd, e, data);
4319             this.lastOverNode = null;
4320         }
4321     },
4322
4323     /**
4324      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4325      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4326      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4327      * otherwise it will call {@link #onContainerDrop}.
4328      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4329      * @param {Event} e The event
4330      * @param {Object} data An object containing arbitrary data supplied by the drag source
4331      * @return {Boolean} True if the drop was valid, else false
4332      */
4333     notifyDrop : function(dd, e, data){
4334         if(this.lastOverNode){
4335             this.onNodeOut(this.lastOverNode, dd, e, data);
4336             this.lastOverNode = null;
4337         }
4338         var n = this.getTargetFromEvent(e);
4339         return n ?
4340             this.onNodeDrop(n, dd, e, data) :
4341             this.onContainerDrop(dd, e, data);
4342     },
4343
4344     // private
4345     triggerCacheRefresh : function(){
4346         Roo.dd.DDM.refreshCache(this.groups);
4347     }  
4348 });/*
4349  * Based on:
4350  * Ext JS Library 1.1.1
4351  * Copyright(c) 2006-2007, Ext JS, LLC.
4352  *
4353  * Originally Released Under LGPL - original licence link has changed is not relivant.
4354  *
4355  * Fork - LGPL
4356  * <script type="text/javascript">
4357  */
4358
4359
4360 /**
4361  * @class Roo.data.SortTypes
4362  * @singleton
4363  * Defines the default sorting (casting?) comparison functions used when sorting data.
4364  */
4365 Roo.data.SortTypes = {
4366     /**
4367      * Default sort that does nothing
4368      * @param {Mixed} s The value being converted
4369      * @return {Mixed} The comparison value
4370      */
4371     none : function(s){
4372         return s;
4373     },
4374     
4375     /**
4376      * The regular expression used to strip tags
4377      * @type {RegExp}
4378      * @property
4379      */
4380     stripTagsRE : /<\/?[^>]+>/gi,
4381     
4382     /**
4383      * Strips all HTML tags to sort on text only
4384      * @param {Mixed} s The value being converted
4385      * @return {String} The comparison value
4386      */
4387     asText : function(s){
4388         return String(s).replace(this.stripTagsRE, "");
4389     },
4390     
4391     /**
4392      * Strips all HTML tags to sort on text only - Case insensitive
4393      * @param {Mixed} s The value being converted
4394      * @return {String} The comparison value
4395      */
4396     asUCText : function(s){
4397         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4398     },
4399     
4400     /**
4401      * Case insensitive string
4402      * @param {Mixed} s The value being converted
4403      * @return {String} The comparison value
4404      */
4405     asUCString : function(s) {
4406         return String(s).toUpperCase();
4407     },
4408     
4409     /**
4410      * Date sorting
4411      * @param {Mixed} s The value being converted
4412      * @return {Number} The comparison value
4413      */
4414     asDate : function(s) {
4415         if(!s){
4416             return 0;
4417         }
4418         if(s instanceof Date){
4419             return s.getTime();
4420         }
4421         return Date.parse(String(s));
4422     },
4423     
4424     /**
4425      * Float sorting
4426      * @param {Mixed} s The value being converted
4427      * @return {Float} The comparison value
4428      */
4429     asFloat : function(s) {
4430         var val = parseFloat(String(s).replace(/,/g, ""));
4431         if(isNaN(val)) val = 0;
4432         return val;
4433     },
4434     
4435     /**
4436      * Integer sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Number} The comparison value
4439      */
4440     asInt : function(s) {
4441         var val = parseInt(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     }
4445 };/*
4446  * Based on:
4447  * Ext JS Library 1.1.1
4448  * Copyright(c) 2006-2007, Ext JS, LLC.
4449  *
4450  * Originally Released Under LGPL - original licence link has changed is not relivant.
4451  *
4452  * Fork - LGPL
4453  * <script type="text/javascript">
4454  */
4455
4456 /**
4457 * @class Roo.data.Record
4458  * Instances of this class encapsulate both record <em>definition</em> information, and record
4459  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4460  * to access Records cached in an {@link Roo.data.Store} object.<br>
4461  * <p>
4462  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4463  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4464  * objects.<br>
4465  * <p>
4466  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4467  * @constructor
4468  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4469  * {@link #create}. The parameters are the same.
4470  * @param {Array} data An associative Array of data values keyed by the field name.
4471  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4472  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4473  * not specified an integer id is generated.
4474  */
4475 Roo.data.Record = function(data, id){
4476     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4477     this.data = data;
4478 };
4479
4480 /**
4481  * Generate a constructor for a specific record layout.
4482  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4483  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4484  * Each field definition object may contain the following properties: <ul>
4485  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4486  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4487  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4488  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4489  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4490  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4491  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4492  * this may be omitted.</p></li>
4493  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4494  * <ul><li>auto (Default, implies no conversion)</li>
4495  * <li>string</li>
4496  * <li>int</li>
4497  * <li>float</li>
4498  * <li>boolean</li>
4499  * <li>date</li></ul></p></li>
4500  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4501  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4502  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4503  * by the Reader into an object that will be stored in the Record. It is passed the
4504  * following parameters:<ul>
4505  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4506  * </ul></p></li>
4507  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4508  * </ul>
4509  * <br>usage:<br><pre><code>
4510 var TopicRecord = Roo.data.Record.create(
4511     {name: 'title', mapping: 'topic_title'},
4512     {name: 'author', mapping: 'username'},
4513     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4514     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4515     {name: 'lastPoster', mapping: 'user2'},
4516     {name: 'excerpt', mapping: 'post_text'}
4517 );
4518
4519 var myNewRecord = new TopicRecord({
4520     title: 'Do my job please',
4521     author: 'noobie',
4522     totalPosts: 1,
4523     lastPost: new Date(),
4524     lastPoster: 'Animal',
4525     excerpt: 'No way dude!'
4526 });
4527 myStore.add(myNewRecord);
4528 </code></pre>
4529  * @method create
4530  * @static
4531  */
4532 Roo.data.Record.create = function(o){
4533     var f = function(){
4534         f.superclass.constructor.apply(this, arguments);
4535     };
4536     Roo.extend(f, Roo.data.Record);
4537     var p = f.prototype;
4538     p.fields = new Roo.util.MixedCollection(false, function(field){
4539         return field.name;
4540     });
4541     for(var i = 0, len = o.length; i < len; i++){
4542         p.fields.add(new Roo.data.Field(o[i]));
4543     }
4544     f.getField = function(name){
4545         return p.fields.get(name);  
4546     };
4547     return f;
4548 };
4549
4550 Roo.data.Record.AUTO_ID = 1000;
4551 Roo.data.Record.EDIT = 'edit';
4552 Roo.data.Record.REJECT = 'reject';
4553 Roo.data.Record.COMMIT = 'commit';
4554
4555 Roo.data.Record.prototype = {
4556     /**
4557      * Readonly flag - true if this record has been modified.
4558      * @type Boolean
4559      */
4560     dirty : false,
4561     editing : false,
4562     error: null,
4563     modified: null,
4564
4565     // private
4566     join : function(store){
4567         this.store = store;
4568     },
4569
4570     /**
4571      * Set the named field to the specified value.
4572      * @param {String} name The name of the field to set.
4573      * @param {Object} value The value to set the field to.
4574      */
4575     set : function(name, value){
4576         if(this.data[name] == value){
4577             return;
4578         }
4579         this.dirty = true;
4580         if(!this.modified){
4581             this.modified = {};
4582         }
4583         if(typeof this.modified[name] == 'undefined'){
4584             this.modified[name] = this.data[name];
4585         }
4586         this.data[name] = value;
4587         if(!this.editing){
4588             this.store.afterEdit(this);
4589         }       
4590     },
4591
4592     /**
4593      * Get the value of the named field.
4594      * @param {String} name The name of the field to get the value of.
4595      * @return {Object} The value of the field.
4596      */
4597     get : function(name){
4598         return this.data[name]; 
4599     },
4600
4601     // private
4602     beginEdit : function(){
4603         this.editing = true;
4604         this.modified = {}; 
4605     },
4606
4607     // private
4608     cancelEdit : function(){
4609         this.editing = false;
4610         delete this.modified;
4611     },
4612
4613     // private
4614     endEdit : function(){
4615         this.editing = false;
4616         if(this.dirty && this.store){
4617             this.store.afterEdit(this);
4618         }
4619     },
4620
4621     /**
4622      * Usually called by the {@link Roo.data.Store} which owns the Record.
4623      * Rejects all changes made to the Record since either creation, or the last commit operation.
4624      * Modified fields are reverted to their original values.
4625      * <p>
4626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4627      * of reject operations.
4628      */
4629     reject : function(){
4630         var m = this.modified;
4631         for(var n in m){
4632             if(typeof m[n] != "function"){
4633                 this.data[n] = m[n];
4634             }
4635         }
4636         this.dirty = false;
4637         delete this.modified;
4638         this.editing = false;
4639         if(this.store){
4640             this.store.afterReject(this);
4641         }
4642     },
4643
4644     /**
4645      * Usually called by the {@link Roo.data.Store} which owns the Record.
4646      * Commits all changes made to the Record since either creation, or the last commit operation.
4647      * <p>
4648      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4649      * of commit operations.
4650      */
4651     commit : function(){
4652         this.dirty = false;
4653         delete this.modified;
4654         this.editing = false;
4655         if(this.store){
4656             this.store.afterCommit(this);
4657         }
4658     },
4659
4660     // private
4661     hasError : function(){
4662         return this.error != null;
4663     },
4664
4665     // private
4666     clearError : function(){
4667         this.error = null;
4668     },
4669
4670     /**
4671      * Creates a copy of this record.
4672      * @param {String} id (optional) A new record id if you don't want to use this record's id
4673      * @return {Record}
4674      */
4675     copy : function(newId) {
4676         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4677     }
4678 };/*
4679  * Based on:
4680  * Ext JS Library 1.1.1
4681  * Copyright(c) 2006-2007, Ext JS, LLC.
4682  *
4683  * Originally Released Under LGPL - original licence link has changed is not relivant.
4684  *
4685  * Fork - LGPL
4686  * <script type="text/javascript">
4687  */
4688
4689
4690
4691 /**
4692  * @class Roo.data.Store
4693  * @extends Roo.util.Observable
4694  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4695  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4696  * <p>
4697  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4698  * has no knowledge of the format of the data returned by the Proxy.<br>
4699  * <p>
4700  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4701  * instances from the data object. These records are cached and made available through accessor functions.
4702  * @constructor
4703  * Creates a new Store.
4704  * @param {Object} config A config object containing the objects needed for the Store to access data,
4705  * and read the data into Records.
4706  */
4707 Roo.data.Store = function(config){
4708     this.data = new Roo.util.MixedCollection(false);
4709     this.data.getKey = function(o){
4710         return o.id;
4711     };
4712     this.baseParams = {};
4713     // private
4714     this.paramNames = {
4715         "start" : "start",
4716         "limit" : "limit",
4717         "sort" : "sort",
4718         "dir" : "dir"
4719     };
4720
4721     if(config && config.data){
4722         this.inlineData = config.data;
4723         delete config.data;
4724     }
4725
4726     Roo.apply(this, config);
4727     
4728     if(this.reader){ // reader passed
4729         this.reader = Roo.factory(this.reader, Roo.data);
4730         this.reader.xmodule = this.xmodule || false;
4731         if(!this.recordType){
4732             this.recordType = this.reader.recordType;
4733         }
4734         if(this.reader.onMetaChange){
4735             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4736         }
4737     }
4738
4739     if(this.recordType){
4740         this.fields = this.recordType.prototype.fields;
4741     }
4742     this.modified = [];
4743
4744     this.addEvents({
4745         /**
4746          * @event datachanged
4747          * Fires when the data cache has changed, and a widget which is using this Store
4748          * as a Record cache should refresh its view.
4749          * @param {Store} this
4750          */
4751         datachanged : true,
4752         /**
4753          * @event metachange
4754          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4755          * @param {Store} this
4756          * @param {Object} meta The JSON metadata
4757          */
4758         metachange : true,
4759         /**
4760          * @event add
4761          * Fires when Records have been added to the Store
4762          * @param {Store} this
4763          * @param {Roo.data.Record[]} records The array of Records added
4764          * @param {Number} index The index at which the record(s) were added
4765          */
4766         add : true,
4767         /**
4768          * @event remove
4769          * Fires when a Record has been removed from the Store
4770          * @param {Store} this
4771          * @param {Roo.data.Record} record The Record that was removed
4772          * @param {Number} index The index at which the record was removed
4773          */
4774         remove : true,
4775         /**
4776          * @event update
4777          * Fires when a Record has been updated
4778          * @param {Store} this
4779          * @param {Roo.data.Record} record The Record that was updated
4780          * @param {String} operation The update operation being performed.  Value may be one of:
4781          * <pre><code>
4782  Roo.data.Record.EDIT
4783  Roo.data.Record.REJECT
4784  Roo.data.Record.COMMIT
4785          * </code></pre>
4786          */
4787         update : true,
4788         /**
4789          * @event clear
4790          * Fires when the data cache has been cleared.
4791          * @param {Store} this
4792          */
4793         clear : true,
4794         /**
4795          * @event beforeload
4796          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4797          * the load action will be canceled.
4798          * @param {Store} this
4799          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4800          */
4801         beforeload : true,
4802         /**
4803          * @event load
4804          * Fires after a new set of Records has been loaded.
4805          * @param {Store} this
4806          * @param {Roo.data.Record[]} records The Records that were loaded
4807          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4808          */
4809         load : true,
4810         /**
4811          * @event loadexception
4812          * Fires if an exception occurs in the Proxy during loading.
4813          * Called with the signature of the Proxy's "loadexception" event.
4814          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4815          * 
4816          * @param {Proxy} 
4817          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4818          * @param {Object} load options 
4819          * @param {Object} jsonData from your request (normally this contains the Exception)
4820          */
4821         loadexception : true
4822     });
4823     
4824     if(this.proxy){
4825         this.proxy = Roo.factory(this.proxy, Roo.data);
4826         this.proxy.xmodule = this.xmodule || false;
4827         this.relayEvents(this.proxy,  ["loadexception"]);
4828     }
4829     this.sortToggle = {};
4830
4831     Roo.data.Store.superclass.constructor.call(this);
4832
4833     if(this.inlineData){
4834         this.loadData(this.inlineData);
4835         delete this.inlineData;
4836     }
4837 };
4838 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4839      /**
4840     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4841     * without a remote query - used by combo/forms at present.
4842     */
4843     
4844     /**
4845     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4846     */
4847     /**
4848     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4849     */
4850     /**
4851     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4852     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4853     */
4854     /**
4855     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4856     * on any HTTP request
4857     */
4858     /**
4859     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4860     */
4861     /**
4862     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4863     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4864     */
4865     remoteSort : false,
4866
4867     /**
4868     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4869      * loaded or when a record is removed. (defaults to false).
4870     */
4871     pruneModifiedRecords : false,
4872
4873     // private
4874     lastOptions : null,
4875
4876     /**
4877      * Add Records to the Store and fires the add event.
4878      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4879      */
4880     add : function(records){
4881         records = [].concat(records);
4882         for(var i = 0, len = records.length; i < len; i++){
4883             records[i].join(this);
4884         }
4885         var index = this.data.length;
4886         this.data.addAll(records);
4887         this.fireEvent("add", this, records, index);
4888     },
4889
4890     /**
4891      * Remove a Record from the Store and fires the remove event.
4892      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4893      */
4894     remove : function(record){
4895         var index = this.data.indexOf(record);
4896         this.data.removeAt(index);
4897         if(this.pruneModifiedRecords){
4898             this.modified.remove(record);
4899         }
4900         this.fireEvent("remove", this, record, index);
4901     },
4902
4903     /**
4904      * Remove all Records from the Store and fires the clear event.
4905      */
4906     removeAll : function(){
4907         this.data.clear();
4908         if(this.pruneModifiedRecords){
4909             this.modified = [];
4910         }
4911         this.fireEvent("clear", this);
4912     },
4913
4914     /**
4915      * Inserts Records to the Store at the given index and fires the add event.
4916      * @param {Number} index The start index at which to insert the passed Records.
4917      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4918      */
4919     insert : function(index, records){
4920         records = [].concat(records);
4921         for(var i = 0, len = records.length; i < len; i++){
4922             this.data.insert(index, records[i]);
4923             records[i].join(this);
4924         }
4925         this.fireEvent("add", this, records, index);
4926     },
4927
4928     /**
4929      * Get the index within the cache of the passed Record.
4930      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4931      * @return {Number} The index of the passed Record. Returns -1 if not found.
4932      */
4933     indexOf : function(record){
4934         return this.data.indexOf(record);
4935     },
4936
4937     /**
4938      * Get the index within the cache of the Record with the passed id.
4939      * @param {String} id The id of the Record to find.
4940      * @return {Number} The index of the Record. Returns -1 if not found.
4941      */
4942     indexOfId : function(id){
4943         return this.data.indexOfKey(id);
4944     },
4945
4946     /**
4947      * Get the Record with the specified id.
4948      * @param {String} id The id of the Record to find.
4949      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4950      */
4951     getById : function(id){
4952         return this.data.key(id);
4953     },
4954
4955     /**
4956      * Get the Record at the specified index.
4957      * @param {Number} index The index of the Record to find.
4958      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4959      */
4960     getAt : function(index){
4961         return this.data.itemAt(index);
4962     },
4963
4964     /**
4965      * Returns a range of Records between specified indices.
4966      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4967      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4968      * @return {Roo.data.Record[]} An array of Records
4969      */
4970     getRange : function(start, end){
4971         return this.data.getRange(start, end);
4972     },
4973
4974     // private
4975     storeOptions : function(o){
4976         o = Roo.apply({}, o);
4977         delete o.callback;
4978         delete o.scope;
4979         this.lastOptions = o;
4980     },
4981
4982     /**
4983      * Loads the Record cache from the configured Proxy using the configured Reader.
4984      * <p>
4985      * If using remote paging, then the first load call must specify the <em>start</em>
4986      * and <em>limit</em> properties in the options.params property to establish the initial
4987      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4988      * <p>
4989      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4990      * and this call will return before the new data has been loaded. Perform any post-processing
4991      * in a callback function, or in a "load" event handler.</strong>
4992      * <p>
4993      * @param {Object} options An object containing properties which control loading options:<ul>
4994      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4995      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4996      * passed the following arguments:<ul>
4997      * <li>r : Roo.data.Record[]</li>
4998      * <li>options: Options object from the load call</li>
4999      * <li>success: Boolean success indicator</li></ul></li>
5000      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5001      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5002      * </ul>
5003      */
5004     load : function(options){
5005         options = options || {};
5006         if(this.fireEvent("beforeload", this, options) !== false){
5007             this.storeOptions(options);
5008             var p = Roo.apply(options.params || {}, this.baseParams);
5009             // if meta was not loaded from remote source.. try requesting it.
5010             if (!this.reader.metaFromRemote) {
5011                 p._requestMeta = 1;
5012             }
5013             if(this.sortInfo && this.remoteSort){
5014                 var pn = this.paramNames;
5015                 p[pn["sort"]] = this.sortInfo.field;
5016                 p[pn["dir"]] = this.sortInfo.direction;
5017             }
5018             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5019         }
5020     },
5021
5022     /**
5023      * Reloads the Record cache from the configured Proxy using the configured Reader and
5024      * the options from the last load operation performed.
5025      * @param {Object} options (optional) An object containing properties which may override the options
5026      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5027      * the most recently used options are reused).
5028      */
5029     reload : function(options){
5030         this.load(Roo.applyIf(options||{}, this.lastOptions));
5031     },
5032
5033     // private
5034     // Called as a callback by the Reader during a load operation.
5035     loadRecords : function(o, options, success){
5036         if(!o || success === false){
5037             if(success !== false){
5038                 this.fireEvent("load", this, [], options);
5039             }
5040             if(options.callback){
5041                 options.callback.call(options.scope || this, [], options, false);
5042             }
5043             return;
5044         }
5045         // if data returned failure - throw an exception.
5046         if (o.success === false) {
5047             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5048             return;
5049         }
5050         var r = o.records, t = o.totalRecords || r.length;
5051         if(!options || options.add !== true){
5052             if(this.pruneModifiedRecords){
5053                 this.modified = [];
5054             }
5055             for(var i = 0, len = r.length; i < len; i++){
5056                 r[i].join(this);
5057             }
5058             if(this.snapshot){
5059                 this.data = this.snapshot;
5060                 delete this.snapshot;
5061             }
5062             this.data.clear();
5063             this.data.addAll(r);
5064             this.totalLength = t;
5065             this.applySort();
5066             this.fireEvent("datachanged", this);
5067         }else{
5068             this.totalLength = Math.max(t, this.data.length+r.length);
5069             this.add(r);
5070         }
5071         this.fireEvent("load", this, r, options);
5072         if(options.callback){
5073             options.callback.call(options.scope || this, r, options, true);
5074         }
5075     },
5076
5077     /**
5078      * Loads data from a passed data block. A Reader which understands the format of the data
5079      * must have been configured in the constructor.
5080      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5081      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5082      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5083      */
5084     loadData : function(o, append){
5085         var r = this.reader.readRecords(o);
5086         this.loadRecords(r, {add: append}, true);
5087     },
5088
5089     /**
5090      * Gets the number of cached records.
5091      * <p>
5092      * <em>If using paging, this may not be the total size of the dataset. If the data object
5093      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5094      * the data set size</em>
5095      */
5096     getCount : function(){
5097         return this.data.length || 0;
5098     },
5099
5100     /**
5101      * Gets the total number of records in the dataset as returned by the server.
5102      * <p>
5103      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5104      * the dataset size</em>
5105      */
5106     getTotalCount : function(){
5107         return this.totalLength || 0;
5108     },
5109
5110     /**
5111      * Returns the sort state of the Store as an object with two properties:
5112      * <pre><code>
5113  field {String} The name of the field by which the Records are sorted
5114  direction {String} The sort order, "ASC" or "DESC"
5115      * </code></pre>
5116      */
5117     getSortState : function(){
5118         return this.sortInfo;
5119     },
5120
5121     // private
5122     applySort : function(){
5123         if(this.sortInfo && !this.remoteSort){
5124             var s = this.sortInfo, f = s.field;
5125             var st = this.fields.get(f).sortType;
5126             var fn = function(r1, r2){
5127                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5128                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5129             };
5130             this.data.sort(s.direction, fn);
5131             if(this.snapshot && this.snapshot != this.data){
5132                 this.snapshot.sort(s.direction, fn);
5133             }
5134         }
5135     },
5136
5137     /**
5138      * Sets the default sort column and order to be used by the next load operation.
5139      * @param {String} fieldName The name of the field to sort by.
5140      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5141      */
5142     setDefaultSort : function(field, dir){
5143         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5144     },
5145
5146     /**
5147      * Sort the Records.
5148      * If remote sorting is used, the sort is performed on the server, and the cache is
5149      * reloaded. If local sorting is used, the cache is sorted internally.
5150      * @param {String} fieldName The name of the field to sort by.
5151      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5152      */
5153     sort : function(fieldName, dir){
5154         var f = this.fields.get(fieldName);
5155         if(!dir){
5156             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5157                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5158             }else{
5159                 dir = f.sortDir;
5160             }
5161         }
5162         this.sortToggle[f.name] = dir;
5163         this.sortInfo = {field: f.name, direction: dir};
5164         if(!this.remoteSort){
5165             this.applySort();
5166             this.fireEvent("datachanged", this);
5167         }else{
5168             this.load(this.lastOptions);
5169         }
5170     },
5171
5172     /**
5173      * Calls the specified function for each of the Records in the cache.
5174      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5175      * Returning <em>false</em> aborts and exits the iteration.
5176      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5177      */
5178     each : function(fn, scope){
5179         this.data.each(fn, scope);
5180     },
5181
5182     /**
5183      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5184      * (e.g., during paging).
5185      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5186      */
5187     getModifiedRecords : function(){
5188         return this.modified;
5189     },
5190
5191     // private
5192     createFilterFn : function(property, value, anyMatch){
5193         if(!value.exec){ // not a regex
5194             value = String(value);
5195             if(value.length == 0){
5196                 return false;
5197             }
5198             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5199         }
5200         return function(r){
5201             return value.test(r.data[property]);
5202         };
5203     },
5204
5205     /**
5206      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5207      * @param {String} property A field on your records
5208      * @param {Number} start The record index to start at (defaults to 0)
5209      * @param {Number} end The last record index to include (defaults to length - 1)
5210      * @return {Number} The sum
5211      */
5212     sum : function(property, start, end){
5213         var rs = this.data.items, v = 0;
5214         start = start || 0;
5215         end = (end || end === 0) ? end : rs.length-1;
5216
5217         for(var i = start; i <= end; i++){
5218             v += (rs[i].data[property] || 0);
5219         }
5220         return v;
5221     },
5222
5223     /**
5224      * Filter the records by a specified property.
5225      * @param {String} field A field on your records
5226      * @param {String/RegExp} value Either a string that the field
5227      * should start with or a RegExp to test against the field
5228      * @param {Boolean} anyMatch True to match any part not just the beginning
5229      */
5230     filter : function(property, value, anyMatch){
5231         var fn = this.createFilterFn(property, value, anyMatch);
5232         return fn ? this.filterBy(fn) : this.clearFilter();
5233     },
5234
5235     /**
5236      * Filter by a function. The specified function will be called with each
5237      * record in this data source. If the function returns true the record is included,
5238      * otherwise it is filtered.
5239      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5240      * @param {Object} scope (optional) The scope of the function (defaults to this)
5241      */
5242     filterBy : function(fn, scope){
5243         this.snapshot = this.snapshot || this.data;
5244         this.data = this.queryBy(fn, scope||this);
5245         this.fireEvent("datachanged", this);
5246     },
5247
5248     /**
5249      * Query the records by a specified property.
5250      * @param {String} field A field on your records
5251      * @param {String/RegExp} value Either a string that the field
5252      * should start with or a RegExp to test against the field
5253      * @param {Boolean} anyMatch True to match any part not just the beginning
5254      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5255      */
5256     query : function(property, value, anyMatch){
5257         var fn = this.createFilterFn(property, value, anyMatch);
5258         return fn ? this.queryBy(fn) : this.data.clone();
5259     },
5260
5261     /**
5262      * Query by a function. The specified function will be called with each
5263      * record in this data source. If the function returns true the record is included
5264      * in the results.
5265      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5266      * @param {Object} scope (optional) The scope of the function (defaults to this)
5267       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5268      **/
5269     queryBy : function(fn, scope){
5270         var data = this.snapshot || this.data;
5271         return data.filterBy(fn, scope||this);
5272     },
5273
5274     /**
5275      * Collects unique values for a particular dataIndex from this store.
5276      * @param {String} dataIndex The property to collect
5277      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5278      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5279      * @return {Array} An array of the unique values
5280      **/
5281     collect : function(dataIndex, allowNull, bypassFilter){
5282         var d = (bypassFilter === true && this.snapshot) ?
5283                 this.snapshot.items : this.data.items;
5284         var v, sv, r = [], l = {};
5285         for(var i = 0, len = d.length; i < len; i++){
5286             v = d[i].data[dataIndex];
5287             sv = String(v);
5288             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5289                 l[sv] = true;
5290                 r[r.length] = v;
5291             }
5292         }
5293         return r;
5294     },
5295
5296     /**
5297      * Revert to a view of the Record cache with no filtering applied.
5298      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5299      */
5300     clearFilter : function(suppressEvent){
5301         if(this.snapshot && this.snapshot != this.data){
5302             this.data = this.snapshot;
5303             delete this.snapshot;
5304             if(suppressEvent !== true){
5305                 this.fireEvent("datachanged", this);
5306             }
5307         }
5308     },
5309
5310     // private
5311     afterEdit : function(record){
5312         if(this.modified.indexOf(record) == -1){
5313             this.modified.push(record);
5314         }
5315         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5316     },
5317
5318     // private
5319     afterReject : function(record){
5320         this.modified.remove(record);
5321         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5322     },
5323
5324     // private
5325     afterCommit : function(record){
5326         this.modified.remove(record);
5327         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5328     },
5329
5330     /**
5331      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5332      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5333      */
5334     commitChanges : function(){
5335         var m = this.modified.slice(0);
5336         this.modified = [];
5337         for(var i = 0, len = m.length; i < len; i++){
5338             m[i].commit();
5339         }
5340     },
5341
5342     /**
5343      * Cancel outstanding changes on all changed records.
5344      */
5345     rejectChanges : function(){
5346         var m = this.modified.slice(0);
5347         this.modified = [];
5348         for(var i = 0, len = m.length; i < len; i++){
5349             m[i].reject();
5350         }
5351     },
5352
5353     onMetaChange : function(meta, rtype, o){
5354         this.recordType = rtype;
5355         this.fields = rtype.prototype.fields;
5356         delete this.snapshot;
5357         this.sortInfo = meta.sortInfo || this.sortInfo;
5358         this.modified = [];
5359         this.fireEvent('metachange', this, this.reader.meta);
5360     }
5361 });/*
5362  * Based on:
5363  * Ext JS Library 1.1.1
5364  * Copyright(c) 2006-2007, Ext JS, LLC.
5365  *
5366  * Originally Released Under LGPL - original licence link has changed is not relivant.
5367  *
5368  * Fork - LGPL
5369  * <script type="text/javascript">
5370  */
5371
5372 /**
5373  * @class Roo.data.SimpleStore
5374  * @extends Roo.data.Store
5375  * Small helper class to make creating Stores from Array data easier.
5376  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5377  * @cfg {Array} fields An array of field definition objects, or field name strings.
5378  * @cfg {Array} data The multi-dimensional array of data
5379  * @constructor
5380  * @param {Object} config
5381  */
5382 Roo.data.SimpleStore = function(config){
5383     Roo.data.SimpleStore.superclass.constructor.call(this, {
5384         isLocal : true,
5385         reader: new Roo.data.ArrayReader({
5386                 id: config.id
5387             },
5388             Roo.data.Record.create(config.fields)
5389         ),
5390         proxy : new Roo.data.MemoryProxy(config.data)
5391     });
5392     this.load();
5393 };
5394 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5395  * Based on:
5396  * Ext JS Library 1.1.1
5397  * Copyright(c) 2006-2007, Ext JS, LLC.
5398  *
5399  * Originally Released Under LGPL - original licence link has changed is not relivant.
5400  *
5401  * Fork - LGPL
5402  * <script type="text/javascript">
5403  */
5404
5405 /**
5406 /**
5407  * @extends Roo.data.Store
5408  * @class Roo.data.JsonStore
5409  * Small helper class to make creating Stores for JSON data easier. <br/>
5410 <pre><code>
5411 var store = new Roo.data.JsonStore({
5412     url: 'get-images.php',
5413     root: 'images',
5414     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5415 });
5416 </code></pre>
5417  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5418  * JsonReader and HttpProxy (unless inline data is provided).</b>
5419  * @cfg {Array} fields An array of field definition objects, or field name strings.
5420  * @constructor
5421  * @param {Object} config
5422  */
5423 Roo.data.JsonStore = function(c){
5424     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5425         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5426         reader: new Roo.data.JsonReader(c, c.fields)
5427     }));
5428 };
5429 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5430  * Based on:
5431  * Ext JS Library 1.1.1
5432  * Copyright(c) 2006-2007, Ext JS, LLC.
5433  *
5434  * Originally Released Under LGPL - original licence link has changed is not relivant.
5435  *
5436  * Fork - LGPL
5437  * <script type="text/javascript">
5438  */
5439
5440  
5441 Roo.data.Field = function(config){
5442     if(typeof config == "string"){
5443         config = {name: config};
5444     }
5445     Roo.apply(this, config);
5446     
5447     if(!this.type){
5448         this.type = "auto";
5449     }
5450     
5451     var st = Roo.data.SortTypes;
5452     // named sortTypes are supported, here we look them up
5453     if(typeof this.sortType == "string"){
5454         this.sortType = st[this.sortType];
5455     }
5456     
5457     // set default sortType for strings and dates
5458     if(!this.sortType){
5459         switch(this.type){
5460             case "string":
5461                 this.sortType = st.asUCString;
5462                 break;
5463             case "date":
5464                 this.sortType = st.asDate;
5465                 break;
5466             default:
5467                 this.sortType = st.none;
5468         }
5469     }
5470
5471     // define once
5472     var stripRe = /[\$,%]/g;
5473
5474     // prebuilt conversion function for this field, instead of
5475     // switching every time we're reading a value
5476     if(!this.convert){
5477         var cv, dateFormat = this.dateFormat;
5478         switch(this.type){
5479             case "":
5480             case "auto":
5481             case undefined:
5482                 cv = function(v){ return v; };
5483                 break;
5484             case "string":
5485                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5486                 break;
5487             case "int":
5488                 cv = function(v){
5489                     return v !== undefined && v !== null && v !== '' ?
5490                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5491                     };
5492                 break;
5493             case "float":
5494                 cv = function(v){
5495                     return v !== undefined && v !== null && v !== '' ?
5496                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5497                     };
5498                 break;
5499             case "bool":
5500             case "boolean":
5501                 cv = function(v){ return v === true || v === "true" || v == 1; };
5502                 break;
5503             case "date":
5504                 cv = function(v){
5505                     if(!v){
5506                         return '';
5507                     }
5508                     if(v instanceof Date){
5509                         return v;
5510                     }
5511                     if(dateFormat){
5512                         if(dateFormat == "timestamp"){
5513                             return new Date(v*1000);
5514                         }
5515                         return Date.parseDate(v, dateFormat);
5516                     }
5517                     var parsed = Date.parse(v);
5518                     return parsed ? new Date(parsed) : null;
5519                 };
5520              break;
5521             
5522         }
5523         this.convert = cv;
5524     }
5525 };
5526
5527 Roo.data.Field.prototype = {
5528     dateFormat: null,
5529     defaultValue: "",
5530     mapping: null,
5531     sortType : null,
5532     sortDir : "ASC"
5533 };/*
5534  * Based on:
5535  * Ext JS Library 1.1.1
5536  * Copyright(c) 2006-2007, Ext JS, LLC.
5537  *
5538  * Originally Released Under LGPL - original licence link has changed is not relivant.
5539  *
5540  * Fork - LGPL
5541  * <script type="text/javascript">
5542  */
5543  
5544 // Base class for reading structured data from a data source.  This class is intended to be
5545 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5546
5547 /**
5548  * @class Roo.data.DataReader
5549  * Base class for reading structured data from a data source.  This class is intended to be
5550  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5551  */
5552
5553 Roo.data.DataReader = function(meta, recordType){
5554     
5555     this.meta = meta;
5556     
5557     this.recordType = recordType instanceof Array ? 
5558         Roo.data.Record.create(recordType) : recordType;
5559 };
5560
5561 Roo.data.DataReader.prototype = {
5562      /**
5563      * Create an empty record
5564      * @param {Object} data (optional) - overlay some values
5565      * @return {Roo.data.Record} record created.
5566      */
5567     newRow :  function(d) {
5568         var da =  {};
5569         this.recordType.prototype.fields.each(function(c) {
5570             switch( c.type) {
5571                 case 'int' : da[c.name] = 0; break;
5572                 case 'date' : da[c.name] = new Date(); break;
5573                 case 'float' : da[c.name] = 0.0; break;
5574                 case 'boolean' : da[c.name] = false; break;
5575                 default : da[c.name] = ""; break;
5576             }
5577             
5578         });
5579         return new this.recordType(Roo.apply(da, d));
5580     }
5581     
5582 };/*
5583  * Based on:
5584  * Ext JS Library 1.1.1
5585  * Copyright(c) 2006-2007, Ext JS, LLC.
5586  *
5587  * Originally Released Under LGPL - original licence link has changed is not relivant.
5588  *
5589  * Fork - LGPL
5590  * <script type="text/javascript">
5591  */
5592
5593 /**
5594  * @class Roo.data.DataProxy
5595  * @extends Roo.data.Observable
5596  * This class is an abstract base class for implementations which provide retrieval of
5597  * unformatted data objects.<br>
5598  * <p>
5599  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5600  * (of the appropriate type which knows how to parse the data object) to provide a block of
5601  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5602  * <p>
5603  * Custom implementations must implement the load method as described in
5604  * {@link Roo.data.HttpProxy#load}.
5605  */
5606 Roo.data.DataProxy = function(){
5607     this.addEvents({
5608         /**
5609          * @event beforeload
5610          * Fires before a network request is made to retrieve a data object.
5611          * @param {Object} This DataProxy object.
5612          * @param {Object} params The params parameter to the load function.
5613          */
5614         beforeload : true,
5615         /**
5616          * @event load
5617          * Fires before the load method's callback is called.
5618          * @param {Object} This DataProxy object.
5619          * @param {Object} o The data object.
5620          * @param {Object} arg The callback argument object passed to the load function.
5621          */
5622         load : true,
5623         /**
5624          * @event loadexception
5625          * Fires if an Exception occurs during data retrieval.
5626          * @param {Object} This DataProxy object.
5627          * @param {Object} o The data object.
5628          * @param {Object} arg The callback argument object passed to the load function.
5629          * @param {Object} e The Exception.
5630          */
5631         loadexception : true
5632     });
5633     Roo.data.DataProxy.superclass.constructor.call(this);
5634 };
5635
5636 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5637
5638     /**
5639      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5640      */
5641 /*
5642  * Based on:
5643  * Ext JS Library 1.1.1
5644  * Copyright(c) 2006-2007, Ext JS, LLC.
5645  *
5646  * Originally Released Under LGPL - original licence link has changed is not relivant.
5647  *
5648  * Fork - LGPL
5649  * <script type="text/javascript">
5650  */
5651 /**
5652  * @class Roo.data.MemoryProxy
5653  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5654  * to the Reader when its load method is called.
5655  * @constructor
5656  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5657  */
5658 Roo.data.MemoryProxy = function(data){
5659     if (data.data) {
5660         data = data.data;
5661     }
5662     Roo.data.MemoryProxy.superclass.constructor.call(this);
5663     this.data = data;
5664 };
5665
5666 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5667     /**
5668      * Load data from the requested source (in this case an in-memory
5669      * data object passed to the constructor), read the data object into
5670      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5671      * process that block using the passed callback.
5672      * @param {Object} params This parameter is not used by the MemoryProxy class.
5673      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5674      * object into a block of Roo.data.Records.
5675      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5676      * The function must be passed <ul>
5677      * <li>The Record block object</li>
5678      * <li>The "arg" argument from the load function</li>
5679      * <li>A boolean success indicator</li>
5680      * </ul>
5681      * @param {Object} scope The scope in which to call the callback
5682      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5683      */
5684     load : function(params, reader, callback, scope, arg){
5685         params = params || {};
5686         var result;
5687         try {
5688             result = reader.readRecords(this.data);
5689         }catch(e){
5690             this.fireEvent("loadexception", this, arg, null, e);
5691             callback.call(scope, null, arg, false);
5692             return;
5693         }
5694         callback.call(scope, result, arg, true);
5695     },
5696     
5697     // private
5698     update : function(params, records){
5699         
5700     }
5701 });/*
5702  * Based on:
5703  * Ext JS Library 1.1.1
5704  * Copyright(c) 2006-2007, Ext JS, LLC.
5705  *
5706  * Originally Released Under LGPL - original licence link has changed is not relivant.
5707  *
5708  * Fork - LGPL
5709  * <script type="text/javascript">
5710  */
5711 /**
5712  * @class Roo.data.HttpProxy
5713  * @extends Roo.data.DataProxy
5714  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5715  * configured to reference a certain URL.<br><br>
5716  * <p>
5717  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5718  * from which the running page was served.<br><br>
5719  * <p>
5720  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5721  * <p>
5722  * Be aware that to enable the browser to parse an XML document, the server must set
5723  * the Content-Type header in the HTTP response to "text/xml".
5724  * @constructor
5725  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5726  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5727  * will be used to make the request.
5728  */
5729 Roo.data.HttpProxy = function(conn){
5730     Roo.data.HttpProxy.superclass.constructor.call(this);
5731     // is conn a conn config or a real conn?
5732     this.conn = conn;
5733     this.useAjax = !conn || !conn.events;
5734   
5735 };
5736
5737 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5738     // thse are take from connection...
5739     
5740     /**
5741      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5742      */
5743     /**
5744      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5745      * extra parameters to each request made by this object. (defaults to undefined)
5746      */
5747     /**
5748      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5749      *  to each request made by this object. (defaults to undefined)
5750      */
5751     /**
5752      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5753      */
5754     /**
5755      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5756      */
5757      /**
5758      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5759      * @type Boolean
5760      */
5761   
5762
5763     /**
5764      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5765      * @type Boolean
5766      */
5767     /**
5768      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5769      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5770      * a finer-grained basis than the DataProxy events.
5771      */
5772     getConnection : function(){
5773         return this.useAjax ? Roo.Ajax : this.conn;
5774     },
5775
5776     /**
5777      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5778      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5779      * process that block using the passed callback.
5780      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5781      * for the request to the remote server.
5782      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5783      * object into a block of Roo.data.Records.
5784      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5785      * The function must be passed <ul>
5786      * <li>The Record block object</li>
5787      * <li>The "arg" argument from the load function</li>
5788      * <li>A boolean success indicator</li>
5789      * </ul>
5790      * @param {Object} scope The scope in which to call the callback
5791      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5792      */
5793     load : function(params, reader, callback, scope, arg){
5794         if(this.fireEvent("beforeload", this, params) !== false){
5795             var  o = {
5796                 params : params || {},
5797                 request: {
5798                     callback : callback,
5799                     scope : scope,
5800                     arg : arg
5801                 },
5802                 reader: reader,
5803                 callback : this.loadResponse,
5804                 scope: this
5805             };
5806             if(this.useAjax){
5807                 Roo.applyIf(o, this.conn);
5808                 if(this.activeRequest){
5809                     Roo.Ajax.abort(this.activeRequest);
5810                 }
5811                 this.activeRequest = Roo.Ajax.request(o);
5812             }else{
5813                 this.conn.request(o);
5814             }
5815         }else{
5816             callback.call(scope||this, null, arg, false);
5817         }
5818     },
5819
5820     // private
5821     loadResponse : function(o, success, response){
5822         delete this.activeRequest;
5823         if(!success){
5824             this.fireEvent("loadexception", this, o, response);
5825             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5826             return;
5827         }
5828         var result;
5829         try {
5830             result = o.reader.read(response);
5831         }catch(e){
5832             this.fireEvent("loadexception", this, o, response, e);
5833             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5834             return;
5835         }
5836         
5837         this.fireEvent("load", this, o, o.request.arg);
5838         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5839     },
5840
5841     // private
5842     update : function(dataSet){
5843
5844     },
5845
5846     // private
5847     updateResponse : function(dataSet){
5848
5849     }
5850 });/*
5851  * Based on:
5852  * Ext JS Library 1.1.1
5853  * Copyright(c) 2006-2007, Ext JS, LLC.
5854  *
5855  * Originally Released Under LGPL - original licence link has changed is not relivant.
5856  *
5857  * Fork - LGPL
5858  * <script type="text/javascript">
5859  */
5860
5861 /**
5862  * @class Roo.data.ScriptTagProxy
5863  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5864  * other than the originating domain of the running page.<br><br>
5865  * <p>
5866  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5867  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5868  * <p>
5869  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5870  * source code that is used as the source inside a &lt;script> tag.<br><br>
5871  * <p>
5872  * In order for the browser to process the returned data, the server must wrap the data object
5873  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5874  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5875  * depending on whether the callback name was passed:
5876  * <p>
5877  * <pre><code>
5878 boolean scriptTag = false;
5879 String cb = request.getParameter("callback");
5880 if (cb != null) {
5881     scriptTag = true;
5882     response.setContentType("text/javascript");
5883 } else {
5884     response.setContentType("application/x-json");
5885 }
5886 Writer out = response.getWriter();
5887 if (scriptTag) {
5888     out.write(cb + "(");
5889 }
5890 out.print(dataBlock.toJsonString());
5891 if (scriptTag) {
5892     out.write(");");
5893 }
5894 </pre></code>
5895  *
5896  * @constructor
5897  * @param {Object} config A configuration object.
5898  */
5899 Roo.data.ScriptTagProxy = function(config){
5900     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5901     Roo.apply(this, config);
5902     this.head = document.getElementsByTagName("head")[0];
5903 };
5904
5905 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5906
5907 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5908     /**
5909      * @cfg {String} url The URL from which to request the data object.
5910      */
5911     /**
5912      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5913      */
5914     timeout : 30000,
5915     /**
5916      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5917      * the server the name of the callback function set up by the load call to process the returned data object.
5918      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5919      * javascript output which calls this named function passing the data object as its only parameter.
5920      */
5921     callbackParam : "callback",
5922     /**
5923      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5924      * name to the request.
5925      */
5926     nocache : true,
5927
5928     /**
5929      * Load data from the configured URL, read the data object into
5930      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5931      * process that block using the passed callback.
5932      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5933      * for the request to the remote server.
5934      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5935      * object into a block of Roo.data.Records.
5936      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5937      * The function must be passed <ul>
5938      * <li>The Record block object</li>
5939      * <li>The "arg" argument from the load function</li>
5940      * <li>A boolean success indicator</li>
5941      * </ul>
5942      * @param {Object} scope The scope in which to call the callback
5943      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5944      */
5945     load : function(params, reader, callback, scope, arg){
5946         if(this.fireEvent("beforeload", this, params) !== false){
5947
5948             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5949
5950             var url = this.url;
5951             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5952             if(this.nocache){
5953                 url += "&_dc=" + (new Date().getTime());
5954             }
5955             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5956             var trans = {
5957                 id : transId,
5958                 cb : "stcCallback"+transId,
5959                 scriptId : "stcScript"+transId,
5960                 params : params,
5961                 arg : arg,
5962                 url : url,
5963                 callback : callback,
5964                 scope : scope,
5965                 reader : reader
5966             };
5967             var conn = this;
5968
5969             window[trans.cb] = function(o){
5970                 conn.handleResponse(o, trans);
5971             };
5972
5973             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5974
5975             if(this.autoAbort !== false){
5976                 this.abort();
5977             }
5978
5979             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5980
5981             var script = document.createElement("script");
5982             script.setAttribute("src", url);
5983             script.setAttribute("type", "text/javascript");
5984             script.setAttribute("id", trans.scriptId);
5985             this.head.appendChild(script);
5986
5987             this.trans = trans;
5988         }else{
5989             callback.call(scope||this, null, arg, false);
5990         }
5991     },
5992
5993     // private
5994     isLoading : function(){
5995         return this.trans ? true : false;
5996     },
5997
5998     /**
5999      * Abort the current server request.
6000      */
6001     abort : function(){
6002         if(this.isLoading()){
6003             this.destroyTrans(this.trans);
6004         }
6005     },
6006
6007     // private
6008     destroyTrans : function(trans, isLoaded){
6009         this.head.removeChild(document.getElementById(trans.scriptId));
6010         clearTimeout(trans.timeoutId);
6011         if(isLoaded){
6012             window[trans.cb] = undefined;
6013             try{
6014                 delete window[trans.cb];
6015             }catch(e){}
6016         }else{
6017             // if hasn't been loaded, wait for load to remove it to prevent script error
6018             window[trans.cb] = function(){
6019                 window[trans.cb] = undefined;
6020                 try{
6021                     delete window[trans.cb];
6022                 }catch(e){}
6023             };
6024         }
6025     },
6026
6027     // private
6028     handleResponse : function(o, trans){
6029         this.trans = false;
6030         this.destroyTrans(trans, true);
6031         var result;
6032         try {
6033             result = trans.reader.readRecords(o);
6034         }catch(e){
6035             this.fireEvent("loadexception", this, o, trans.arg, e);
6036             trans.callback.call(trans.scope||window, null, trans.arg, false);
6037             return;
6038         }
6039         this.fireEvent("load", this, o, trans.arg);
6040         trans.callback.call(trans.scope||window, result, trans.arg, true);
6041     },
6042
6043     // private
6044     handleFailure : function(trans){
6045         this.trans = false;
6046         this.destroyTrans(trans, false);
6047         this.fireEvent("loadexception", this, null, trans.arg);
6048         trans.callback.call(trans.scope||window, null, trans.arg, false);
6049     }
6050 });/*
6051  * Based on:
6052  * Ext JS Library 1.1.1
6053  * Copyright(c) 2006-2007, Ext JS, LLC.
6054  *
6055  * Originally Released Under LGPL - original licence link has changed is not relivant.
6056  *
6057  * Fork - LGPL
6058  * <script type="text/javascript">
6059  */
6060
6061 /**
6062  * @class Roo.data.JsonReader
6063  * @extends Roo.data.DataReader
6064  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6065  * based on mappings in a provided Roo.data.Record constructor.
6066  * 
6067  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6068  * in the reply previously. 
6069  * 
6070  * <p>
6071  * Example code:
6072  * <pre><code>
6073 var RecordDef = Roo.data.Record.create([
6074     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6075     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6076 ]);
6077 var myReader = new Roo.data.JsonReader({
6078     totalProperty: "results",    // The property which contains the total dataset size (optional)
6079     root: "rows",                // The property which contains an Array of row objects
6080     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6081 }, RecordDef);
6082 </code></pre>
6083  * <p>
6084  * This would consume a JSON file like this:
6085  * <pre><code>
6086 { 'results': 2, 'rows': [
6087     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6088     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6089 }
6090 </code></pre>
6091  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6092  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6093  * paged from the remote server.
6094  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6095  * @cfg {String} root name of the property which contains the Array of row objects.
6096  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6097  * @constructor
6098  * Create a new JsonReader
6099  * @param {Object} meta Metadata configuration options
6100  * @param {Object} recordType Either an Array of field definition objects,
6101  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6102  */
6103 Roo.data.JsonReader = function(meta, recordType){
6104     
6105     meta = meta || {};
6106     // set some defaults:
6107     Roo.applyIf(meta, {
6108         totalProperty: 'total',
6109         successProperty : 'success',
6110         root : 'data',
6111         id : 'id'
6112     });
6113     
6114     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6115 };
6116 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6117     
6118     /**
6119      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6120      * Used by Store query builder to append _requestMeta to params.
6121      * 
6122      */
6123     metaFromRemote : false,
6124     /**
6125      * This method is only used by a DataProxy which has retrieved data from a remote server.
6126      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6127      * @return {Object} data A data block which is used by an Roo.data.Store object as
6128      * a cache of Roo.data.Records.
6129      */
6130     read : function(response){
6131         var json = response.responseText;
6132        
6133         var o = /* eval:var:o */ eval("("+json+")");
6134         if(!o) {
6135             throw {message: "JsonReader.read: Json object not found"};
6136         }
6137         
6138         if(o.metaData){
6139             
6140             delete this.ef;
6141             this.metaFromRemote = true;
6142             this.meta = o.metaData;
6143             this.recordType = Roo.data.Record.create(o.metaData.fields);
6144             this.onMetaChange(this.meta, this.recordType, o);
6145         }
6146         return this.readRecords(o);
6147     },
6148
6149     // private function a store will implement
6150     onMetaChange : function(meta, recordType, o){
6151
6152     },
6153
6154     /**
6155          * @ignore
6156          */
6157     simpleAccess: function(obj, subsc) {
6158         return obj[subsc];
6159     },
6160
6161         /**
6162          * @ignore
6163          */
6164     getJsonAccessor: function(){
6165         var re = /[\[\.]/;
6166         return function(expr) {
6167             try {
6168                 return(re.test(expr))
6169                     ? new Function("obj", "return obj." + expr)
6170                     : function(obj){
6171                         return obj[expr];
6172                     };
6173             } catch(e){}
6174             return Roo.emptyFn;
6175         };
6176     }(),
6177
6178     /**
6179      * Create a data block containing Roo.data.Records from an XML document.
6180      * @param {Object} o An object which contains an Array of row objects in the property specified
6181      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6182      * which contains the total size of the dataset.
6183      * @return {Object} data A data block which is used by an Roo.data.Store object as
6184      * a cache of Roo.data.Records.
6185      */
6186     readRecords : function(o){
6187         /**
6188          * After any data loads, the raw JSON data is available for further custom processing.
6189          * @type Object
6190          */
6191         this.jsonData = o;
6192         var s = this.meta, Record = this.recordType,
6193             f = Record.prototype.fields, fi = f.items, fl = f.length;
6194
6195 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6196         if (!this.ef) {
6197             if(s.totalProperty) {
6198                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6199                 }
6200                 if(s.successProperty) {
6201                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6202                 }
6203                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6204                 if (s.id) {
6205                         var g = this.getJsonAccessor(s.id);
6206                         this.getId = function(rec) {
6207                                 var r = g(rec);
6208                                 return (r === undefined || r === "") ? null : r;
6209                         };
6210                 } else {
6211                         this.getId = function(){return null;};
6212                 }
6213             this.ef = [];
6214             for(var jj = 0; jj < fl; jj++){
6215                 f = fi[jj];
6216                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6217                 this.ef[jj] = this.getJsonAccessor(map);
6218             }
6219         }
6220
6221         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6222         if(s.totalProperty){
6223             var vt = parseInt(this.getTotal(o), 10);
6224             if(!isNaN(vt)){
6225                 totalRecords = vt;
6226             }
6227         }
6228         if(s.successProperty){
6229             var vs = this.getSuccess(o);
6230             if(vs === false || vs === 'false'){
6231                 success = false;
6232             }
6233         }
6234         var records = [];
6235             for(var i = 0; i < c; i++){
6236                     var n = root[i];
6237                 var values = {};
6238                 var id = this.getId(n);
6239                 for(var j = 0; j < fl; j++){
6240                     f = fi[j];
6241                 var v = this.ef[j](n);
6242                 if (!f.convert) {
6243                     Roo.log('missing convert for ' + f.name);
6244                     Roo.log(f);
6245                     continue;
6246                 }
6247                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6248                 }
6249                 var record = new Record(values, id);
6250                 record.json = n;
6251                 records[i] = record;
6252             }
6253             return {
6254                 success : success,
6255                 records : records,
6256                 totalRecords : totalRecords
6257             };
6258     }
6259 });/*
6260  * Based on:
6261  * Ext JS Library 1.1.1
6262  * Copyright(c) 2006-2007, Ext JS, LLC.
6263  *
6264  * Originally Released Under LGPL - original licence link has changed is not relivant.
6265  *
6266  * Fork - LGPL
6267  * <script type="text/javascript">
6268  */
6269
6270 /**
6271  * @class Roo.data.XmlReader
6272  * @extends Roo.data.DataReader
6273  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6274  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6275  * <p>
6276  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6277  * header in the HTTP response must be set to "text/xml".</em>
6278  * <p>
6279  * Example code:
6280  * <pre><code>
6281 var RecordDef = Roo.data.Record.create([
6282    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6283    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6284 ]);
6285 var myReader = new Roo.data.XmlReader({
6286    totalRecords: "results", // The element which contains the total dataset size (optional)
6287    record: "row",           // The repeated element which contains row information
6288    id: "id"                 // The element within the row that provides an ID for the record (optional)
6289 }, RecordDef);
6290 </code></pre>
6291  * <p>
6292  * This would consume an XML file like this:
6293  * <pre><code>
6294 &lt;?xml?>
6295 &lt;dataset>
6296  &lt;results>2&lt;/results>
6297  &lt;row>
6298    &lt;id>1&lt;/id>
6299    &lt;name>Bill&lt;/name>
6300    &lt;occupation>Gardener&lt;/occupation>
6301  &lt;/row>
6302  &lt;row>
6303    &lt;id>2&lt;/id>
6304    &lt;name>Ben&lt;/name>
6305    &lt;occupation>Horticulturalist&lt;/occupation>
6306  &lt;/row>
6307 &lt;/dataset>
6308 </code></pre>
6309  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6310  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6311  * paged from the remote server.
6312  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6313  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6314  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6315  * a record identifier value.
6316  * @constructor
6317  * Create a new XmlReader
6318  * @param {Object} meta Metadata configuration options
6319  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6320  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6321  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6322  */
6323 Roo.data.XmlReader = function(meta, recordType){
6324     meta = meta || {};
6325     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6326 };
6327 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6328     /**
6329      * This method is only used by a DataProxy which has retrieved data from a remote server.
6330          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6331          * to contain a method called 'responseXML' that returns an XML document object.
6332      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6333      * a cache of Roo.data.Records.
6334      */
6335     read : function(response){
6336         var doc = response.responseXML;
6337         if(!doc) {
6338             throw {message: "XmlReader.read: XML Document not available"};
6339         }
6340         return this.readRecords(doc);
6341     },
6342
6343     /**
6344      * Create a data block containing Roo.data.Records from an XML document.
6345          * @param {Object} doc A parsed XML document.
6346      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6347      * a cache of Roo.data.Records.
6348      */
6349     readRecords : function(doc){
6350         /**
6351          * After any data loads/reads, the raw XML Document is available for further custom processing.
6352          * @type XMLDocument
6353          */
6354         this.xmlData = doc;
6355         var root = doc.documentElement || doc;
6356         var q = Roo.DomQuery;
6357         var recordType = this.recordType, fields = recordType.prototype.fields;
6358         var sid = this.meta.id;
6359         var totalRecords = 0, success = true;
6360         if(this.meta.totalRecords){
6361             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6362         }
6363         
6364         if(this.meta.success){
6365             var sv = q.selectValue(this.meta.success, root, true);
6366             success = sv !== false && sv !== 'false';
6367         }
6368         var records = [];
6369         var ns = q.select(this.meta.record, root);
6370         for(var i = 0, len = ns.length; i < len; i++) {
6371                 var n = ns[i];
6372                 var values = {};
6373                 var id = sid ? q.selectValue(sid, n) : undefined;
6374                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6375                     var f = fields.items[j];
6376                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6377                     v = f.convert(v);
6378                     values[f.name] = v;
6379                 }
6380                 var record = new recordType(values, id);
6381                 record.node = n;
6382                 records[records.length] = record;
6383             }
6384
6385             return {
6386                 success : success,
6387                 records : records,
6388                 totalRecords : totalRecords || records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402 /**
6403  * @class Roo.data.ArrayReader
6404  * @extends Roo.data.DataReader
6405  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6406  * Each element of that Array represents a row of data fields. The
6407  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6408  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6409  * <p>
6410  * Example code:.
6411  * <pre><code>
6412 var RecordDef = Roo.data.Record.create([
6413     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6414     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6415 ]);
6416 var myReader = new Roo.data.ArrayReader({
6417     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6418 }, RecordDef);
6419 </code></pre>
6420  * <p>
6421  * This would consume an Array like this:
6422  * <pre><code>
6423 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6424   </code></pre>
6425  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6426  * @constructor
6427  * Create a new JsonReader
6428  * @param {Object} meta Metadata configuration options.
6429  * @param {Object} recordType Either an Array of field definition objects
6430  * as specified to {@link Roo.data.Record#create},
6431  * or an {@link Roo.data.Record} object
6432  * created using {@link Roo.data.Record#create}.
6433  */
6434 Roo.data.ArrayReader = function(meta, recordType){
6435     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6436 };
6437
6438 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6439     /**
6440      * Create a data block containing Roo.data.Records from an XML document.
6441      * @param {Object} o An Array of row objects which represents the dataset.
6442      * @return {Object} data A data block which is used by an Roo.data.Store object as
6443      * a cache of Roo.data.Records.
6444      */
6445     readRecords : function(o){
6446         var sid = this.meta ? this.meta.id : null;
6447         var recordType = this.recordType, fields = recordType.prototype.fields;
6448         var records = [];
6449         var root = o;
6450             for(var i = 0; i < root.length; i++){
6451                     var n = root[i];
6452                 var values = {};
6453                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6454                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6455                 var f = fields.items[j];
6456                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6457                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6458                 v = f.convert(v);
6459                 values[f.name] = v;
6460             }
6461                 var record = new recordType(values, id);
6462                 record.json = n;
6463                 records[records.length] = record;
6464             }
6465             return {
6466                 records : records,
6467                 totalRecords : records.length
6468             };
6469     }
6470 });/*
6471  * Based on:
6472  * Ext JS Library 1.1.1
6473  * Copyright(c) 2006-2007, Ext JS, LLC.
6474  *
6475  * Originally Released Under LGPL - original licence link has changed is not relivant.
6476  *
6477  * Fork - LGPL
6478  * <script type="text/javascript">
6479  */
6480
6481
6482 /**
6483  * @class Roo.data.Tree
6484  * @extends Roo.util.Observable
6485  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6486  * in the tree have most standard DOM functionality.
6487  * @constructor
6488  * @param {Node} root (optional) The root node
6489  */
6490 Roo.data.Tree = function(root){
6491    this.nodeHash = {};
6492    /**
6493     * The root node for this tree
6494     * @type Node
6495     */
6496    this.root = null;
6497    if(root){
6498        this.setRootNode(root);
6499    }
6500    this.addEvents({
6501        /**
6502         * @event append
6503         * Fires when a new child node is appended to a node in this tree.
6504         * @param {Tree} tree The owner tree
6505         * @param {Node} parent The parent node
6506         * @param {Node} node The newly appended node
6507         * @param {Number} index The index of the newly appended node
6508         */
6509        "append" : true,
6510        /**
6511         * @event remove
6512         * Fires when a child node is removed from a node in this tree.
6513         * @param {Tree} tree The owner tree
6514         * @param {Node} parent The parent node
6515         * @param {Node} node The child node removed
6516         */
6517        "remove" : true,
6518        /**
6519         * @event move
6520         * Fires when a node is moved to a new location in the tree
6521         * @param {Tree} tree The owner tree
6522         * @param {Node} node The node moved
6523         * @param {Node} oldParent The old parent of this node
6524         * @param {Node} newParent The new parent of this node
6525         * @param {Number} index The index it was moved to
6526         */
6527        "move" : true,
6528        /**
6529         * @event insert
6530         * Fires when a new child node is inserted in a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node inserted
6534         * @param {Node} refNode The child node the node was inserted before
6535         */
6536        "insert" : true,
6537        /**
6538         * @event beforeappend
6539         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6540         * @param {Tree} tree The owner tree
6541         * @param {Node} parent The parent node
6542         * @param {Node} node The child node to be appended
6543         */
6544        "beforeappend" : true,
6545        /**
6546         * @event beforeremove
6547         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The child node to be removed
6551         */
6552        "beforeremove" : true,
6553        /**
6554         * @event beforemove
6555         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6556         * @param {Tree} tree The owner tree
6557         * @param {Node} node The node being moved
6558         * @param {Node} oldParent The parent of the node
6559         * @param {Node} newParent The new parent the node is moving to
6560         * @param {Number} index The index it is being moved to
6561         */
6562        "beforemove" : true,
6563        /**
6564         * @event beforeinsert
6565         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be inserted
6569         * @param {Node} refNode The child node the node is being inserted before
6570         */
6571        "beforeinsert" : true
6572    });
6573
6574     Roo.data.Tree.superclass.constructor.call(this);
6575 };
6576
6577 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6578     pathSeparator: "/",
6579
6580     proxyNodeEvent : function(){
6581         return this.fireEvent.apply(this, arguments);
6582     },
6583
6584     /**
6585      * Returns the root node for this tree.
6586      * @return {Node}
6587      */
6588     getRootNode : function(){
6589         return this.root;
6590     },
6591
6592     /**
6593      * Sets the root node for this tree.
6594      * @param {Node} node
6595      * @return {Node}
6596      */
6597     setRootNode : function(node){
6598         this.root = node;
6599         node.ownerTree = this;
6600         node.isRoot = true;
6601         this.registerNode(node);
6602         return node;
6603     },
6604
6605     /**
6606      * Gets a node in this tree by its id.
6607      * @param {String} id
6608      * @return {Node}
6609      */
6610     getNodeById : function(id){
6611         return this.nodeHash[id];
6612     },
6613
6614     registerNode : function(node){
6615         this.nodeHash[node.id] = node;
6616     },
6617
6618     unregisterNode : function(node){
6619         delete this.nodeHash[node.id];
6620     },
6621
6622     toString : function(){
6623         return "[Tree"+(this.id?" "+this.id:"")+"]";
6624     }
6625 });
6626
6627 /**
6628  * @class Roo.data.Node
6629  * @extends Roo.util.Observable
6630  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6631  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6632  * @constructor
6633  * @param {Object} attributes The attributes/config for the node
6634  */
6635 Roo.data.Node = function(attributes){
6636     /**
6637      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6638      * @type {Object}
6639      */
6640     this.attributes = attributes || {};
6641     this.leaf = this.attributes.leaf;
6642     /**
6643      * The node id. @type String
6644      */
6645     this.id = this.attributes.id;
6646     if(!this.id){
6647         this.id = Roo.id(null, "ynode-");
6648         this.attributes.id = this.id;
6649     }
6650     /**
6651      * All child nodes of this node. @type Array
6652      */
6653     this.childNodes = [];
6654     if(!this.childNodes.indexOf){ // indexOf is a must
6655         this.childNodes.indexOf = function(o){
6656             for(var i = 0, len = this.length; i < len; i++){
6657                 if(this[i] == o) {
6658                     return i;
6659                 }
6660             }
6661             return -1;
6662         };
6663     }
6664     /**
6665      * The parent node for this node. @type Node
6666      */
6667     this.parentNode = null;
6668     /**
6669      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6670      */
6671     this.firstChild = null;
6672     /**
6673      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6674      */
6675     this.lastChild = null;
6676     /**
6677      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6678      */
6679     this.previousSibling = null;
6680     /**
6681      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6682      */
6683     this.nextSibling = null;
6684
6685     this.addEvents({
6686        /**
6687         * @event append
6688         * Fires when a new child node is appended
6689         * @param {Tree} tree The owner tree
6690         * @param {Node} this This node
6691         * @param {Node} node The newly appended node
6692         * @param {Number} index The index of the newly appended node
6693         */
6694        "append" : true,
6695        /**
6696         * @event remove
6697         * Fires when a child node is removed
6698         * @param {Tree} tree The owner tree
6699         * @param {Node} this This node
6700         * @param {Node} node The removed node
6701         */
6702        "remove" : true,
6703        /**
6704         * @event move
6705         * Fires when this node is moved to a new location in the tree
6706         * @param {Tree} tree The owner tree
6707         * @param {Node} this This node
6708         * @param {Node} oldParent The old parent of this node
6709         * @param {Node} newParent The new parent of this node
6710         * @param {Number} index The index it was moved to
6711         */
6712        "move" : true,
6713        /**
6714         * @event insert
6715         * Fires when a new child node is inserted.
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The child node inserted
6719         * @param {Node} refNode The child node the node was inserted before
6720         */
6721        "insert" : true,
6722        /**
6723         * @event beforeappend
6724         * Fires before a new child is appended, return false to cancel the append.
6725         * @param {Tree} tree The owner tree
6726         * @param {Node} this This node
6727         * @param {Node} node The child node to be appended
6728         */
6729        "beforeappend" : true,
6730        /**
6731         * @event beforeremove
6732         * Fires before a child is removed, return false to cancel the remove.
6733         * @param {Tree} tree The owner tree
6734         * @param {Node} this This node
6735         * @param {Node} node The child node to be removed
6736         */
6737        "beforeremove" : true,
6738        /**
6739         * @event beforemove
6740         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6741         * @param {Tree} tree The owner tree
6742         * @param {Node} this This node
6743         * @param {Node} oldParent The parent of this node
6744         * @param {Node} newParent The new parent this node is moving to
6745         * @param {Number} index The index it is being moved to
6746         */
6747        "beforemove" : true,
6748        /**
6749         * @event beforeinsert
6750         * Fires before a new child is inserted, return false to cancel the insert.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be inserted
6754         * @param {Node} refNode The child node the node is being inserted before
6755         */
6756        "beforeinsert" : true
6757    });
6758     this.listeners = this.attributes.listeners;
6759     Roo.data.Node.superclass.constructor.call(this);
6760 };
6761
6762 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6763     fireEvent : function(evtName){
6764         // first do standard event for this node
6765         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6766             return false;
6767         }
6768         // then bubble it up to the tree if the event wasn't cancelled
6769         var ot = this.getOwnerTree();
6770         if(ot){
6771             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6772                 return false;
6773             }
6774         }
6775         return true;
6776     },
6777
6778     /**
6779      * Returns true if this node is a leaf
6780      * @return {Boolean}
6781      */
6782     isLeaf : function(){
6783         return this.leaf === true;
6784     },
6785
6786     // private
6787     setFirstChild : function(node){
6788         this.firstChild = node;
6789     },
6790
6791     //private
6792     setLastChild : function(node){
6793         this.lastChild = node;
6794     },
6795
6796
6797     /**
6798      * Returns true if this node is the last child of its parent
6799      * @return {Boolean}
6800      */
6801     isLast : function(){
6802        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6803     },
6804
6805     /**
6806      * Returns true if this node is the first child of its parent
6807      * @return {Boolean}
6808      */
6809     isFirst : function(){
6810        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6811     },
6812
6813     hasChildNodes : function(){
6814         return !this.isLeaf() && this.childNodes.length > 0;
6815     },
6816
6817     /**
6818      * Insert node(s) as the last child node of this node.
6819      * @param {Node/Array} node The node or Array of nodes to append
6820      * @return {Node} The appended node if single append, or null if an array was passed
6821      */
6822     appendChild : function(node){
6823         var multi = false;
6824         if(node instanceof Array){
6825             multi = node;
6826         }else if(arguments.length > 1){
6827             multi = arguments;
6828         }
6829         // if passed an array or multiple args do them one by one
6830         if(multi){
6831             for(var i = 0, len = multi.length; i < len; i++) {
6832                 this.appendChild(multi[i]);
6833             }
6834         }else{
6835             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6836                 return false;
6837             }
6838             var index = this.childNodes.length;
6839             var oldParent = node.parentNode;
6840             // it's a move, make sure we move it cleanly
6841             if(oldParent){
6842                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6843                     return false;
6844                 }
6845                 oldParent.removeChild(node);
6846             }
6847             index = this.childNodes.length;
6848             if(index == 0){
6849                 this.setFirstChild(node);
6850             }
6851             this.childNodes.push(node);
6852             node.parentNode = this;
6853             var ps = this.childNodes[index-1];
6854             if(ps){
6855                 node.previousSibling = ps;
6856                 ps.nextSibling = node;
6857             }else{
6858                 node.previousSibling = null;
6859             }
6860             node.nextSibling = null;
6861             this.setLastChild(node);
6862             node.setOwnerTree(this.getOwnerTree());
6863             this.fireEvent("append", this.ownerTree, this, node, index);
6864             if(oldParent){
6865                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6866             }
6867             return node;
6868         }
6869     },
6870
6871     /**
6872      * Removes a child node from this node.
6873      * @param {Node} node The node to remove
6874      * @return {Node} The removed node
6875      */
6876     removeChild : function(node){
6877         var index = this.childNodes.indexOf(node);
6878         if(index == -1){
6879             return false;
6880         }
6881         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6882             return false;
6883         }
6884
6885         // remove it from childNodes collection
6886         this.childNodes.splice(index, 1);
6887
6888         // update siblings
6889         if(node.previousSibling){
6890             node.previousSibling.nextSibling = node.nextSibling;
6891         }
6892         if(node.nextSibling){
6893             node.nextSibling.previousSibling = node.previousSibling;
6894         }
6895
6896         // update child refs
6897         if(this.firstChild == node){
6898             this.setFirstChild(node.nextSibling);
6899         }
6900         if(this.lastChild == node){
6901             this.setLastChild(node.previousSibling);
6902         }
6903
6904         node.setOwnerTree(null);
6905         // clear any references from the node
6906         node.parentNode = null;
6907         node.previousSibling = null;
6908         node.nextSibling = null;
6909         this.fireEvent("remove", this.ownerTree, this, node);
6910         return node;
6911     },
6912
6913     /**
6914      * Inserts the first node before the second node in this nodes childNodes collection.
6915      * @param {Node} node The node to insert
6916      * @param {Node} refNode The node to insert before (if null the node is appended)
6917      * @return {Node} The inserted node
6918      */
6919     insertBefore : function(node, refNode){
6920         if(!refNode){ // like standard Dom, refNode can be null for append
6921             return this.appendChild(node);
6922         }
6923         // nothing to do
6924         if(node == refNode){
6925             return false;
6926         }
6927
6928         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6929             return false;
6930         }
6931         var index = this.childNodes.indexOf(refNode);
6932         var oldParent = node.parentNode;
6933         var refIndex = index;
6934
6935         // when moving internally, indexes will change after remove
6936         if(oldParent == this && this.childNodes.indexOf(node) < index){
6937             refIndex--;
6938         }
6939
6940         // it's a move, make sure we move it cleanly
6941         if(oldParent){
6942             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6943                 return false;
6944             }
6945             oldParent.removeChild(node);
6946         }
6947         if(refIndex == 0){
6948             this.setFirstChild(node);
6949         }
6950         this.childNodes.splice(refIndex, 0, node);
6951         node.parentNode = this;
6952         var ps = this.childNodes[refIndex-1];
6953         if(ps){
6954             node.previousSibling = ps;
6955             ps.nextSibling = node;
6956         }else{
6957             node.previousSibling = null;
6958         }
6959         node.nextSibling = refNode;
6960         refNode.previousSibling = node;
6961         node.setOwnerTree(this.getOwnerTree());
6962         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6963         if(oldParent){
6964             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6965         }
6966         return node;
6967     },
6968
6969     /**
6970      * Returns the child node at the specified index.
6971      * @param {Number} index
6972      * @return {Node}
6973      */
6974     item : function(index){
6975         return this.childNodes[index];
6976     },
6977
6978     /**
6979      * Replaces one child node in this node with another.
6980      * @param {Node} newChild The replacement node
6981      * @param {Node} oldChild The node to replace
6982      * @return {Node} The replaced node
6983      */
6984     replaceChild : function(newChild, oldChild){
6985         this.insertBefore(newChild, oldChild);
6986         this.removeChild(oldChild);
6987         return oldChild;
6988     },
6989
6990     /**
6991      * Returns the index of a child node
6992      * @param {Node} node
6993      * @return {Number} The index of the node or -1 if it was not found
6994      */
6995     indexOf : function(child){
6996         return this.childNodes.indexOf(child);
6997     },
6998
6999     /**
7000      * Returns the tree this node is in.
7001      * @return {Tree}
7002      */
7003     getOwnerTree : function(){
7004         // if it doesn't have one, look for one
7005         if(!this.ownerTree){
7006             var p = this;
7007             while(p){
7008                 if(p.ownerTree){
7009                     this.ownerTree = p.ownerTree;
7010                     break;
7011                 }
7012                 p = p.parentNode;
7013             }
7014         }
7015         return this.ownerTree;
7016     },
7017
7018     /**
7019      * Returns depth of this node (the root node has a depth of 0)
7020      * @return {Number}
7021      */
7022     getDepth : function(){
7023         var depth = 0;
7024         var p = this;
7025         while(p.parentNode){
7026             ++depth;
7027             p = p.parentNode;
7028         }
7029         return depth;
7030     },
7031
7032     // private
7033     setOwnerTree : function(tree){
7034         // if it's move, we need to update everyone
7035         if(tree != this.ownerTree){
7036             if(this.ownerTree){
7037                 this.ownerTree.unregisterNode(this);
7038             }
7039             this.ownerTree = tree;
7040             var cs = this.childNodes;
7041             for(var i = 0, len = cs.length; i < len; i++) {
7042                 cs[i].setOwnerTree(tree);
7043             }
7044             if(tree){
7045                 tree.registerNode(this);
7046             }
7047         }
7048     },
7049
7050     /**
7051      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7052      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7053      * @return {String} The path
7054      */
7055     getPath : function(attr){
7056         attr = attr || "id";
7057         var p = this.parentNode;
7058         var b = [this.attributes[attr]];
7059         while(p){
7060             b.unshift(p.attributes[attr]);
7061             p = p.parentNode;
7062         }
7063         var sep = this.getOwnerTree().pathSeparator;
7064         return sep + b.join(sep);
7065     },
7066
7067     /**
7068      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7069      * function call will be the scope provided or the current node. The arguments to the function
7070      * will be the args provided or the current node. If the function returns false at any point,
7071      * the bubble is stopped.
7072      * @param {Function} fn The function to call
7073      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7074      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7075      */
7076     bubble : function(fn, scope, args){
7077         var p = this;
7078         while(p){
7079             if(fn.call(scope || p, args || p) === false){
7080                 break;
7081             }
7082             p = p.parentNode;
7083         }
7084     },
7085
7086     /**
7087      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7088      * function call will be the scope provided or the current node. The arguments to the function
7089      * will be the args provided or the current node. If the function returns false at any point,
7090      * the cascade is stopped on that branch.
7091      * @param {Function} fn The function to call
7092      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7093      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7094      */
7095     cascade : function(fn, scope, args){
7096         if(fn.call(scope || this, args || this) !== false){
7097             var cs = this.childNodes;
7098             for(var i = 0, len = cs.length; i < len; i++) {
7099                 cs[i].cascade(fn, scope, args);
7100             }
7101         }
7102     },
7103
7104     /**
7105      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the iteration stops.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     eachChild : function(fn, scope, args){
7114         var cs = this.childNodes;
7115         for(var i = 0, len = cs.length; i < len; i++) {
7116                 if(fn.call(scope || this, args || cs[i]) === false){
7117                     break;
7118                 }
7119         }
7120     },
7121
7122     /**
7123      * Finds the first child that has the attribute with the specified value.
7124      * @param {String} attribute The attribute name
7125      * @param {Mixed} value The value to search for
7126      * @return {Node} The found child or null if none was found
7127      */
7128     findChild : function(attribute, value){
7129         var cs = this.childNodes;
7130         for(var i = 0, len = cs.length; i < len; i++) {
7131                 if(cs[i].attributes[attribute] == value){
7132                     return cs[i];
7133                 }
7134         }
7135         return null;
7136     },
7137
7138     /**
7139      * Finds the first child by a custom function. The child matches if the function passed
7140      * returns true.
7141      * @param {Function} fn
7142      * @param {Object} scope (optional)
7143      * @return {Node} The found child or null if none was found
7144      */
7145     findChildBy : function(fn, scope){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope||cs[i], cs[i]) === true){
7149                     return cs[i];
7150                 }
7151         }
7152         return null;
7153     },
7154
7155     /**
7156      * Sorts this nodes children using the supplied sort function
7157      * @param {Function} fn
7158      * @param {Object} scope (optional)
7159      */
7160     sort : function(fn, scope){
7161         var cs = this.childNodes;
7162         var len = cs.length;
7163         if(len > 0){
7164             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7165             cs.sort(sortFn);
7166             for(var i = 0; i < len; i++){
7167                 var n = cs[i];
7168                 n.previousSibling = cs[i-1];
7169                 n.nextSibling = cs[i+1];
7170                 if(i == 0){
7171                     this.setFirstChild(n);
7172                 }
7173                 if(i == len-1){
7174                     this.setLastChild(n);
7175                 }
7176             }
7177         }
7178     },
7179
7180     /**
7181      * Returns true if this node is an ancestor (at any point) of the passed node.
7182      * @param {Node} node
7183      * @return {Boolean}
7184      */
7185     contains : function(node){
7186         return node.isAncestor(this);
7187     },
7188
7189     /**
7190      * Returns true if the passed node is an ancestor (at any point) of this node.
7191      * @param {Node} node
7192      * @return {Boolean}
7193      */
7194     isAncestor : function(node){
7195         var p = this.parentNode;
7196         while(p){
7197             if(p == node){
7198                 return true;
7199             }
7200             p = p.parentNode;
7201         }
7202         return false;
7203     },
7204
7205     toString : function(){
7206         return "[Node"+(this.id?" "+this.id:"")+"]";
7207     }
7208 });/*
7209  * Based on:
7210  * Ext JS Library 1.1.1
7211  * Copyright(c) 2006-2007, Ext JS, LLC.
7212  *
7213  * Originally Released Under LGPL - original licence link has changed is not relivant.
7214  *
7215  * Fork - LGPL
7216  * <script type="text/javascript">
7217  */
7218  
7219
7220 /**
7221  * @class Roo.ComponentMgr
7222  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7223  * @singleton
7224  */
7225 Roo.ComponentMgr = function(){
7226     var all = new Roo.util.MixedCollection();
7227
7228     return {
7229         /**
7230          * Registers a component.
7231          * @param {Roo.Component} c The component
7232          */
7233         register : function(c){
7234             all.add(c);
7235         },
7236
7237         /**
7238          * Unregisters a component.
7239          * @param {Roo.Component} c The component
7240          */
7241         unregister : function(c){
7242             all.remove(c);
7243         },
7244
7245         /**
7246          * Returns a component by id
7247          * @param {String} id The component id
7248          */
7249         get : function(id){
7250             return all.get(id);
7251         },
7252
7253         /**
7254          * Registers a function that will be called when a specified component is added to ComponentMgr
7255          * @param {String} id The component id
7256          * @param {Funtction} fn The callback function
7257          * @param {Object} scope The scope of the callback
7258          */
7259         onAvailable : function(id, fn, scope){
7260             all.on("add", function(index, o){
7261                 if(o.id == id){
7262                     fn.call(scope || o, o);
7263                     all.un("add", fn, scope);
7264                 }
7265             });
7266         }
7267     };
7268 }();/*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279 /**
7280  * @class Roo.Component
7281  * @extends Roo.util.Observable
7282  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7283  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7284  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7285  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7286  * All visual components (widgets) that require rendering into a layout should subclass Component.
7287  * @constructor
7288  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7289  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7290  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7291  */
7292 Roo.Component = function(config){
7293     config = config || {};
7294     if(config.tagName || config.dom || typeof config == "string"){ // element object
7295         config = {el: config, id: config.id || config};
7296     }
7297     this.initialConfig = config;
7298
7299     Roo.apply(this, config);
7300     this.addEvents({
7301         /**
7302          * @event disable
7303          * Fires after the component is disabled.
7304              * @param {Roo.Component} this
7305              */
7306         disable : true,
7307         /**
7308          * @event enable
7309          * Fires after the component is enabled.
7310              * @param {Roo.Component} this
7311              */
7312         enable : true,
7313         /**
7314          * @event beforeshow
7315          * Fires before the component is shown.  Return false to stop the show.
7316              * @param {Roo.Component} this
7317              */
7318         beforeshow : true,
7319         /**
7320          * @event show
7321          * Fires after the component is shown.
7322              * @param {Roo.Component} this
7323              */
7324         show : true,
7325         /**
7326          * @event beforehide
7327          * Fires before the component is hidden. Return false to stop the hide.
7328              * @param {Roo.Component} this
7329              */
7330         beforehide : true,
7331         /**
7332          * @event hide
7333          * Fires after the component is hidden.
7334              * @param {Roo.Component} this
7335              */
7336         hide : true,
7337         /**
7338          * @event beforerender
7339          * Fires before the component is rendered. Return false to stop the render.
7340              * @param {Roo.Component} this
7341              */
7342         beforerender : true,
7343         /**
7344          * @event render
7345          * Fires after the component is rendered.
7346              * @param {Roo.Component} this
7347              */
7348         render : true,
7349         /**
7350          * @event beforedestroy
7351          * Fires before the component is destroyed. Return false to stop the destroy.
7352              * @param {Roo.Component} this
7353              */
7354         beforedestroy : true,
7355         /**
7356          * @event destroy
7357          * Fires after the component is destroyed.
7358              * @param {Roo.Component} this
7359              */
7360         destroy : true
7361     });
7362     if(!this.id){
7363         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7364     }
7365     Roo.ComponentMgr.register(this);
7366     Roo.Component.superclass.constructor.call(this);
7367     this.initComponent();
7368     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7369         this.render(this.renderTo);
7370         delete this.renderTo;
7371     }
7372 };
7373
7374 // private
7375 Roo.Component.AUTO_ID = 1000;
7376
7377 Roo.extend(Roo.Component, Roo.util.Observable, {
7378     /**
7379      * @property {Boolean} hidden
7380      * true if this component is hidden. Read-only.
7381      */
7382     hidden : false,
7383     /**
7384      * true if this component is disabled. Read-only.
7385      */
7386     disabled : false,
7387     /**
7388      * true if this component has been rendered. Read-only.
7389      */
7390     rendered : false,
7391     
7392     /** @cfg {String} disableClass
7393      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7394      */
7395     disabledClass : "x-item-disabled",
7396         /** @cfg {Boolean} allowDomMove
7397          * Whether the component can move the Dom node when rendering (defaults to true).
7398          */
7399     allowDomMove : true,
7400     /** @cfg {String} hideMode
7401      * How this component should hidden. Supported values are
7402      * "visibility" (css visibility), "offsets" (negative offset position) and
7403      * "display" (css display) - defaults to "display".
7404      */
7405     hideMode: 'display',
7406
7407     // private
7408     ctype : "Roo.Component",
7409
7410     /** @cfg {String} actionMode 
7411      * which property holds the element that used for  hide() / show() / disable() / enable()
7412      * default is 'el' 
7413      */
7414     actionMode : "el",
7415
7416     // private
7417     getActionEl : function(){
7418         return this[this.actionMode];
7419     },
7420
7421     initComponent : Roo.emptyFn,
7422     /**
7423      * If this is a lazy rendering component, render it to its container element.
7424      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7425      */
7426     render : function(container, position){
7427         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7428             if(!container && this.el){
7429                 this.el = Roo.get(this.el);
7430                 container = this.el.dom.parentNode;
7431                 this.allowDomMove = false;
7432             }
7433             this.container = Roo.get(container);
7434             this.rendered = true;
7435             if(position !== undefined){
7436                 if(typeof position == 'number'){
7437                     position = this.container.dom.childNodes[position];
7438                 }else{
7439                     position = Roo.getDom(position);
7440                 }
7441             }
7442             this.onRender(this.container, position || null);
7443             if(this.cls){
7444                 this.el.addClass(this.cls);
7445                 delete this.cls;
7446             }
7447             if(this.style){
7448                 this.el.applyStyles(this.style);
7449                 delete this.style;
7450             }
7451             this.fireEvent("render", this);
7452             this.afterRender(this.container);
7453             if(this.hidden){
7454                 this.hide();
7455             }
7456             if(this.disabled){
7457                 this.disable();
7458             }
7459         }
7460         return this;
7461     },
7462
7463     // private
7464     // default function is not really useful
7465     onRender : function(ct, position){
7466         if(this.el){
7467             this.el = Roo.get(this.el);
7468             if(this.allowDomMove !== false){
7469                 ct.dom.insertBefore(this.el.dom, position);
7470             }
7471         }
7472     },
7473
7474     // private
7475     getAutoCreate : function(){
7476         var cfg = typeof this.autoCreate == "object" ?
7477                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7478         if(this.id && !cfg.id){
7479             cfg.id = this.id;
7480         }
7481         return cfg;
7482     },
7483
7484     // private
7485     afterRender : Roo.emptyFn,
7486
7487     /**
7488      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7489      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7490      */
7491     destroy : function(){
7492         if(this.fireEvent("beforedestroy", this) !== false){
7493             this.purgeListeners();
7494             this.beforeDestroy();
7495             if(this.rendered){
7496                 this.el.removeAllListeners();
7497                 this.el.remove();
7498                 if(this.actionMode == "container"){
7499                     this.container.remove();
7500                 }
7501             }
7502             this.onDestroy();
7503             Roo.ComponentMgr.unregister(this);
7504             this.fireEvent("destroy", this);
7505         }
7506     },
7507
7508         // private
7509     beforeDestroy : function(){
7510
7511     },
7512
7513         // private
7514         onDestroy : function(){
7515
7516     },
7517
7518     /**
7519      * Returns the underlying {@link Roo.Element}.
7520      * @return {Roo.Element} The element
7521      */
7522     getEl : function(){
7523         return this.el;
7524     },
7525
7526     /**
7527      * Returns the id of this component.
7528      * @return {String}
7529      */
7530     getId : function(){
7531         return this.id;
7532     },
7533
7534     /**
7535      * Try to focus this component.
7536      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7537      * @return {Roo.Component} this
7538      */
7539     focus : function(selectText){
7540         if(this.rendered){
7541             this.el.focus();
7542             if(selectText === true){
7543                 this.el.dom.select();
7544             }
7545         }
7546         return this;
7547     },
7548
7549     // private
7550     blur : function(){
7551         if(this.rendered){
7552             this.el.blur();
7553         }
7554         return this;
7555     },
7556
7557     /**
7558      * Disable this component.
7559      * @return {Roo.Component} this
7560      */
7561     disable : function(){
7562         if(this.rendered){
7563             this.onDisable();
7564         }
7565         this.disabled = true;
7566         this.fireEvent("disable", this);
7567         return this;
7568     },
7569
7570         // private
7571     onDisable : function(){
7572         this.getActionEl().addClass(this.disabledClass);
7573         this.el.dom.disabled = true;
7574     },
7575
7576     /**
7577      * Enable this component.
7578      * @return {Roo.Component} this
7579      */
7580     enable : function(){
7581         if(this.rendered){
7582             this.onEnable();
7583         }
7584         this.disabled = false;
7585         this.fireEvent("enable", this);
7586         return this;
7587     },
7588
7589         // private
7590     onEnable : function(){
7591         this.getActionEl().removeClass(this.disabledClass);
7592         this.el.dom.disabled = false;
7593     },
7594
7595     /**
7596      * Convenience function for setting disabled/enabled by boolean.
7597      * @param {Boolean} disabled
7598      */
7599     setDisabled : function(disabled){
7600         this[disabled ? "disable" : "enable"]();
7601     },
7602
7603     /**
7604      * Show this component.
7605      * @return {Roo.Component} this
7606      */
7607     show: function(){
7608         if(this.fireEvent("beforeshow", this) !== false){
7609             this.hidden = false;
7610             if(this.rendered){
7611                 this.onShow();
7612             }
7613             this.fireEvent("show", this);
7614         }
7615         return this;
7616     },
7617
7618     // private
7619     onShow : function(){
7620         var ae = this.getActionEl();
7621         if(this.hideMode == 'visibility'){
7622             ae.dom.style.visibility = "visible";
7623         }else if(this.hideMode == 'offsets'){
7624             ae.removeClass('x-hidden');
7625         }else{
7626             ae.dom.style.display = "";
7627         }
7628     },
7629
7630     /**
7631      * Hide this component.
7632      * @return {Roo.Component} this
7633      */
7634     hide: function(){
7635         if(this.fireEvent("beforehide", this) !== false){
7636             this.hidden = true;
7637             if(this.rendered){
7638                 this.onHide();
7639             }
7640             this.fireEvent("hide", this);
7641         }
7642         return this;
7643     },
7644
7645     // private
7646     onHide : function(){
7647         var ae = this.getActionEl();
7648         if(this.hideMode == 'visibility'){
7649             ae.dom.style.visibility = "hidden";
7650         }else if(this.hideMode == 'offsets'){
7651             ae.addClass('x-hidden');
7652         }else{
7653             ae.dom.style.display = "none";
7654         }
7655     },
7656
7657     /**
7658      * Convenience function to hide or show this component by boolean.
7659      * @param {Boolean} visible True to show, false to hide
7660      * @return {Roo.Component} this
7661      */
7662     setVisible: function(visible){
7663         if(visible) {
7664             this.show();
7665         }else{
7666             this.hide();
7667         }
7668         return this;
7669     },
7670
7671     /**
7672      * Returns true if this component is visible.
7673      */
7674     isVisible : function(){
7675         return this.getActionEl().isVisible();
7676     },
7677
7678     cloneConfig : function(overrides){
7679         overrides = overrides || {};
7680         var id = overrides.id || Roo.id();
7681         var cfg = Roo.applyIf(overrides, this.initialConfig);
7682         cfg.id = id; // prevent dup id
7683         return new this.constructor(cfg);
7684     }
7685 });/*
7686  * Based on:
7687  * Ext JS Library 1.1.1
7688  * Copyright(c) 2006-2007, Ext JS, LLC.
7689  *
7690  * Originally Released Under LGPL - original licence link has changed is not relivant.
7691  *
7692  * Fork - LGPL
7693  * <script type="text/javascript">
7694  */
7695  (function(){ 
7696 /**
7697  * @class Roo.Layer
7698  * @extends Roo.Element
7699  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7700  * automatic maintaining of shadow/shim positions.
7701  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7702  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7703  * you can pass a string with a CSS class name. False turns off the shadow.
7704  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7705  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7706  * @cfg {String} cls CSS class to add to the element
7707  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7708  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7709  * @constructor
7710  * @param {Object} config An object with config options.
7711  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7712  */
7713
7714 Roo.Layer = function(config, existingEl){
7715     config = config || {};
7716     var dh = Roo.DomHelper;
7717     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7718     if(existingEl){
7719         this.dom = Roo.getDom(existingEl);
7720     }
7721     if(!this.dom){
7722         var o = config.dh || {tag: "div", cls: "x-layer"};
7723         this.dom = dh.append(pel, o);
7724     }
7725     if(config.cls){
7726         this.addClass(config.cls);
7727     }
7728     this.constrain = config.constrain !== false;
7729     this.visibilityMode = Roo.Element.VISIBILITY;
7730     if(config.id){
7731         this.id = this.dom.id = config.id;
7732     }else{
7733         this.id = Roo.id(this.dom);
7734     }
7735     this.zindex = config.zindex || this.getZIndex();
7736     this.position("absolute", this.zindex);
7737     if(config.shadow){
7738         this.shadowOffset = config.shadowOffset || 4;
7739         this.shadow = new Roo.Shadow({
7740             offset : this.shadowOffset,
7741             mode : config.shadow
7742         });
7743     }else{
7744         this.shadowOffset = 0;
7745     }
7746     this.useShim = config.shim !== false && Roo.useShims;
7747     this.useDisplay = config.useDisplay;
7748     this.hide();
7749 };
7750
7751 var supr = Roo.Element.prototype;
7752
7753 // shims are shared among layer to keep from having 100 iframes
7754 var shims = [];
7755
7756 Roo.extend(Roo.Layer, Roo.Element, {
7757
7758     getZIndex : function(){
7759         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7760     },
7761
7762     getShim : function(){
7763         if(!this.useShim){
7764             return null;
7765         }
7766         if(this.shim){
7767             return this.shim;
7768         }
7769         var shim = shims.shift();
7770         if(!shim){
7771             shim = this.createShim();
7772             shim.enableDisplayMode('block');
7773             shim.dom.style.display = 'none';
7774             shim.dom.style.visibility = 'visible';
7775         }
7776         var pn = this.dom.parentNode;
7777         if(shim.dom.parentNode != pn){
7778             pn.insertBefore(shim.dom, this.dom);
7779         }
7780         shim.setStyle('z-index', this.getZIndex()-2);
7781         this.shim = shim;
7782         return shim;
7783     },
7784
7785     hideShim : function(){
7786         if(this.shim){
7787             this.shim.setDisplayed(false);
7788             shims.push(this.shim);
7789             delete this.shim;
7790         }
7791     },
7792
7793     disableShadow : function(){
7794         if(this.shadow){
7795             this.shadowDisabled = true;
7796             this.shadow.hide();
7797             this.lastShadowOffset = this.shadowOffset;
7798             this.shadowOffset = 0;
7799         }
7800     },
7801
7802     enableShadow : function(show){
7803         if(this.shadow){
7804             this.shadowDisabled = false;
7805             this.shadowOffset = this.lastShadowOffset;
7806             delete this.lastShadowOffset;
7807             if(show){
7808                 this.sync(true);
7809             }
7810         }
7811     },
7812
7813     // private
7814     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7815     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7816     sync : function(doShow){
7817         var sw = this.shadow;
7818         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7819             var sh = this.getShim();
7820
7821             var w = this.getWidth(),
7822                 h = this.getHeight();
7823
7824             var l = this.getLeft(true),
7825                 t = this.getTop(true);
7826
7827             if(sw && !this.shadowDisabled){
7828                 if(doShow && !sw.isVisible()){
7829                     sw.show(this);
7830                 }else{
7831                     sw.realign(l, t, w, h);
7832                 }
7833                 if(sh){
7834                     if(doShow){
7835                        sh.show();
7836                     }
7837                     // fit the shim behind the shadow, so it is shimmed too
7838                     var a = sw.adjusts, s = sh.dom.style;
7839                     s.left = (Math.min(l, l+a.l))+"px";
7840                     s.top = (Math.min(t, t+a.t))+"px";
7841                     s.width = (w+a.w)+"px";
7842                     s.height = (h+a.h)+"px";
7843                 }
7844             }else if(sh){
7845                 if(doShow){
7846                    sh.show();
7847                 }
7848                 sh.setSize(w, h);
7849                 sh.setLeftTop(l, t);
7850             }
7851             
7852         }
7853     },
7854
7855     // private
7856     destroy : function(){
7857         this.hideShim();
7858         if(this.shadow){
7859             this.shadow.hide();
7860         }
7861         this.removeAllListeners();
7862         var pn = this.dom.parentNode;
7863         if(pn){
7864             pn.removeChild(this.dom);
7865         }
7866         Roo.Element.uncache(this.id);
7867     },
7868
7869     remove : function(){
7870         this.destroy();
7871     },
7872
7873     // private
7874     beginUpdate : function(){
7875         this.updating = true;
7876     },
7877
7878     // private
7879     endUpdate : function(){
7880         this.updating = false;
7881         this.sync(true);
7882     },
7883
7884     // private
7885     hideUnders : function(negOffset){
7886         if(this.shadow){
7887             this.shadow.hide();
7888         }
7889         this.hideShim();
7890     },
7891
7892     // private
7893     constrainXY : function(){
7894         if(this.constrain){
7895             var vw = Roo.lib.Dom.getViewWidth(),
7896                 vh = Roo.lib.Dom.getViewHeight();
7897             var s = Roo.get(document).getScroll();
7898
7899             var xy = this.getXY();
7900             var x = xy[0], y = xy[1];   
7901             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7902             // only move it if it needs it
7903             var moved = false;
7904             // first validate right/bottom
7905             if((x + w) > vw+s.left){
7906                 x = vw - w - this.shadowOffset;
7907                 moved = true;
7908             }
7909             if((y + h) > vh+s.top){
7910                 y = vh - h - this.shadowOffset;
7911                 moved = true;
7912             }
7913             // then make sure top/left isn't negative
7914             if(x < s.left){
7915                 x = s.left;
7916                 moved = true;
7917             }
7918             if(y < s.top){
7919                 y = s.top;
7920                 moved = true;
7921             }
7922             if(moved){
7923                 if(this.avoidY){
7924                     var ay = this.avoidY;
7925                     if(y <= ay && (y+h) >= ay){
7926                         y = ay-h-5;   
7927                     }
7928                 }
7929                 xy = [x, y];
7930                 this.storeXY(xy);
7931                 supr.setXY.call(this, xy);
7932                 this.sync();
7933             }
7934         }
7935     },
7936
7937     isVisible : function(){
7938         return this.visible;    
7939     },
7940
7941     // private
7942     showAction : function(){
7943         this.visible = true; // track visibility to prevent getStyle calls
7944         if(this.useDisplay === true){
7945             this.setDisplayed("");
7946         }else if(this.lastXY){
7947             supr.setXY.call(this, this.lastXY);
7948         }else if(this.lastLT){
7949             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7950         }
7951     },
7952
7953     // private
7954     hideAction : function(){
7955         this.visible = false;
7956         if(this.useDisplay === true){
7957             this.setDisplayed(false);
7958         }else{
7959             this.setLeftTop(-10000,-10000);
7960         }
7961     },
7962
7963     // overridden Element method
7964     setVisible : function(v, a, d, c, e){
7965         if(v){
7966             this.showAction();
7967         }
7968         if(a && v){
7969             var cb = function(){
7970                 this.sync(true);
7971                 if(c){
7972                     c();
7973                 }
7974             }.createDelegate(this);
7975             supr.setVisible.call(this, true, true, d, cb, e);
7976         }else{
7977             if(!v){
7978                 this.hideUnders(true);
7979             }
7980             var cb = c;
7981             if(a){
7982                 cb = function(){
7983                     this.hideAction();
7984                     if(c){
7985                         c();
7986                     }
7987                 }.createDelegate(this);
7988             }
7989             supr.setVisible.call(this, v, a, d, cb, e);
7990             if(v){
7991                 this.sync(true);
7992             }else if(!a){
7993                 this.hideAction();
7994             }
7995         }
7996     },
7997
7998     storeXY : function(xy){
7999         delete this.lastLT;
8000         this.lastXY = xy;
8001     },
8002
8003     storeLeftTop : function(left, top){
8004         delete this.lastXY;
8005         this.lastLT = [left, top];
8006     },
8007
8008     // private
8009     beforeFx : function(){
8010         this.beforeAction();
8011         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8012     },
8013
8014     // private
8015     afterFx : function(){
8016         Roo.Layer.superclass.afterFx.apply(this, arguments);
8017         this.sync(this.isVisible());
8018     },
8019
8020     // private
8021     beforeAction : function(){
8022         if(!this.updating && this.shadow){
8023             this.shadow.hide();
8024         }
8025     },
8026
8027     // overridden Element method
8028     setLeft : function(left){
8029         this.storeLeftTop(left, this.getTop(true));
8030         supr.setLeft.apply(this, arguments);
8031         this.sync();
8032     },
8033
8034     setTop : function(top){
8035         this.storeLeftTop(this.getLeft(true), top);
8036         supr.setTop.apply(this, arguments);
8037         this.sync();
8038     },
8039
8040     setLeftTop : function(left, top){
8041         this.storeLeftTop(left, top);
8042         supr.setLeftTop.apply(this, arguments);
8043         this.sync();
8044     },
8045
8046     setXY : function(xy, a, d, c, e){
8047         this.fixDisplay();
8048         this.beforeAction();
8049         this.storeXY(xy);
8050         var cb = this.createCB(c);
8051         supr.setXY.call(this, xy, a, d, cb, e);
8052         if(!a){
8053             cb();
8054         }
8055     },
8056
8057     // private
8058     createCB : function(c){
8059         var el = this;
8060         return function(){
8061             el.constrainXY();
8062             el.sync(true);
8063             if(c){
8064                 c();
8065             }
8066         };
8067     },
8068
8069     // overridden Element method
8070     setX : function(x, a, d, c, e){
8071         this.setXY([x, this.getY()], a, d, c, e);
8072     },
8073
8074     // overridden Element method
8075     setY : function(y, a, d, c, e){
8076         this.setXY([this.getX(), y], a, d, c, e);
8077     },
8078
8079     // overridden Element method
8080     setSize : function(w, h, a, d, c, e){
8081         this.beforeAction();
8082         var cb = this.createCB(c);
8083         supr.setSize.call(this, w, h, a, d, cb, e);
8084         if(!a){
8085             cb();
8086         }
8087     },
8088
8089     // overridden Element method
8090     setWidth : function(w, a, d, c, e){
8091         this.beforeAction();
8092         var cb = this.createCB(c);
8093         supr.setWidth.call(this, w, a, d, cb, e);
8094         if(!a){
8095             cb();
8096         }
8097     },
8098
8099     // overridden Element method
8100     setHeight : function(h, a, d, c, e){
8101         this.beforeAction();
8102         var cb = this.createCB(c);
8103         supr.setHeight.call(this, h, a, d, cb, e);
8104         if(!a){
8105             cb();
8106         }
8107     },
8108
8109     // overridden Element method
8110     setBounds : function(x, y, w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         if(!a){
8114             this.storeXY([x, y]);
8115             supr.setXY.call(this, [x, y]);
8116             supr.setSize.call(this, w, h, a, d, cb, e);
8117             cb();
8118         }else{
8119             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8120         }
8121         return this;
8122     },
8123     
8124     /**
8125      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8126      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8127      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8128      * @param {Number} zindex The new z-index to set
8129      * @return {this} The Layer
8130      */
8131     setZIndex : function(zindex){
8132         this.zindex = zindex;
8133         this.setStyle("z-index", zindex + 2);
8134         if(this.shadow){
8135             this.shadow.setZIndex(zindex + 1);
8136         }
8137         if(this.shim){
8138             this.shim.setStyle("z-index", zindex);
8139         }
8140     }
8141 });
8142 })();/*
8143  * Based on:
8144  * Ext JS Library 1.1.1
8145  * Copyright(c) 2006-2007, Ext JS, LLC.
8146  *
8147  * Originally Released Under LGPL - original licence link has changed is not relivant.
8148  *
8149  * Fork - LGPL
8150  * <script type="text/javascript">
8151  */
8152
8153
8154 /**
8155  * @class Roo.Shadow
8156  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8157  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8158  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8159  * @constructor
8160  * Create a new Shadow
8161  * @param {Object} config The config object
8162  */
8163 Roo.Shadow = function(config){
8164     Roo.apply(this, config);
8165     if(typeof this.mode != "string"){
8166         this.mode = this.defaultMode;
8167     }
8168     var o = this.offset, a = {h: 0};
8169     var rad = Math.floor(this.offset/2);
8170     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8171         case "drop":
8172             a.w = 0;
8173             a.l = a.t = o;
8174             a.t -= 1;
8175             if(Roo.isIE){
8176                 a.l -= this.offset + rad;
8177                 a.t -= this.offset + rad;
8178                 a.w -= rad;
8179                 a.h -= rad;
8180                 a.t += 1;
8181             }
8182         break;
8183         case "sides":
8184             a.w = (o*2);
8185             a.l = -o;
8186             a.t = o-1;
8187             if(Roo.isIE){
8188                 a.l -= (this.offset - rad);
8189                 a.t -= this.offset + rad;
8190                 a.l += 1;
8191                 a.w -= (this.offset - rad)*2;
8192                 a.w -= rad + 1;
8193                 a.h -= 1;
8194             }
8195         break;
8196         case "frame":
8197             a.w = a.h = (o*2);
8198             a.l = a.t = -o;
8199             a.t += 1;
8200             a.h -= 2;
8201             if(Roo.isIE){
8202                 a.l -= (this.offset - rad);
8203                 a.t -= (this.offset - rad);
8204                 a.l += 1;
8205                 a.w -= (this.offset + rad + 1);
8206                 a.h -= (this.offset + rad);
8207                 a.h += 1;
8208             }
8209         break;
8210     };
8211
8212     this.adjusts = a;
8213 };
8214
8215 Roo.Shadow.prototype = {
8216     /**
8217      * @cfg {String} mode
8218      * The shadow display mode.  Supports the following options:<br />
8219      * sides: Shadow displays on both sides and bottom only<br />
8220      * frame: Shadow displays equally on all four sides<br />
8221      * drop: Traditional bottom-right drop shadow (default)
8222      */
8223     /**
8224      * @cfg {String} offset
8225      * The number of pixels to offset the shadow from the element (defaults to 4)
8226      */
8227     offset: 4,
8228
8229     // private
8230     defaultMode: "drop",
8231
8232     /**
8233      * Displays the shadow under the target element
8234      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8235      */
8236     show : function(target){
8237         target = Roo.get(target);
8238         if(!this.el){
8239             this.el = Roo.Shadow.Pool.pull();
8240             if(this.el.dom.nextSibling != target.dom){
8241                 this.el.insertBefore(target);
8242             }
8243         }
8244         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8245         if(Roo.isIE){
8246             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8247         }
8248         this.realign(
8249             target.getLeft(true),
8250             target.getTop(true),
8251             target.getWidth(),
8252             target.getHeight()
8253         );
8254         this.el.dom.style.display = "block";
8255     },
8256
8257     /**
8258      * Returns true if the shadow is visible, else false
8259      */
8260     isVisible : function(){
8261         return this.el ? true : false;  
8262     },
8263
8264     /**
8265      * Direct alignment when values are already available. Show must be called at least once before
8266      * calling this method to ensure it is initialized.
8267      * @param {Number} left The target element left position
8268      * @param {Number} top The target element top position
8269      * @param {Number} width The target element width
8270      * @param {Number} height The target element height
8271      */
8272     realign : function(l, t, w, h){
8273         if(!this.el){
8274             return;
8275         }
8276         var a = this.adjusts, d = this.el.dom, s = d.style;
8277         var iea = 0;
8278         s.left = (l+a.l)+"px";
8279         s.top = (t+a.t)+"px";
8280         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8281  
8282         if(s.width != sws || s.height != shs){
8283             s.width = sws;
8284             s.height = shs;
8285             if(!Roo.isIE){
8286                 var cn = d.childNodes;
8287                 var sww = Math.max(0, (sw-12))+"px";
8288                 cn[0].childNodes[1].style.width = sww;
8289                 cn[1].childNodes[1].style.width = sww;
8290                 cn[2].childNodes[1].style.width = sww;
8291                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8292             }
8293         }
8294     },
8295
8296     /**
8297      * Hides this shadow
8298      */
8299     hide : function(){
8300         if(this.el){
8301             this.el.dom.style.display = "none";
8302             Roo.Shadow.Pool.push(this.el);
8303             delete this.el;
8304         }
8305     },
8306
8307     /**
8308      * Adjust the z-index of this shadow
8309      * @param {Number} zindex The new z-index
8310      */
8311     setZIndex : function(z){
8312         this.zIndex = z;
8313         if(this.el){
8314             this.el.setStyle("z-index", z);
8315         }
8316     }
8317 };
8318
8319 // Private utility class that manages the internal Shadow cache
8320 Roo.Shadow.Pool = function(){
8321     var p = [];
8322     var markup = Roo.isIE ?
8323                  '<div class="x-ie-shadow"></div>' :
8324                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8325     return {
8326         pull : function(){
8327             var sh = p.shift();
8328             if(!sh){
8329                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8330                 sh.autoBoxAdjust = false;
8331             }
8332             return sh;
8333         },
8334
8335         push : function(sh){
8336             p.push(sh);
8337         }
8338     };
8339 }();/*
8340  * Based on:
8341  * Ext JS Library 1.1.1
8342  * Copyright(c) 2006-2007, Ext JS, LLC.
8343  *
8344  * Originally Released Under LGPL - original licence link has changed is not relivant.
8345  *
8346  * Fork - LGPL
8347  * <script type="text/javascript">
8348  */
8349
8350 /**
8351  * @class Roo.BoxComponent
8352  * @extends Roo.Component
8353  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8354  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8355  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8356  * layout containers.
8357  * @constructor
8358  * @param {Roo.Element/String/Object} config The configuration options.
8359  */
8360 Roo.BoxComponent = function(config){
8361     Roo.Component.call(this, config);
8362     this.addEvents({
8363         /**
8364          * @event resize
8365          * Fires after the component is resized.
8366              * @param {Roo.Component} this
8367              * @param {Number} adjWidth The box-adjusted width that was set
8368              * @param {Number} adjHeight The box-adjusted height that was set
8369              * @param {Number} rawWidth The width that was originally specified
8370              * @param {Number} rawHeight The height that was originally specified
8371              */
8372         resize : true,
8373         /**
8374          * @event move
8375          * Fires after the component is moved.
8376              * @param {Roo.Component} this
8377              * @param {Number} x The new x position
8378              * @param {Number} y The new y position
8379              */
8380         move : true
8381     });
8382 };
8383
8384 Roo.extend(Roo.BoxComponent, Roo.Component, {
8385     // private, set in afterRender to signify that the component has been rendered
8386     boxReady : false,
8387     // private, used to defer height settings to subclasses
8388     deferHeight: false,
8389     /** @cfg {Number} width
8390      * width (optional) size of component
8391      */
8392      /** @cfg {Number} height
8393      * height (optional) size of component
8394      */
8395      
8396     /**
8397      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8398      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8399      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8400      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8401      * @return {Roo.BoxComponent} this
8402      */
8403     setSize : function(w, h){
8404         // support for standard size objects
8405         if(typeof w == 'object'){
8406             h = w.height;
8407             w = w.width;
8408         }
8409         // not rendered
8410         if(!this.boxReady){
8411             this.width = w;
8412             this.height = h;
8413             return this;
8414         }
8415
8416         // prevent recalcs when not needed
8417         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8418             return this;
8419         }
8420         this.lastSize = {width: w, height: h};
8421
8422         var adj = this.adjustSize(w, h);
8423         var aw = adj.width, ah = adj.height;
8424         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8425             var rz = this.getResizeEl();
8426             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8427                 rz.setSize(aw, ah);
8428             }else if(!this.deferHeight && ah !== undefined){
8429                 rz.setHeight(ah);
8430             }else if(aw !== undefined){
8431                 rz.setWidth(aw);
8432             }
8433             this.onResize(aw, ah, w, h);
8434             this.fireEvent('resize', this, aw, ah, w, h);
8435         }
8436         return this;
8437     },
8438
8439     /**
8440      * Gets the current size of the component's underlying element.
8441      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8442      */
8443     getSize : function(){
8444         return this.el.getSize();
8445     },
8446
8447     /**
8448      * Gets the current XY position of the component's underlying element.
8449      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8450      * @return {Array} The XY position of the element (e.g., [100, 200])
8451      */
8452     getPosition : function(local){
8453         if(local === true){
8454             return [this.el.getLeft(true), this.el.getTop(true)];
8455         }
8456         return this.xy || this.el.getXY();
8457     },
8458
8459     /**
8460      * Gets the current box measurements of the component's underlying element.
8461      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8462      * @returns {Object} box An object in the format {x, y, width, height}
8463      */
8464     getBox : function(local){
8465         var s = this.el.getSize();
8466         if(local){
8467             s.x = this.el.getLeft(true);
8468             s.y = this.el.getTop(true);
8469         }else{
8470             var xy = this.xy || this.el.getXY();
8471             s.x = xy[0];
8472             s.y = xy[1];
8473         }
8474         return s;
8475     },
8476
8477     /**
8478      * Sets the current box measurements of the component's underlying element.
8479      * @param {Object} box An object in the format {x, y, width, height}
8480      * @returns {Roo.BoxComponent} this
8481      */
8482     updateBox : function(box){
8483         this.setSize(box.width, box.height);
8484         this.setPagePosition(box.x, box.y);
8485         return this;
8486     },
8487
8488     // protected
8489     getResizeEl : function(){
8490         return this.resizeEl || this.el;
8491     },
8492
8493     // protected
8494     getPositionEl : function(){
8495         return this.positionEl || this.el;
8496     },
8497
8498     /**
8499      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8500      * This method fires the move event.
8501      * @param {Number} left The new left
8502      * @param {Number} top The new top
8503      * @returns {Roo.BoxComponent} this
8504      */
8505     setPosition : function(x, y){
8506         this.x = x;
8507         this.y = y;
8508         if(!this.boxReady){
8509             return this;
8510         }
8511         var adj = this.adjustPosition(x, y);
8512         var ax = adj.x, ay = adj.y;
8513
8514         var el = this.getPositionEl();
8515         if(ax !== undefined || ay !== undefined){
8516             if(ax !== undefined && ay !== undefined){
8517                 el.setLeftTop(ax, ay);
8518             }else if(ax !== undefined){
8519                 el.setLeft(ax);
8520             }else if(ay !== undefined){
8521                 el.setTop(ay);
8522             }
8523             this.onPosition(ax, ay);
8524             this.fireEvent('move', this, ax, ay);
8525         }
8526         return this;
8527     },
8528
8529     /**
8530      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8531      * This method fires the move event.
8532      * @param {Number} x The new x position
8533      * @param {Number} y The new y position
8534      * @returns {Roo.BoxComponent} this
8535      */
8536     setPagePosition : function(x, y){
8537         this.pageX = x;
8538         this.pageY = y;
8539         if(!this.boxReady){
8540             return;
8541         }
8542         if(x === undefined || y === undefined){ // cannot translate undefined points
8543             return;
8544         }
8545         var p = this.el.translatePoints(x, y);
8546         this.setPosition(p.left, p.top);
8547         return this;
8548     },
8549
8550     // private
8551     onRender : function(ct, position){
8552         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8553         if(this.resizeEl){
8554             this.resizeEl = Roo.get(this.resizeEl);
8555         }
8556         if(this.positionEl){
8557             this.positionEl = Roo.get(this.positionEl);
8558         }
8559     },
8560
8561     // private
8562     afterRender : function(){
8563         Roo.BoxComponent.superclass.afterRender.call(this);
8564         this.boxReady = true;
8565         this.setSize(this.width, this.height);
8566         if(this.x || this.y){
8567             this.setPosition(this.x, this.y);
8568         }
8569         if(this.pageX || this.pageY){
8570             this.setPagePosition(this.pageX, this.pageY);
8571         }
8572     },
8573
8574     /**
8575      * Force the component's size to recalculate based on the underlying element's current height and width.
8576      * @returns {Roo.BoxComponent} this
8577      */
8578     syncSize : function(){
8579         delete this.lastSize;
8580         this.setSize(this.el.getWidth(), this.el.getHeight());
8581         return this;
8582     },
8583
8584     /**
8585      * Called after the component is resized, this method is empty by default but can be implemented by any
8586      * subclass that needs to perform custom logic after a resize occurs.
8587      * @param {Number} adjWidth The box-adjusted width that was set
8588      * @param {Number} adjHeight The box-adjusted height that was set
8589      * @param {Number} rawWidth The width that was originally specified
8590      * @param {Number} rawHeight The height that was originally specified
8591      */
8592     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8593
8594     },
8595
8596     /**
8597      * Called after the component is moved, this method is empty by default but can be implemented by any
8598      * subclass that needs to perform custom logic after a move occurs.
8599      * @param {Number} x The new x position
8600      * @param {Number} y The new y position
8601      */
8602     onPosition : function(x, y){
8603
8604     },
8605
8606     // private
8607     adjustSize : function(w, h){
8608         if(this.autoWidth){
8609             w = 'auto';
8610         }
8611         if(this.autoHeight){
8612             h = 'auto';
8613         }
8614         return {width : w, height: h};
8615     },
8616
8617     // private
8618     adjustPosition : function(x, y){
8619         return {x : x, y: y};
8620     }
8621 });/*
8622  * Based on:
8623  * Ext JS Library 1.1.1
8624  * Copyright(c) 2006-2007, Ext JS, LLC.
8625  *
8626  * Originally Released Under LGPL - original licence link has changed is not relivant.
8627  *
8628  * Fork - LGPL
8629  * <script type="text/javascript">
8630  */
8631
8632
8633 /**
8634  * @class Roo.SplitBar
8635  * @extends Roo.util.Observable
8636  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8637  * <br><br>
8638  * Usage:
8639  * <pre><code>
8640 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8641                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8642 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8643 split.minSize = 100;
8644 split.maxSize = 600;
8645 split.animate = true;
8646 split.on('moved', splitterMoved);
8647 </code></pre>
8648  * @constructor
8649  * Create a new SplitBar
8650  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8651  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8652  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8653  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8654                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8655                         position of the SplitBar).
8656  */
8657 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8658     
8659     /** @private */
8660     this.el = Roo.get(dragElement, true);
8661     this.el.dom.unselectable = "on";
8662     /** @private */
8663     this.resizingEl = Roo.get(resizingElement, true);
8664
8665     /**
8666      * @private
8667      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8668      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8669      * @type Number
8670      */
8671     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8672     
8673     /**
8674      * The minimum size of the resizing element. (Defaults to 0)
8675      * @type Number
8676      */
8677     this.minSize = 0;
8678     
8679     /**
8680      * The maximum size of the resizing element. (Defaults to 2000)
8681      * @type Number
8682      */
8683     this.maxSize = 2000;
8684     
8685     /**
8686      * Whether to animate the transition to the new size
8687      * @type Boolean
8688      */
8689     this.animate = false;
8690     
8691     /**
8692      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8693      * @type Boolean
8694      */
8695     this.useShim = false;
8696     
8697     /** @private */
8698     this.shim = null;
8699     
8700     if(!existingProxy){
8701         /** @private */
8702         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8703     }else{
8704         this.proxy = Roo.get(existingProxy).dom;
8705     }
8706     /** @private */
8707     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8708     
8709     /** @private */
8710     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8711     
8712     /** @private */
8713     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8714     
8715     /** @private */
8716     this.dragSpecs = {};
8717     
8718     /**
8719      * @private The adapter to use to positon and resize elements
8720      */
8721     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8722     this.adapter.init(this);
8723     
8724     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8725         /** @private */
8726         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8727         this.el.addClass("x-splitbar-h");
8728     }else{
8729         /** @private */
8730         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8731         this.el.addClass("x-splitbar-v");
8732     }
8733     
8734     this.addEvents({
8735         /**
8736          * @event resize
8737          * Fires when the splitter is moved (alias for {@link #event-moved})
8738          * @param {Roo.SplitBar} this
8739          * @param {Number} newSize the new width or height
8740          */
8741         "resize" : true,
8742         /**
8743          * @event moved
8744          * Fires when the splitter is moved
8745          * @param {Roo.SplitBar} this
8746          * @param {Number} newSize the new width or height
8747          */
8748         "moved" : true,
8749         /**
8750          * @event beforeresize
8751          * Fires before the splitter is dragged
8752          * @param {Roo.SplitBar} this
8753          */
8754         "beforeresize" : true,
8755
8756         "beforeapply" : true
8757     });
8758
8759     Roo.util.Observable.call(this);
8760 };
8761
8762 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8763     onStartProxyDrag : function(x, y){
8764         this.fireEvent("beforeresize", this);
8765         if(!this.overlay){
8766             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8767             o.unselectable();
8768             o.enableDisplayMode("block");
8769             // all splitbars share the same overlay
8770             Roo.SplitBar.prototype.overlay = o;
8771         }
8772         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8773         this.overlay.show();
8774         Roo.get(this.proxy).setDisplayed("block");
8775         var size = this.adapter.getElementSize(this);
8776         this.activeMinSize = this.getMinimumSize();;
8777         this.activeMaxSize = this.getMaximumSize();;
8778         var c1 = size - this.activeMinSize;
8779         var c2 = Math.max(this.activeMaxSize - size, 0);
8780         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8781             this.dd.resetConstraints();
8782             this.dd.setXConstraint(
8783                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8784                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8785             );
8786             this.dd.setYConstraint(0, 0);
8787         }else{
8788             this.dd.resetConstraints();
8789             this.dd.setXConstraint(0, 0);
8790             this.dd.setYConstraint(
8791                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8792                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8793             );
8794          }
8795         this.dragSpecs.startSize = size;
8796         this.dragSpecs.startPoint = [x, y];
8797         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8798     },
8799     
8800     /** 
8801      * @private Called after the drag operation by the DDProxy
8802      */
8803     onEndProxyDrag : function(e){
8804         Roo.get(this.proxy).setDisplayed(false);
8805         var endPoint = Roo.lib.Event.getXY(e);
8806         if(this.overlay){
8807             this.overlay.hide();
8808         }
8809         var newSize;
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             newSize = this.dragSpecs.startSize + 
8812                 (this.placement == Roo.SplitBar.LEFT ?
8813                     endPoint[0] - this.dragSpecs.startPoint[0] :
8814                     this.dragSpecs.startPoint[0] - endPoint[0]
8815                 );
8816         }else{
8817             newSize = this.dragSpecs.startSize + 
8818                 (this.placement == Roo.SplitBar.TOP ?
8819                     endPoint[1] - this.dragSpecs.startPoint[1] :
8820                     this.dragSpecs.startPoint[1] - endPoint[1]
8821                 );
8822         }
8823         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8824         if(newSize != this.dragSpecs.startSize){
8825             if(this.fireEvent('beforeapply', this, newSize) !== false){
8826                 this.adapter.setElementSize(this, newSize);
8827                 this.fireEvent("moved", this, newSize);
8828                 this.fireEvent("resize", this, newSize);
8829             }
8830         }
8831     },
8832     
8833     /**
8834      * Get the adapter this SplitBar uses
8835      * @return The adapter object
8836      */
8837     getAdapter : function(){
8838         return this.adapter;
8839     },
8840     
8841     /**
8842      * Set the adapter this SplitBar uses
8843      * @param {Object} adapter A SplitBar adapter object
8844      */
8845     setAdapter : function(adapter){
8846         this.adapter = adapter;
8847         this.adapter.init(this);
8848     },
8849     
8850     /**
8851      * Gets the minimum size for the resizing element
8852      * @return {Number} The minimum size
8853      */
8854     getMinimumSize : function(){
8855         return this.minSize;
8856     },
8857     
8858     /**
8859      * Sets the minimum size for the resizing element
8860      * @param {Number} minSize The minimum size
8861      */
8862     setMinimumSize : function(minSize){
8863         this.minSize = minSize;
8864     },
8865     
8866     /**
8867      * Gets the maximum size for the resizing element
8868      * @return {Number} The maximum size
8869      */
8870     getMaximumSize : function(){
8871         return this.maxSize;
8872     },
8873     
8874     /**
8875      * Sets the maximum size for the resizing element
8876      * @param {Number} maxSize The maximum size
8877      */
8878     setMaximumSize : function(maxSize){
8879         this.maxSize = maxSize;
8880     },
8881     
8882     /**
8883      * Sets the initialize size for the resizing element
8884      * @param {Number} size The initial size
8885      */
8886     setCurrentSize : function(size){
8887         var oldAnimate = this.animate;
8888         this.animate = false;
8889         this.adapter.setElementSize(this, size);
8890         this.animate = oldAnimate;
8891     },
8892     
8893     /**
8894      * Destroy this splitbar. 
8895      * @param {Boolean} removeEl True to remove the element
8896      */
8897     destroy : function(removeEl){
8898         if(this.shim){
8899             this.shim.remove();
8900         }
8901         this.dd.unreg();
8902         this.proxy.parentNode.removeChild(this.proxy);
8903         if(removeEl){
8904             this.el.remove();
8905         }
8906     }
8907 });
8908
8909 /**
8910  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8911  */
8912 Roo.SplitBar.createProxy = function(dir){
8913     var proxy = new Roo.Element(document.createElement("div"));
8914     proxy.unselectable();
8915     var cls = 'x-splitbar-proxy';
8916     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8917     document.body.appendChild(proxy.dom);
8918     return proxy.dom;
8919 };
8920
8921 /** 
8922  * @class Roo.SplitBar.BasicLayoutAdapter
8923  * Default Adapter. It assumes the splitter and resizing element are not positioned
8924  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8925  */
8926 Roo.SplitBar.BasicLayoutAdapter = function(){
8927 };
8928
8929 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8930     // do nothing for now
8931     init : function(s){
8932     
8933     },
8934     /**
8935      * Called before drag operations to get the current size of the resizing element. 
8936      * @param {Roo.SplitBar} s The SplitBar using this adapter
8937      */
8938      getElementSize : function(s){
8939         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8940             return s.resizingEl.getWidth();
8941         }else{
8942             return s.resizingEl.getHeight();
8943         }
8944     },
8945     
8946     /**
8947      * Called after drag operations to set the size of the resizing element.
8948      * @param {Roo.SplitBar} s The SplitBar using this adapter
8949      * @param {Number} newSize The new size to set
8950      * @param {Function} onComplete A function to be invoked when resizing is complete
8951      */
8952     setElementSize : function(s, newSize, onComplete){
8953         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8954             if(!s.animate){
8955                 s.resizingEl.setWidth(newSize);
8956                 if(onComplete){
8957                     onComplete(s, newSize);
8958                 }
8959             }else{
8960                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8961             }
8962         }else{
8963             
8964             if(!s.animate){
8965                 s.resizingEl.setHeight(newSize);
8966                 if(onComplete){
8967                     onComplete(s, newSize);
8968                 }
8969             }else{
8970                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8971             }
8972         }
8973     }
8974 };
8975
8976 /** 
8977  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8978  * @extends Roo.SplitBar.BasicLayoutAdapter
8979  * Adapter that  moves the splitter element to align with the resized sizing element. 
8980  * Used with an absolute positioned SplitBar.
8981  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8982  * document.body, make sure you assign an id to the body element.
8983  */
8984 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8985     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8986     this.container = Roo.get(container);
8987 };
8988
8989 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8990     init : function(s){
8991         this.basic.init(s);
8992     },
8993     
8994     getElementSize : function(s){
8995         return this.basic.getElementSize(s);
8996     },
8997     
8998     setElementSize : function(s, newSize, onComplete){
8999         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9000     },
9001     
9002     moveSplitter : function(s){
9003         var yes = Roo.SplitBar;
9004         switch(s.placement){
9005             case yes.LEFT:
9006                 s.el.setX(s.resizingEl.getRight());
9007                 break;
9008             case yes.RIGHT:
9009                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9010                 break;
9011             case yes.TOP:
9012                 s.el.setY(s.resizingEl.getBottom());
9013                 break;
9014             case yes.BOTTOM:
9015                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9016                 break;
9017         }
9018     }
9019 };
9020
9021 /**
9022  * Orientation constant - Create a vertical SplitBar
9023  * @static
9024  * @type Number
9025  */
9026 Roo.SplitBar.VERTICAL = 1;
9027
9028 /**
9029  * Orientation constant - Create a horizontal SplitBar
9030  * @static
9031  * @type Number
9032  */
9033 Roo.SplitBar.HORIZONTAL = 2;
9034
9035 /**
9036  * Placement constant - The resizing element is to the left of the splitter element
9037  * @static
9038  * @type Number
9039  */
9040 Roo.SplitBar.LEFT = 1;
9041
9042 /**
9043  * Placement constant - The resizing element is to the right of the splitter element
9044  * @static
9045  * @type Number
9046  */
9047 Roo.SplitBar.RIGHT = 2;
9048
9049 /**
9050  * Placement constant - The resizing element is positioned above the splitter element
9051  * @static
9052  * @type Number
9053  */
9054 Roo.SplitBar.TOP = 3;
9055
9056 /**
9057  * Placement constant - The resizing element is positioned under splitter element
9058  * @static
9059  * @type Number
9060  */
9061 Roo.SplitBar.BOTTOM = 4;
9062 /*
9063  * Based on:
9064  * Ext JS Library 1.1.1
9065  * Copyright(c) 2006-2007, Ext JS, LLC.
9066  *
9067  * Originally Released Under LGPL - original licence link has changed is not relivant.
9068  *
9069  * Fork - LGPL
9070  * <script type="text/javascript">
9071  */
9072
9073 /**
9074  * @class Roo.View
9075  * @extends Roo.util.Observable
9076  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9077  * This class also supports single and multi selection modes. <br>
9078  * Create a data model bound view:
9079  <pre><code>
9080  var store = new Roo.data.Store(...);
9081
9082  var view = new Roo.View({
9083     el : "my-element",
9084     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9085  
9086     singleSelect: true,
9087     selectedClass: "ydataview-selected",
9088     store: store
9089  });
9090
9091  // listen for node click?
9092  view.on("click", function(vw, index, node, e){
9093  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9094  });
9095
9096  // load XML data
9097  dataModel.load("foobar.xml");
9098  </code></pre>
9099  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9100  * <br><br>
9101  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9102  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9103  * 
9104  * Note: old style constructor is still suported (container, template, config)
9105  * 
9106  * @constructor
9107  * Create a new View
9108  * @param {Object} config The config object
9109  * 
9110  */
9111 Roo.View = function(config, depreciated_tpl, depreciated_config){
9112     
9113     if (typeof(depreciated_tpl) == 'undefined') {
9114         // new way.. - universal constructor.
9115         Roo.apply(this, config);
9116         this.el  = Roo.get(this.el);
9117     } else {
9118         // old format..
9119         this.el  = Roo.get(config);
9120         this.tpl = depreciated_tpl;
9121         Roo.apply(this, depreciated_config);
9122     }
9123      
9124     
9125     if(typeof(this.tpl) == "string"){
9126         this.tpl = new Roo.Template(this.tpl);
9127     } else {
9128         // support xtype ctors..
9129         this.tpl = new Roo.factory(this.tpl, Roo);
9130     }
9131     
9132     
9133     this.tpl.compile();
9134    
9135
9136      
9137     /** @private */
9138     this.addEvents({
9139     /**
9140      * @event beforeclick
9141      * Fires before a click is processed. Returns false to cancel the default action.
9142      * @param {Roo.View} this
9143      * @param {Number} index The index of the target node
9144      * @param {HTMLElement} node The target node
9145      * @param {Roo.EventObject} e The raw event object
9146      */
9147         "beforeclick" : true,
9148     /**
9149      * @event click
9150      * Fires when a template node is clicked.
9151      * @param {Roo.View} this
9152      * @param {Number} index The index of the target node
9153      * @param {HTMLElement} node The target node
9154      * @param {Roo.EventObject} e The raw event object
9155      */
9156         "click" : true,
9157     /**
9158      * @event dblclick
9159      * Fires when a template node is double clicked.
9160      * @param {Roo.View} this
9161      * @param {Number} index The index of the target node
9162      * @param {HTMLElement} node The target node
9163      * @param {Roo.EventObject} e The raw event object
9164      */
9165         "dblclick" : true,
9166     /**
9167      * @event contextmenu
9168      * Fires when a template node is right clicked.
9169      * @param {Roo.View} this
9170      * @param {Number} index The index of the target node
9171      * @param {HTMLElement} node The target node
9172      * @param {Roo.EventObject} e The raw event object
9173      */
9174         "contextmenu" : true,
9175     /**
9176      * @event selectionchange
9177      * Fires when the selected nodes change.
9178      * @param {Roo.View} this
9179      * @param {Array} selections Array of the selected nodes
9180      */
9181         "selectionchange" : true,
9182
9183     /**
9184      * @event beforeselect
9185      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9186      * @param {Roo.View} this
9187      * @param {HTMLElement} node The node to be selected
9188      * @param {Array} selections Array of currently selected nodes
9189      */
9190         "beforeselect" : true
9191     });
9192
9193     this.el.on({
9194         "click": this.onClick,
9195         "dblclick": this.onDblClick,
9196         "contextmenu": this.onContextMenu,
9197         scope:this
9198     });
9199
9200     this.selections = [];
9201     this.nodes = [];
9202     this.cmp = new Roo.CompositeElementLite([]);
9203     if(this.store){
9204         this.store = Roo.factory(this.store, Roo.data);
9205         this.setStore(this.store, true);
9206     }
9207     Roo.View.superclass.constructor.call(this);
9208 };
9209
9210 Roo.extend(Roo.View, Roo.util.Observable, {
9211     
9212      /**
9213      * @cfg {Roo.data.Store} store Data store to load data from.
9214      */
9215     store : false,
9216     
9217     /**
9218      * @cfg {String|Roo.Element} el The container element.
9219      */
9220     el : '',
9221     
9222     /**
9223      * @cfg {String|Roo.Template} tpl The template used by this View 
9224      */
9225     tpl : false,
9226     
9227     /**
9228      * @cfg {String} selectedClass The css class to add to selected nodes
9229      */
9230     selectedClass : "x-view-selected",
9231      /**
9232      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9233      */
9234     emptyText : "",
9235     /**
9236      * @cfg {Boolean} multiSelect Allow multiple selection
9237      */
9238     
9239     multiSelect : false,
9240     /**
9241      * @cfg {Boolean} singleSelect Allow single selection
9242      */
9243     singleSelect:  false,
9244     
9245     /**
9246      * Returns the element this view is bound to.
9247      * @return {Roo.Element}
9248      */
9249     getEl : function(){
9250         return this.el;
9251     },
9252
9253     /**
9254      * Refreshes the view.
9255      */
9256     refresh : function(){
9257         var t = this.tpl;
9258         this.clearSelections();
9259         this.el.update("");
9260         var html = [];
9261         var records = this.store.getRange();
9262         if(records.length < 1){
9263             this.el.update(this.emptyText);
9264             return;
9265         }
9266         for(var i = 0, len = records.length; i < len; i++){
9267             var data = this.prepareData(records[i].data, i, records[i]);
9268             html[html.length] = t.apply(data);
9269         }
9270         this.el.update(html.join(""));
9271         this.nodes = this.el.dom.childNodes;
9272         this.updateIndexes(0);
9273     },
9274
9275     /**
9276      * Function to override to reformat the data that is sent to
9277      * the template for each node.
9278      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9279      * a JSON object for an UpdateManager bound view).
9280      */
9281     prepareData : function(data){
9282         return data;
9283     },
9284
9285     onUpdate : function(ds, record){
9286         this.clearSelections();
9287         var index = this.store.indexOf(record);
9288         var n = this.nodes[index];
9289         this.tpl.insertBefore(n, this.prepareData(record.data));
9290         n.parentNode.removeChild(n);
9291         this.updateIndexes(index, index);
9292     },
9293
9294     onAdd : function(ds, records, index){
9295         this.clearSelections();
9296         if(this.nodes.length == 0){
9297             this.refresh();
9298             return;
9299         }
9300         var n = this.nodes[index];
9301         for(var i = 0, len = records.length; i < len; i++){
9302             var d = this.prepareData(records[i].data);
9303             if(n){
9304                 this.tpl.insertBefore(n, d);
9305             }else{
9306                 this.tpl.append(this.el, d);
9307             }
9308         }
9309         this.updateIndexes(index);
9310     },
9311
9312     onRemove : function(ds, record, index){
9313         this.clearSelections();
9314         this.el.dom.removeChild(this.nodes[index]);
9315         this.updateIndexes(index);
9316     },
9317
9318     /**
9319      * Refresh an individual node.
9320      * @param {Number} index
9321      */
9322     refreshNode : function(index){
9323         this.onUpdate(this.store, this.store.getAt(index));
9324     },
9325
9326     updateIndexes : function(startIndex, endIndex){
9327         var ns = this.nodes;
9328         startIndex = startIndex || 0;
9329         endIndex = endIndex || ns.length - 1;
9330         for(var i = startIndex; i <= endIndex; i++){
9331             ns[i].nodeIndex = i;
9332         }
9333     },
9334
9335     /**
9336      * Changes the data store this view uses and refresh the view.
9337      * @param {Store} store
9338      */
9339     setStore : function(store, initial){
9340         if(!initial && this.store){
9341             this.store.un("datachanged", this.refresh);
9342             this.store.un("add", this.onAdd);
9343             this.store.un("remove", this.onRemove);
9344             this.store.un("update", this.onUpdate);
9345             this.store.un("clear", this.refresh);
9346         }
9347         if(store){
9348           
9349             store.on("datachanged", this.refresh, this);
9350             store.on("add", this.onAdd, this);
9351             store.on("remove", this.onRemove, this);
9352             store.on("update", this.onUpdate, this);
9353             store.on("clear", this.refresh, this);
9354         }
9355         
9356         if(store){
9357             this.refresh();
9358         }
9359     },
9360
9361     /**
9362      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9363      * @param {HTMLElement} node
9364      * @return {HTMLElement} The template node
9365      */
9366     findItemFromChild : function(node){
9367         var el = this.el.dom;
9368         if(!node || node.parentNode == el){
9369                     return node;
9370             }
9371             var p = node.parentNode;
9372             while(p && p != el){
9373             if(p.parentNode == el){
9374                 return p;
9375             }
9376             p = p.parentNode;
9377         }
9378             return null;
9379     },
9380
9381     /** @ignore */
9382     onClick : function(e){
9383         var item = this.findItemFromChild(e.getTarget());
9384         if(item){
9385             var index = this.indexOf(item);
9386             if(this.onItemClick(item, index, e) !== false){
9387                 this.fireEvent("click", this, index, item, e);
9388             }
9389         }else{
9390             this.clearSelections();
9391         }
9392     },
9393
9394     /** @ignore */
9395     onContextMenu : function(e){
9396         var item = this.findItemFromChild(e.getTarget());
9397         if(item){
9398             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9399         }
9400     },
9401
9402     /** @ignore */
9403     onDblClick : function(e){
9404         var item = this.findItemFromChild(e.getTarget());
9405         if(item){
9406             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9407         }
9408     },
9409
9410     onItemClick : function(item, index, e){
9411         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9412             return false;
9413         }
9414         if(this.multiSelect || this.singleSelect){
9415             if(this.multiSelect && e.shiftKey && this.lastSelection){
9416                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9417             }else{
9418                 this.select(item, this.multiSelect && e.ctrlKey);
9419                 this.lastSelection = item;
9420             }
9421             e.preventDefault();
9422         }
9423         return true;
9424     },
9425
9426     /**
9427      * Get the number of selected nodes.
9428      * @return {Number}
9429      */
9430     getSelectionCount : function(){
9431         return this.selections.length;
9432     },
9433
9434     /**
9435      * Get the currently selected nodes.
9436      * @return {Array} An array of HTMLElements
9437      */
9438     getSelectedNodes : function(){
9439         return this.selections;
9440     },
9441
9442     /**
9443      * Get the indexes of the selected nodes.
9444      * @return {Array}
9445      */
9446     getSelectedIndexes : function(){
9447         var indexes = [], s = this.selections;
9448         for(var i = 0, len = s.length; i < len; i++){
9449             indexes.push(s[i].nodeIndex);
9450         }
9451         return indexes;
9452     },
9453
9454     /**
9455      * Clear all selections
9456      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9457      */
9458     clearSelections : function(suppressEvent){
9459         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9460             this.cmp.elements = this.selections;
9461             this.cmp.removeClass(this.selectedClass);
9462             this.selections = [];
9463             if(!suppressEvent){
9464                 this.fireEvent("selectionchange", this, this.selections);
9465             }
9466         }
9467     },
9468
9469     /**
9470      * Returns true if the passed node is selected
9471      * @param {HTMLElement/Number} node The node or node index
9472      * @return {Boolean}
9473      */
9474     isSelected : function(node){
9475         var s = this.selections;
9476         if(s.length < 1){
9477             return false;
9478         }
9479         node = this.getNode(node);
9480         return s.indexOf(node) !== -1;
9481     },
9482
9483     /**
9484      * Selects nodes.
9485      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9486      * @param {Boolean} keepExisting (optional) true to keep existing selections
9487      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9488      */
9489     select : function(nodeInfo, keepExisting, suppressEvent){
9490         if(nodeInfo instanceof Array){
9491             if(!keepExisting){
9492                 this.clearSelections(true);
9493             }
9494             for(var i = 0, len = nodeInfo.length; i < len; i++){
9495                 this.select(nodeInfo[i], true, true);
9496             }
9497         } else{
9498             var node = this.getNode(nodeInfo);
9499             if(node && !this.isSelected(node)){
9500                 if(!keepExisting){
9501                     this.clearSelections(true);
9502                 }
9503                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9504                     Roo.fly(node).addClass(this.selectedClass);
9505                     this.selections.push(node);
9506                     if(!suppressEvent){
9507                         this.fireEvent("selectionchange", this, this.selections);
9508                     }
9509                 }
9510             }
9511         }
9512     },
9513
9514     /**
9515      * Gets a template node.
9516      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9517      * @return {HTMLElement} The node or null if it wasn't found
9518      */
9519     getNode : function(nodeInfo){
9520         if(typeof nodeInfo == "string"){
9521             return document.getElementById(nodeInfo);
9522         }else if(typeof nodeInfo == "number"){
9523             return this.nodes[nodeInfo];
9524         }
9525         return nodeInfo;
9526     },
9527
9528     /**
9529      * Gets a range template nodes.
9530      * @param {Number} startIndex
9531      * @param {Number} endIndex
9532      * @return {Array} An array of nodes
9533      */
9534     getNodes : function(start, end){
9535         var ns = this.nodes;
9536         start = start || 0;
9537         end = typeof end == "undefined" ? ns.length - 1 : end;
9538         var nodes = [];
9539         if(start <= end){
9540             for(var i = start; i <= end; i++){
9541                 nodes.push(ns[i]);
9542             }
9543         } else{
9544             for(var i = start; i >= end; i--){
9545                 nodes.push(ns[i]);
9546             }
9547         }
9548         return nodes;
9549     },
9550
9551     /**
9552      * Finds the index of the passed node
9553      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9554      * @return {Number} The index of the node or -1
9555      */
9556     indexOf : function(node){
9557         node = this.getNode(node);
9558         if(typeof node.nodeIndex == "number"){
9559             return node.nodeIndex;
9560         }
9561         var ns = this.nodes;
9562         for(var i = 0, len = ns.length; i < len; i++){
9563             if(ns[i] == node){
9564                 return i;
9565             }
9566         }
9567         return -1;
9568     }
9569 });
9570 /*
9571  * Based on:
9572  * Ext JS Library 1.1.1
9573  * Copyright(c) 2006-2007, Ext JS, LLC.
9574  *
9575  * Originally Released Under LGPL - original licence link has changed is not relivant.
9576  *
9577  * Fork - LGPL
9578  * <script type="text/javascript">
9579  */
9580
9581 /**
9582  * @class Roo.JsonView
9583  * @extends Roo.View
9584  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9585 <pre><code>
9586 var view = new Roo.JsonView({
9587     container: "my-element",
9588     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9589     multiSelect: true, 
9590     jsonRoot: "data" 
9591 });
9592
9593 // listen for node click?
9594 view.on("click", function(vw, index, node, e){
9595     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9596 });
9597
9598 // direct load of JSON data
9599 view.load("foobar.php");
9600
9601 // Example from my blog list
9602 var tpl = new Roo.Template(
9603     '&lt;div class="entry"&gt;' +
9604     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9605     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9606     "&lt;/div&gt;&lt;hr /&gt;"
9607 );
9608
9609 var moreView = new Roo.JsonView({
9610     container :  "entry-list", 
9611     template : tpl,
9612     jsonRoot: "posts"
9613 });
9614 moreView.on("beforerender", this.sortEntries, this);
9615 moreView.load({
9616     url: "/blog/get-posts.php",
9617     params: "allposts=true",
9618     text: "Loading Blog Entries..."
9619 });
9620 </code></pre>
9621
9622 * Note: old code is supported with arguments : (container, template, config)
9623
9624
9625  * @constructor
9626  * Create a new JsonView
9627  * 
9628  * @param {Object} config The config object
9629  * 
9630  */
9631 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9632     
9633     
9634     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9635
9636     var um = this.el.getUpdateManager();
9637     um.setRenderer(this);
9638     um.on("update", this.onLoad, this);
9639     um.on("failure", this.onLoadException, this);
9640
9641     /**
9642      * @event beforerender
9643      * Fires before rendering of the downloaded JSON data.
9644      * @param {Roo.JsonView} this
9645      * @param {Object} data The JSON data loaded
9646      */
9647     /**
9648      * @event load
9649      * Fires when data is loaded.
9650      * @param {Roo.JsonView} this
9651      * @param {Object} data The JSON data loaded
9652      * @param {Object} response The raw Connect response object
9653      */
9654     /**
9655      * @event loadexception
9656      * Fires when loading fails.
9657      * @param {Roo.JsonView} this
9658      * @param {Object} response The raw Connect response object
9659      */
9660     this.addEvents({
9661         'beforerender' : true,
9662         'load' : true,
9663         'loadexception' : true
9664     });
9665 };
9666 Roo.extend(Roo.JsonView, Roo.View, {
9667     /**
9668      * @type {String} The root property in the loaded JSON object that contains the data
9669      */
9670     jsonRoot : "",
9671
9672     /**
9673      * Refreshes the view.
9674      */
9675     refresh : function(){
9676         this.clearSelections();
9677         this.el.update("");
9678         var html = [];
9679         var o = this.jsonData;
9680         if(o && o.length > 0){
9681             for(var i = 0, len = o.length; i < len; i++){
9682                 var data = this.prepareData(o[i], i, o);
9683                 html[html.length] = this.tpl.apply(data);
9684             }
9685         }else{
9686             html.push(this.emptyText);
9687         }
9688         this.el.update(html.join(""));
9689         this.nodes = this.el.dom.childNodes;
9690         this.updateIndexes(0);
9691     },
9692
9693     /**
9694      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9695      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9696      <pre><code>
9697      view.load({
9698          url: "your-url.php",
9699          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9700          callback: yourFunction,
9701          scope: yourObject, //(optional scope)
9702          discardUrl: false,
9703          nocache: false,
9704          text: "Loading...",
9705          timeout: 30,
9706          scripts: false
9707      });
9708      </code></pre>
9709      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9710      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9711      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9712      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9713      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9714      */
9715     load : function(){
9716         var um = this.el.getUpdateManager();
9717         um.update.apply(um, arguments);
9718     },
9719
9720     render : function(el, response){
9721         this.clearSelections();
9722         this.el.update("");
9723         var o;
9724         try{
9725             o = Roo.util.JSON.decode(response.responseText);
9726             if(this.jsonRoot){
9727                 
9728                 o = o[this.jsonRoot];
9729             }
9730         } catch(e){
9731         }
9732         /**
9733          * The current JSON data or null
9734          */
9735         this.jsonData = o;
9736         this.beforeRender();
9737         this.refresh();
9738     },
9739
9740 /**
9741  * Get the number of records in the current JSON dataset
9742  * @return {Number}
9743  */
9744     getCount : function(){
9745         return this.jsonData ? this.jsonData.length : 0;
9746     },
9747
9748 /**
9749  * Returns the JSON object for the specified node(s)
9750  * @param {HTMLElement/Array} node The node or an array of nodes
9751  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9752  * you get the JSON object for the node
9753  */
9754     getNodeData : function(node){
9755         if(node instanceof Array){
9756             var data = [];
9757             for(var i = 0, len = node.length; i < len; i++){
9758                 data.push(this.getNodeData(node[i]));
9759             }
9760             return data;
9761         }
9762         return this.jsonData[this.indexOf(node)] || null;
9763     },
9764
9765     beforeRender : function(){
9766         this.snapshot = this.jsonData;
9767         if(this.sortInfo){
9768             this.sort.apply(this, this.sortInfo);
9769         }
9770         this.fireEvent("beforerender", this, this.jsonData);
9771     },
9772
9773     onLoad : function(el, o){
9774         this.fireEvent("load", this, this.jsonData, o);
9775     },
9776
9777     onLoadException : function(el, o){
9778         this.fireEvent("loadexception", this, o);
9779     },
9780
9781 /**
9782  * Filter the data by a specific property.
9783  * @param {String} property A property on your JSON objects
9784  * @param {String/RegExp} value Either string that the property values
9785  * should start with, or a RegExp to test against the property
9786  */
9787     filter : function(property, value){
9788         if(this.jsonData){
9789             var data = [];
9790             var ss = this.snapshot;
9791             if(typeof value == "string"){
9792                 var vlen = value.length;
9793                 if(vlen == 0){
9794                     this.clearFilter();
9795                     return;
9796                 }
9797                 value = value.toLowerCase();
9798                 for(var i = 0, len = ss.length; i < len; i++){
9799                     var o = ss[i];
9800                     if(o[property].substr(0, vlen).toLowerCase() == value){
9801                         data.push(o);
9802                     }
9803                 }
9804             } else if(value.exec){ // regex?
9805                 for(var i = 0, len = ss.length; i < len; i++){
9806                     var o = ss[i];
9807                     if(value.test(o[property])){
9808                         data.push(o);
9809                     }
9810                 }
9811             } else{
9812                 return;
9813             }
9814             this.jsonData = data;
9815             this.refresh();
9816         }
9817     },
9818
9819 /**
9820  * Filter by a function. The passed function will be called with each
9821  * object in the current dataset. If the function returns true the value is kept,
9822  * otherwise it is filtered.
9823  * @param {Function} fn
9824  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9825  */
9826     filterBy : function(fn, scope){
9827         if(this.jsonData){
9828             var data = [];
9829             var ss = this.snapshot;
9830             for(var i = 0, len = ss.length; i < len; i++){
9831                 var o = ss[i];
9832                 if(fn.call(scope || this, o)){
9833                     data.push(o);
9834                 }
9835             }
9836             this.jsonData = data;
9837             this.refresh();
9838         }
9839     },
9840
9841 /**
9842  * Clears the current filter.
9843  */
9844     clearFilter : function(){
9845         if(this.snapshot && this.jsonData != this.snapshot){
9846             this.jsonData = this.snapshot;
9847             this.refresh();
9848         }
9849     },
9850
9851
9852 /**
9853  * Sorts the data for this view and refreshes it.
9854  * @param {String} property A property on your JSON objects to sort on
9855  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9856  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9857  */
9858     sort : function(property, dir, sortType){
9859         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9860         if(this.jsonData){
9861             var p = property;
9862             var dsc = dir && dir.toLowerCase() == "desc";
9863             var f = function(o1, o2){
9864                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9865                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9866                 ;
9867                 if(v1 < v2){
9868                     return dsc ? +1 : -1;
9869                 } else if(v1 > v2){
9870                     return dsc ? -1 : +1;
9871                 } else{
9872                     return 0;
9873                 }
9874             };
9875             this.jsonData.sort(f);
9876             this.refresh();
9877             if(this.jsonData != this.snapshot){
9878                 this.snapshot.sort(f);
9879             }
9880         }
9881     }
9882 });/*
9883  * Based on:
9884  * Ext JS Library 1.1.1
9885  * Copyright(c) 2006-2007, Ext JS, LLC.
9886  *
9887  * Originally Released Under LGPL - original licence link has changed is not relivant.
9888  *
9889  * Fork - LGPL
9890  * <script type="text/javascript">
9891  */
9892  
9893
9894 /**
9895  * @class Roo.ColorPalette
9896  * @extends Roo.Component
9897  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9898  * Here's an example of typical usage:
9899  * <pre><code>
9900 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9901 cp.render('my-div');
9902
9903 cp.on('select', function(palette, selColor){
9904     // do something with selColor
9905 });
9906 </code></pre>
9907  * @constructor
9908  * Create a new ColorPalette
9909  * @param {Object} config The config object
9910  */
9911 Roo.ColorPalette = function(config){
9912     Roo.ColorPalette.superclass.constructor.call(this, config);
9913     this.addEvents({
9914         /**
9915              * @event select
9916              * Fires when a color is selected
9917              * @param {ColorPalette} this
9918              * @param {String} color The 6-digit color hex code (without the # symbol)
9919              */
9920         select: true
9921     });
9922
9923     if(this.handler){
9924         this.on("select", this.handler, this.scope, true);
9925     }
9926 };
9927 Roo.extend(Roo.ColorPalette, Roo.Component, {
9928     /**
9929      * @cfg {String} itemCls
9930      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9931      */
9932     itemCls : "x-color-palette",
9933     /**
9934      * @cfg {String} value
9935      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9936      * the hex codes are case-sensitive.
9937      */
9938     value : null,
9939     clickEvent:'click',
9940     // private
9941     ctype: "Roo.ColorPalette",
9942
9943     /**
9944      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9945      */
9946     allowReselect : false,
9947
9948     /**
9949      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9950      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9951      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9952      * of colors with the width setting until the box is symmetrical.</p>
9953      * <p>You can override individual colors if needed:</p>
9954      * <pre><code>
9955 var cp = new Roo.ColorPalette();
9956 cp.colors[0] = "FF0000";  // change the first box to red
9957 </code></pre>
9958
9959 Or you can provide a custom array of your own for complete control:
9960 <pre><code>
9961 var cp = new Roo.ColorPalette();
9962 cp.colors = ["000000", "993300", "333300"];
9963 </code></pre>
9964      * @type Array
9965      */
9966     colors : [
9967         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9968         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9969         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9970         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9971         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9972     ],
9973
9974     // private
9975     onRender : function(container, position){
9976         var t = new Roo.MasterTemplate(
9977             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9978         );
9979         var c = this.colors;
9980         for(var i = 0, len = c.length; i < len; i++){
9981             t.add([c[i]]);
9982         }
9983         var el = document.createElement("div");
9984         el.className = this.itemCls;
9985         t.overwrite(el);
9986         container.dom.insertBefore(el, position);
9987         this.el = Roo.get(el);
9988         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9989         if(this.clickEvent != 'click'){
9990             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9991         }
9992     },
9993
9994     // private
9995     afterRender : function(){
9996         Roo.ColorPalette.superclass.afterRender.call(this);
9997         if(this.value){
9998             var s = this.value;
9999             this.value = null;
10000             this.select(s);
10001         }
10002     },
10003
10004     // private
10005     handleClick : function(e, t){
10006         e.preventDefault();
10007         if(!this.disabled){
10008             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10009             this.select(c.toUpperCase());
10010         }
10011     },
10012
10013     /**
10014      * Selects the specified color in the palette (fires the select event)
10015      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10016      */
10017     select : function(color){
10018         color = color.replace("#", "");
10019         if(color != this.value || this.allowReselect){
10020             var el = this.el;
10021             if(this.value){
10022                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10023             }
10024             el.child("a.color-"+color).addClass("x-color-palette-sel");
10025             this.value = color;
10026             this.fireEvent("select", this, color);
10027         }
10028     }
10029 });/*
10030  * Based on:
10031  * Ext JS Library 1.1.1
10032  * Copyright(c) 2006-2007, Ext JS, LLC.
10033  *
10034  * Originally Released Under LGPL - original licence link has changed is not relivant.
10035  *
10036  * Fork - LGPL
10037  * <script type="text/javascript">
10038  */
10039  
10040 /**
10041  * @class Roo.DatePicker
10042  * @extends Roo.Component
10043  * Simple date picker class.
10044  * @constructor
10045  * Create a new DatePicker
10046  * @param {Object} config The config object
10047  */
10048 Roo.DatePicker = function(config){
10049     Roo.DatePicker.superclass.constructor.call(this, config);
10050
10051     this.value = config && config.value ?
10052                  config.value.clearTime() : new Date().clearTime();
10053
10054     this.addEvents({
10055         /**
10056              * @event select
10057              * Fires when a date is selected
10058              * @param {DatePicker} this
10059              * @param {Date} date The selected date
10060              */
10061         select: true
10062     });
10063
10064     if(this.handler){
10065         this.on("select", this.handler,  this.scope || this);
10066     }
10067     // build the disabledDatesRE
10068     if(!this.disabledDatesRE && this.disabledDates){
10069         var dd = this.disabledDates;
10070         var re = "(?:";
10071         for(var i = 0; i < dd.length; i++){
10072             re += dd[i];
10073             if(i != dd.length-1) re += "|";
10074         }
10075         this.disabledDatesRE = new RegExp(re + ")");
10076     }
10077 };
10078
10079 Roo.extend(Roo.DatePicker, Roo.Component, {
10080     /**
10081      * @cfg {String} todayText
10082      * The text to display on the button that selects the current date (defaults to "Today")
10083      */
10084     todayText : "Today",
10085     /**
10086      * @cfg {String} okText
10087      * The text to display on the ok button
10088      */
10089     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10090     /**
10091      * @cfg {String} cancelText
10092      * The text to display on the cancel button
10093      */
10094     cancelText : "Cancel",
10095     /**
10096      * @cfg {String} todayTip
10097      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10098      */
10099     todayTip : "{0} (Spacebar)",
10100     /**
10101      * @cfg {Date} minDate
10102      * Minimum allowable date (JavaScript date object, defaults to null)
10103      */
10104     minDate : null,
10105     /**
10106      * @cfg {Date} maxDate
10107      * Maximum allowable date (JavaScript date object, defaults to null)
10108      */
10109     maxDate : null,
10110     /**
10111      * @cfg {String} minText
10112      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10113      */
10114     minText : "This date is before the minimum date",
10115     /**
10116      * @cfg {String} maxText
10117      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10118      */
10119     maxText : "This date is after the maximum date",
10120     /**
10121      * @cfg {String} format
10122      * The default date format string which can be overriden for localization support.  The format must be
10123      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10124      */
10125     format : "m/d/y",
10126     /**
10127      * @cfg {Array} disabledDays
10128      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10129      */
10130     disabledDays : null,
10131     /**
10132      * @cfg {String} disabledDaysText
10133      * The tooltip to display when the date falls on a disabled day (defaults to "")
10134      */
10135     disabledDaysText : "",
10136     /**
10137      * @cfg {RegExp} disabledDatesRE
10138      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10139      */
10140     disabledDatesRE : null,
10141     /**
10142      * @cfg {String} disabledDatesText
10143      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10144      */
10145     disabledDatesText : "",
10146     /**
10147      * @cfg {Boolean} constrainToViewport
10148      * True to constrain the date picker to the viewport (defaults to true)
10149      */
10150     constrainToViewport : true,
10151     /**
10152      * @cfg {Array} monthNames
10153      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10154      */
10155     monthNames : Date.monthNames,
10156     /**
10157      * @cfg {Array} dayNames
10158      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10159      */
10160     dayNames : Date.dayNames,
10161     /**
10162      * @cfg {String} nextText
10163      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10164      */
10165     nextText: 'Next Month (Control+Right)',
10166     /**
10167      * @cfg {String} prevText
10168      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10169      */
10170     prevText: 'Previous Month (Control+Left)',
10171     /**
10172      * @cfg {String} monthYearText
10173      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10174      */
10175     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10176     /**
10177      * @cfg {Number} startDay
10178      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10179      */
10180     startDay : 0,
10181     /**
10182      * @cfg {Bool} showClear
10183      * Show a clear button (usefull for date form elements that can be blank.)
10184      */
10185     
10186     showClear: false,
10187     
10188     /**
10189      * Sets the value of the date field
10190      * @param {Date} value The date to set
10191      */
10192     setValue : function(value){
10193         var old = this.value;
10194         this.value = value.clearTime(true);
10195         if(this.el){
10196             this.update(this.value);
10197         }
10198     },
10199
10200     /**
10201      * Gets the current selected value of the date field
10202      * @return {Date} The selected date
10203      */
10204     getValue : function(){
10205         return this.value;
10206     },
10207
10208     // private
10209     focus : function(){
10210         if(this.el){
10211             this.update(this.activeDate);
10212         }
10213     },
10214
10215     // private
10216     onRender : function(container, position){
10217         var m = [
10218              '<table cellspacing="0">',
10219                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10220                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10221         var dn = this.dayNames;
10222         for(var i = 0; i < 7; i++){
10223             var d = this.startDay+i;
10224             if(d > 6){
10225                 d = d-7;
10226             }
10227             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10228         }
10229         m[m.length] = "</tr></thead><tbody><tr>";
10230         for(var i = 0; i < 42; i++) {
10231             if(i % 7 == 0 && i != 0){
10232                 m[m.length] = "</tr><tr>";
10233             }
10234             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10235         }
10236         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10237             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10238
10239         var el = document.createElement("div");
10240         el.className = "x-date-picker";
10241         el.innerHTML = m.join("");
10242
10243         container.dom.insertBefore(el, position);
10244
10245         this.el = Roo.get(el);
10246         this.eventEl = Roo.get(el.firstChild);
10247
10248         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10249             handler: this.showPrevMonth,
10250             scope: this,
10251             preventDefault:true,
10252             stopDefault:true
10253         });
10254
10255         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10256             handler: this.showNextMonth,
10257             scope: this,
10258             preventDefault:true,
10259             stopDefault:true
10260         });
10261
10262         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10263
10264         this.monthPicker = this.el.down('div.x-date-mp');
10265         this.monthPicker.enableDisplayMode('block');
10266         
10267         var kn = new Roo.KeyNav(this.eventEl, {
10268             "left" : function(e){
10269                 e.ctrlKey ?
10270                     this.showPrevMonth() :
10271                     this.update(this.activeDate.add("d", -1));
10272             },
10273
10274             "right" : function(e){
10275                 e.ctrlKey ?
10276                     this.showNextMonth() :
10277                     this.update(this.activeDate.add("d", 1));
10278             },
10279
10280             "up" : function(e){
10281                 e.ctrlKey ?
10282                     this.showNextYear() :
10283                     this.update(this.activeDate.add("d", -7));
10284             },
10285
10286             "down" : function(e){
10287                 e.ctrlKey ?
10288                     this.showPrevYear() :
10289                     this.update(this.activeDate.add("d", 7));
10290             },
10291
10292             "pageUp" : function(e){
10293                 this.showNextMonth();
10294             },
10295
10296             "pageDown" : function(e){
10297                 this.showPrevMonth();
10298             },
10299
10300             "enter" : function(e){
10301                 e.stopPropagation();
10302                 return true;
10303             },
10304
10305             scope : this
10306         });
10307
10308         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10309
10310         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10311
10312         this.el.unselectable();
10313         
10314         this.cells = this.el.select("table.x-date-inner tbody td");
10315         this.textNodes = this.el.query("table.x-date-inner tbody span");
10316
10317         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10318             text: "&#160;",
10319             tooltip: this.monthYearText
10320         });
10321
10322         this.mbtn.on('click', this.showMonthPicker, this);
10323         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10324
10325
10326         var today = (new Date()).dateFormat(this.format);
10327         
10328         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10329         if (this.showClear) {
10330             baseTb.add( new Roo.Toolbar.Fill());
10331         }
10332         baseTb.add({
10333             text: String.format(this.todayText, today),
10334             tooltip: String.format(this.todayTip, today),
10335             handler: this.selectToday,
10336             scope: this
10337         });
10338         
10339         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10340             
10341         //});
10342         if (this.showClear) {
10343             
10344             baseTb.add( new Roo.Toolbar.Fill());
10345             baseTb.add({
10346                 text: '&#160;',
10347                 cls: 'x-btn-icon x-btn-clear',
10348                 handler: function() {
10349                     //this.value = '';
10350                     this.fireEvent("select", this, '');
10351                 },
10352                 scope: this
10353             });
10354         }
10355         
10356         
10357         if(Roo.isIE){
10358             this.el.repaint();
10359         }
10360         this.update(this.value);
10361     },
10362
10363     createMonthPicker : function(){
10364         if(!this.monthPicker.dom.firstChild){
10365             var buf = ['<table border="0" cellspacing="0">'];
10366             for(var i = 0; i < 6; i++){
10367                 buf.push(
10368                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10369                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10370                     i == 0 ?
10371                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10372                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10373                 );
10374             }
10375             buf.push(
10376                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10377                     this.okText,
10378                     '</button><button type="button" class="x-date-mp-cancel">',
10379                     this.cancelText,
10380                     '</button></td></tr>',
10381                 '</table>'
10382             );
10383             this.monthPicker.update(buf.join(''));
10384             this.monthPicker.on('click', this.onMonthClick, this);
10385             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10386
10387             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10388             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10389
10390             this.mpMonths.each(function(m, a, i){
10391                 i += 1;
10392                 if((i%2) == 0){
10393                     m.dom.xmonth = 5 + Math.round(i * .5);
10394                 }else{
10395                     m.dom.xmonth = Math.round((i-1) * .5);
10396                 }
10397             });
10398         }
10399     },
10400
10401     showMonthPicker : function(){
10402         this.createMonthPicker();
10403         var size = this.el.getSize();
10404         this.monthPicker.setSize(size);
10405         this.monthPicker.child('table').setSize(size);
10406
10407         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10408         this.updateMPMonth(this.mpSelMonth);
10409         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10410         this.updateMPYear(this.mpSelYear);
10411
10412         this.monthPicker.slideIn('t', {duration:.2});
10413     },
10414
10415     updateMPYear : function(y){
10416         this.mpyear = y;
10417         var ys = this.mpYears.elements;
10418         for(var i = 1; i <= 10; i++){
10419             var td = ys[i-1], y2;
10420             if((i%2) == 0){
10421                 y2 = y + Math.round(i * .5);
10422                 td.firstChild.innerHTML = y2;
10423                 td.xyear = y2;
10424             }else{
10425                 y2 = y - (5-Math.round(i * .5));
10426                 td.firstChild.innerHTML = y2;
10427                 td.xyear = y2;
10428             }
10429             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10430         }
10431     },
10432
10433     updateMPMonth : function(sm){
10434         this.mpMonths.each(function(m, a, i){
10435             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10436         });
10437     },
10438
10439     selectMPMonth: function(m){
10440         
10441     },
10442
10443     onMonthClick : function(e, t){
10444         e.stopEvent();
10445         var el = new Roo.Element(t), pn;
10446         if(el.is('button.x-date-mp-cancel')){
10447             this.hideMonthPicker();
10448         }
10449         else if(el.is('button.x-date-mp-ok')){
10450             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10451             this.hideMonthPicker();
10452         }
10453         else if(pn = el.up('td.x-date-mp-month', 2)){
10454             this.mpMonths.removeClass('x-date-mp-sel');
10455             pn.addClass('x-date-mp-sel');
10456             this.mpSelMonth = pn.dom.xmonth;
10457         }
10458         else if(pn = el.up('td.x-date-mp-year', 2)){
10459             this.mpYears.removeClass('x-date-mp-sel');
10460             pn.addClass('x-date-mp-sel');
10461             this.mpSelYear = pn.dom.xyear;
10462         }
10463         else if(el.is('a.x-date-mp-prev')){
10464             this.updateMPYear(this.mpyear-10);
10465         }
10466         else if(el.is('a.x-date-mp-next')){
10467             this.updateMPYear(this.mpyear+10);
10468         }
10469     },
10470
10471     onMonthDblClick : function(e, t){
10472         e.stopEvent();
10473         var el = new Roo.Element(t), pn;
10474         if(pn = el.up('td.x-date-mp-month', 2)){
10475             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10476             this.hideMonthPicker();
10477         }
10478         else if(pn = el.up('td.x-date-mp-year', 2)){
10479             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10480             this.hideMonthPicker();
10481         }
10482     },
10483
10484     hideMonthPicker : function(disableAnim){
10485         if(this.monthPicker){
10486             if(disableAnim === true){
10487                 this.monthPicker.hide();
10488             }else{
10489                 this.monthPicker.slideOut('t', {duration:.2});
10490             }
10491         }
10492     },
10493
10494     // private
10495     showPrevMonth : function(e){
10496         this.update(this.activeDate.add("mo", -1));
10497     },
10498
10499     // private
10500     showNextMonth : function(e){
10501         this.update(this.activeDate.add("mo", 1));
10502     },
10503
10504     // private
10505     showPrevYear : function(){
10506         this.update(this.activeDate.add("y", -1));
10507     },
10508
10509     // private
10510     showNextYear : function(){
10511         this.update(this.activeDate.add("y", 1));
10512     },
10513
10514     // private
10515     handleMouseWheel : function(e){
10516         var delta = e.getWheelDelta();
10517         if(delta > 0){
10518             this.showPrevMonth();
10519             e.stopEvent();
10520         } else if(delta < 0){
10521             this.showNextMonth();
10522             e.stopEvent();
10523         }
10524     },
10525
10526     // private
10527     handleDateClick : function(e, t){
10528         e.stopEvent();
10529         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10530             this.setValue(new Date(t.dateValue));
10531             this.fireEvent("select", this, this.value);
10532         }
10533     },
10534
10535     // private
10536     selectToday : function(){
10537         this.setValue(new Date().clearTime());
10538         this.fireEvent("select", this, this.value);
10539     },
10540
10541     // private
10542     update : function(date){
10543         var vd = this.activeDate;
10544         this.activeDate = date;
10545         if(vd && this.el){
10546             var t = date.getTime();
10547             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10548                 this.cells.removeClass("x-date-selected");
10549                 this.cells.each(function(c){
10550                    if(c.dom.firstChild.dateValue == t){
10551                        c.addClass("x-date-selected");
10552                        setTimeout(function(){
10553                             try{c.dom.firstChild.focus();}catch(e){}
10554                        }, 50);
10555                        return false;
10556                    }
10557                 });
10558                 return;
10559             }
10560         }
10561         var days = date.getDaysInMonth();
10562         var firstOfMonth = date.getFirstDateOfMonth();
10563         var startingPos = firstOfMonth.getDay()-this.startDay;
10564
10565         if(startingPos <= this.startDay){
10566             startingPos += 7;
10567         }
10568
10569         var pm = date.add("mo", -1);
10570         var prevStart = pm.getDaysInMonth()-startingPos;
10571
10572         var cells = this.cells.elements;
10573         var textEls = this.textNodes;
10574         days += startingPos;
10575
10576         // convert everything to numbers so it's fast
10577         var day = 86400000;
10578         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10579         var today = new Date().clearTime().getTime();
10580         var sel = date.clearTime().getTime();
10581         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10582         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10583         var ddMatch = this.disabledDatesRE;
10584         var ddText = this.disabledDatesText;
10585         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10586         var ddaysText = this.disabledDaysText;
10587         var format = this.format;
10588
10589         var setCellClass = function(cal, cell){
10590             cell.title = "";
10591             var t = d.getTime();
10592             cell.firstChild.dateValue = t;
10593             if(t == today){
10594                 cell.className += " x-date-today";
10595                 cell.title = cal.todayText;
10596             }
10597             if(t == sel){
10598                 cell.className += " x-date-selected";
10599                 setTimeout(function(){
10600                     try{cell.firstChild.focus();}catch(e){}
10601                 }, 50);
10602             }
10603             // disabling
10604             if(t < min) {
10605                 cell.className = " x-date-disabled";
10606                 cell.title = cal.minText;
10607                 return;
10608             }
10609             if(t > max) {
10610                 cell.className = " x-date-disabled";
10611                 cell.title = cal.maxText;
10612                 return;
10613             }
10614             if(ddays){
10615                 if(ddays.indexOf(d.getDay()) != -1){
10616                     cell.title = ddaysText;
10617                     cell.className = " x-date-disabled";
10618                 }
10619             }
10620             if(ddMatch && format){
10621                 var fvalue = d.dateFormat(format);
10622                 if(ddMatch.test(fvalue)){
10623                     cell.title = ddText.replace("%0", fvalue);
10624                     cell.className = " x-date-disabled";
10625                 }
10626             }
10627         };
10628
10629         var i = 0;
10630         for(; i < startingPos; i++) {
10631             textEls[i].innerHTML = (++prevStart);
10632             d.setDate(d.getDate()+1);
10633             cells[i].className = "x-date-prevday";
10634             setCellClass(this, cells[i]);
10635         }
10636         for(; i < days; i++){
10637             intDay = i - startingPos + 1;
10638             textEls[i].innerHTML = (intDay);
10639             d.setDate(d.getDate()+1);
10640             cells[i].className = "x-date-active";
10641             setCellClass(this, cells[i]);
10642         }
10643         var extraDays = 0;
10644         for(; i < 42; i++) {
10645              textEls[i].innerHTML = (++extraDays);
10646              d.setDate(d.getDate()+1);
10647              cells[i].className = "x-date-nextday";
10648              setCellClass(this, cells[i]);
10649         }
10650
10651         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10652
10653         if(!this.internalRender){
10654             var main = this.el.dom.firstChild;
10655             var w = main.offsetWidth;
10656             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10657             Roo.fly(main).setWidth(w);
10658             this.internalRender = true;
10659             // opera does not respect the auto grow header center column
10660             // then, after it gets a width opera refuses to recalculate
10661             // without a second pass
10662             if(Roo.isOpera && !this.secondPass){
10663                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10664                 this.secondPass = true;
10665                 this.update.defer(10, this, [date]);
10666             }
10667         }
10668     }
10669 });/*
10670  * Based on:
10671  * Ext JS Library 1.1.1
10672  * Copyright(c) 2006-2007, Ext JS, LLC.
10673  *
10674  * Originally Released Under LGPL - original licence link has changed is not relivant.
10675  *
10676  * Fork - LGPL
10677  * <script type="text/javascript">
10678  */
10679 /**
10680  * @class Roo.TabPanel
10681  * @extends Roo.util.Observable
10682  * A lightweight tab container.
10683  * <br><br>
10684  * Usage:
10685  * <pre><code>
10686 // basic tabs 1, built from existing content
10687 var tabs = new Roo.TabPanel("tabs1");
10688 tabs.addTab("script", "View Script");
10689 tabs.addTab("markup", "View Markup");
10690 tabs.activate("script");
10691
10692 // more advanced tabs, built from javascript
10693 var jtabs = new Roo.TabPanel("jtabs");
10694 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10695
10696 // set up the UpdateManager
10697 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10698 var updater = tab2.getUpdateManager();
10699 updater.setDefaultUrl("ajax1.htm");
10700 tab2.on('activate', updater.refresh, updater, true);
10701
10702 // Use setUrl for Ajax loading
10703 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10704 tab3.setUrl("ajax2.htm", null, true);
10705
10706 // Disabled tab
10707 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10708 tab4.disable();
10709
10710 jtabs.activate("jtabs-1");
10711  * </code></pre>
10712  * @constructor
10713  * Create a new TabPanel.
10714  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10715  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10716  */
10717 Roo.TabPanel = function(container, config){
10718     /**
10719     * The container element for this TabPanel.
10720     * @type Roo.Element
10721     */
10722     this.el = Roo.get(container, true);
10723     if(config){
10724         if(typeof config == "boolean"){
10725             this.tabPosition = config ? "bottom" : "top";
10726         }else{
10727             Roo.apply(this, config);
10728         }
10729     }
10730     if(this.tabPosition == "bottom"){
10731         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10732         this.el.addClass("x-tabs-bottom");
10733     }
10734     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10735     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10736     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10737     if(Roo.isIE){
10738         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10739     }
10740     if(this.tabPosition != "bottom"){
10741     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10742      * @type Roo.Element
10743      */
10744       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10745       this.el.addClass("x-tabs-top");
10746     }
10747     this.items = [];
10748
10749     this.bodyEl.setStyle("position", "relative");
10750
10751     this.active = null;
10752     this.activateDelegate = this.activate.createDelegate(this);
10753
10754     this.addEvents({
10755         /**
10756          * @event tabchange
10757          * Fires when the active tab changes
10758          * @param {Roo.TabPanel} this
10759          * @param {Roo.TabPanelItem} activePanel The new active tab
10760          */
10761         "tabchange": true,
10762         /**
10763          * @event beforetabchange
10764          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10765          * @param {Roo.TabPanel} this
10766          * @param {Object} e Set cancel to true on this object to cancel the tab change
10767          * @param {Roo.TabPanelItem} tab The tab being changed to
10768          */
10769         "beforetabchange" : true
10770     });
10771
10772     Roo.EventManager.onWindowResize(this.onResize, this);
10773     this.cpad = this.el.getPadding("lr");
10774     this.hiddenCount = 0;
10775
10776     Roo.TabPanel.superclass.constructor.call(this);
10777 };
10778
10779 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10780         /*
10781          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10782          */
10783     tabPosition : "top",
10784         /*
10785          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10786          */
10787     currentTabWidth : 0,
10788         /*
10789          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10790          */
10791     minTabWidth : 40,
10792         /*
10793          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10794          */
10795     maxTabWidth : 250,
10796         /*
10797          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10798          */
10799     preferredTabWidth : 175,
10800         /*
10801          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10802          */
10803     resizeTabs : false,
10804         /*
10805          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10806          */
10807     monitorResize : true,
10808
10809     /**
10810      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10811      * @param {String} id The id of the div to use <b>or create</b>
10812      * @param {String} text The text for the tab
10813      * @param {String} content (optional) Content to put in the TabPanelItem body
10814      * @param {Boolean} closable (optional) True to create a close icon on the tab
10815      * @return {Roo.TabPanelItem} The created TabPanelItem
10816      */
10817     addTab : function(id, text, content, closable){
10818         var item = new Roo.TabPanelItem(this, id, text, closable);
10819         this.addTabItem(item);
10820         if(content){
10821             item.setContent(content);
10822         }
10823         return item;
10824     },
10825
10826     /**
10827      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10828      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10829      * @return {Roo.TabPanelItem}
10830      */
10831     getTab : function(id){
10832         return this.items[id];
10833     },
10834
10835     /**
10836      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10837      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10838      */
10839     hideTab : function(id){
10840         var t = this.items[id];
10841         if(!t.isHidden()){
10842            t.setHidden(true);
10843            this.hiddenCount++;
10844            this.autoSizeTabs();
10845         }
10846     },
10847
10848     /**
10849      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10850      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10851      */
10852     unhideTab : function(id){
10853         var t = this.items[id];
10854         if(t.isHidden()){
10855            t.setHidden(false);
10856            this.hiddenCount--;
10857            this.autoSizeTabs();
10858         }
10859     },
10860
10861     /**
10862      * Adds an existing {@link Roo.TabPanelItem}.
10863      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10864      */
10865     addTabItem : function(item){
10866         this.items[item.id] = item;
10867         this.items.push(item);
10868         if(this.resizeTabs){
10869            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10870            this.autoSizeTabs();
10871         }else{
10872             item.autoSize();
10873         }
10874     },
10875
10876     /**
10877      * Removes a {@link Roo.TabPanelItem}.
10878      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10879      */
10880     removeTab : function(id){
10881         var items = this.items;
10882         var tab = items[id];
10883         if(!tab) { return; }
10884         var index = items.indexOf(tab);
10885         if(this.active == tab && items.length > 1){
10886             var newTab = this.getNextAvailable(index);
10887             if(newTab) {
10888                 newTab.activate();
10889             }
10890         }
10891         this.stripEl.dom.removeChild(tab.pnode.dom);
10892         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10893             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10894         }
10895         items.splice(index, 1);
10896         delete this.items[tab.id];
10897         tab.fireEvent("close", tab);
10898         tab.purgeListeners();
10899         this.autoSizeTabs();
10900     },
10901
10902     getNextAvailable : function(start){
10903         var items = this.items;
10904         var index = start;
10905         // look for a next tab that will slide over to
10906         // replace the one being removed
10907         while(index < items.length){
10908             var item = items[++index];
10909             if(item && !item.isHidden()){
10910                 return item;
10911             }
10912         }
10913         // if one isn't found select the previous tab (on the left)
10914         index = start;
10915         while(index >= 0){
10916             var item = items[--index];
10917             if(item && !item.isHidden()){
10918                 return item;
10919             }
10920         }
10921         return null;
10922     },
10923
10924     /**
10925      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10926      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10927      */
10928     disableTab : function(id){
10929         var tab = this.items[id];
10930         if(tab && this.active != tab){
10931             tab.disable();
10932         }
10933     },
10934
10935     /**
10936      * Enables a {@link Roo.TabPanelItem} that is disabled.
10937      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10938      */
10939     enableTab : function(id){
10940         var tab = this.items[id];
10941         tab.enable();
10942     },
10943
10944     /**
10945      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10946      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10947      * @return {Roo.TabPanelItem} The TabPanelItem.
10948      */
10949     activate : function(id){
10950         var tab = this.items[id];
10951         if(!tab){
10952             return null;
10953         }
10954         if(tab == this.active || tab.disabled){
10955             return tab;
10956         }
10957         var e = {};
10958         this.fireEvent("beforetabchange", this, e, tab);
10959         if(e.cancel !== true && !tab.disabled){
10960             if(this.active){
10961                 this.active.hide();
10962             }
10963             this.active = this.items[id];
10964             this.active.show();
10965             this.fireEvent("tabchange", this, this.active);
10966         }
10967         return tab;
10968     },
10969
10970     /**
10971      * Gets the active {@link Roo.TabPanelItem}.
10972      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10973      */
10974     getActiveTab : function(){
10975         return this.active;
10976     },
10977
10978     /**
10979      * Updates the tab body element to fit the height of the container element
10980      * for overflow scrolling
10981      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10982      */
10983     syncHeight : function(targetHeight){
10984         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10985         var bm = this.bodyEl.getMargins();
10986         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10987         this.bodyEl.setHeight(newHeight);
10988         return newHeight;
10989     },
10990
10991     onResize : function(){
10992         if(this.monitorResize){
10993             this.autoSizeTabs();
10994         }
10995     },
10996
10997     /**
10998      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10999      */
11000     beginUpdate : function(){
11001         this.updating = true;
11002     },
11003
11004     /**
11005      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11006      */
11007     endUpdate : function(){
11008         this.updating = false;
11009         this.autoSizeTabs();
11010     },
11011
11012     /**
11013      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11014      */
11015     autoSizeTabs : function(){
11016         var count = this.items.length;
11017         var vcount = count - this.hiddenCount;
11018         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11019         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11020         var availWidth = Math.floor(w / vcount);
11021         var b = this.stripBody;
11022         if(b.getWidth() > w){
11023             var tabs = this.items;
11024             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11025             if(availWidth < this.minTabWidth){
11026                 /*if(!this.sleft){    // incomplete scrolling code
11027                     this.createScrollButtons();
11028                 }
11029                 this.showScroll();
11030                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11031             }
11032         }else{
11033             if(this.currentTabWidth < this.preferredTabWidth){
11034                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11035             }
11036         }
11037     },
11038
11039     /**
11040      * Returns the number of tabs in this TabPanel.
11041      * @return {Number}
11042      */
11043      getCount : function(){
11044          return this.items.length;
11045      },
11046
11047     /**
11048      * Resizes all the tabs to the passed width
11049      * @param {Number} The new width
11050      */
11051     setTabWidth : function(width){
11052         this.currentTabWidth = width;
11053         for(var i = 0, len = this.items.length; i < len; i++) {
11054                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11055         }
11056     },
11057
11058     /**
11059      * Destroys this TabPanel
11060      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11061      */
11062     destroy : function(removeEl){
11063         Roo.EventManager.removeResizeListener(this.onResize, this);
11064         for(var i = 0, len = this.items.length; i < len; i++){
11065             this.items[i].purgeListeners();
11066         }
11067         if(removeEl === true){
11068             this.el.update("");
11069             this.el.remove();
11070         }
11071     }
11072 });
11073
11074 /**
11075  * @class Roo.TabPanelItem
11076  * @extends Roo.util.Observable
11077  * Represents an individual item (tab plus body) in a TabPanel.
11078  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11079  * @param {String} id The id of this TabPanelItem
11080  * @param {String} text The text for the tab of this TabPanelItem
11081  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11082  */
11083 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11084     /**
11085      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11086      * @type Roo.TabPanel
11087      */
11088     this.tabPanel = tabPanel;
11089     /**
11090      * The id for this TabPanelItem
11091      * @type String
11092      */
11093     this.id = id;
11094     /** @private */
11095     this.disabled = false;
11096     /** @private */
11097     this.text = text;
11098     /** @private */
11099     this.loaded = false;
11100     this.closable = closable;
11101
11102     /**
11103      * The body element for this TabPanelItem.
11104      * @type Roo.Element
11105      */
11106     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11107     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11108     this.bodyEl.setStyle("display", "block");
11109     this.bodyEl.setStyle("zoom", "1");
11110     this.hideAction();
11111
11112     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11113     /** @private */
11114     this.el = Roo.get(els.el, true);
11115     this.inner = Roo.get(els.inner, true);
11116     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11117     this.pnode = Roo.get(els.el.parentNode, true);
11118     this.el.on("mousedown", this.onTabMouseDown, this);
11119     this.el.on("click", this.onTabClick, this);
11120     /** @private */
11121     if(closable){
11122         var c = Roo.get(els.close, true);
11123         c.dom.title = this.closeText;
11124         c.addClassOnOver("close-over");
11125         c.on("click", this.closeClick, this);
11126      }
11127
11128     this.addEvents({
11129          /**
11130          * @event activate
11131          * Fires when this tab becomes the active tab.
11132          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11133          * @param {Roo.TabPanelItem} this
11134          */
11135         "activate": true,
11136         /**
11137          * @event beforeclose
11138          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11139          * @param {Roo.TabPanelItem} this
11140          * @param {Object} e Set cancel to true on this object to cancel the close.
11141          */
11142         "beforeclose": true,
11143         /**
11144          * @event close
11145          * Fires when this tab is closed.
11146          * @param {Roo.TabPanelItem} this
11147          */
11148          "close": true,
11149         /**
11150          * @event deactivate
11151          * Fires when this tab is no longer the active tab.
11152          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11153          * @param {Roo.TabPanelItem} this
11154          */
11155          "deactivate" : true
11156     });
11157     this.hidden = false;
11158
11159     Roo.TabPanelItem.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11163     purgeListeners : function(){
11164        Roo.util.Observable.prototype.purgeListeners.call(this);
11165        this.el.removeAllListeners();
11166     },
11167     /**
11168      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11169      */
11170     show : function(){
11171         this.pnode.addClass("on");
11172         this.showAction();
11173         if(Roo.isOpera){
11174             this.tabPanel.stripWrap.repaint();
11175         }
11176         this.fireEvent("activate", this.tabPanel, this);
11177     },
11178
11179     /**
11180      * Returns true if this tab is the active tab.
11181      * @return {Boolean}
11182      */
11183     isActive : function(){
11184         return this.tabPanel.getActiveTab() == this;
11185     },
11186
11187     /**
11188      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11189      */
11190     hide : function(){
11191         this.pnode.removeClass("on");
11192         this.hideAction();
11193         this.fireEvent("deactivate", this.tabPanel, this);
11194     },
11195
11196     hideAction : function(){
11197         this.bodyEl.hide();
11198         this.bodyEl.setStyle("position", "absolute");
11199         this.bodyEl.setLeft("-20000px");
11200         this.bodyEl.setTop("-20000px");
11201     },
11202
11203     showAction : function(){
11204         this.bodyEl.setStyle("position", "relative");
11205         this.bodyEl.setTop("");
11206         this.bodyEl.setLeft("");
11207         this.bodyEl.show();
11208     },
11209
11210     /**
11211      * Set the tooltip for the tab.
11212      * @param {String} tooltip The tab's tooltip
11213      */
11214     setTooltip : function(text){
11215         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11216             this.textEl.dom.qtip = text;
11217             this.textEl.dom.removeAttribute('title');
11218         }else{
11219             this.textEl.dom.title = text;
11220         }
11221     },
11222
11223     onTabClick : function(e){
11224         e.preventDefault();
11225         this.tabPanel.activate(this.id);
11226     },
11227
11228     onTabMouseDown : function(e){
11229         e.preventDefault();
11230         this.tabPanel.activate(this.id);
11231     },
11232
11233     getWidth : function(){
11234         return this.inner.getWidth();
11235     },
11236
11237     setWidth : function(width){
11238         var iwidth = width - this.pnode.getPadding("lr");
11239         this.inner.setWidth(iwidth);
11240         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11241         this.pnode.setWidth(width);
11242     },
11243
11244     /**
11245      * Show or hide the tab
11246      * @param {Boolean} hidden True to hide or false to show.
11247      */
11248     setHidden : function(hidden){
11249         this.hidden = hidden;
11250         this.pnode.setStyle("display", hidden ? "none" : "");
11251     },
11252
11253     /**
11254      * Returns true if this tab is "hidden"
11255      * @return {Boolean}
11256      */
11257     isHidden : function(){
11258         return this.hidden;
11259     },
11260
11261     /**
11262      * Returns the text for this tab
11263      * @return {String}
11264      */
11265     getText : function(){
11266         return this.text;
11267     },
11268
11269     autoSize : function(){
11270         //this.el.beginMeasure();
11271         this.textEl.setWidth(1);
11272         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11273         //this.el.endMeasure();
11274     },
11275
11276     /**
11277      * Sets the text for the tab (Note: this also sets the tooltip text)
11278      * @param {String} text The tab's text and tooltip
11279      */
11280     setText : function(text){
11281         this.text = text;
11282         this.textEl.update(text);
11283         this.setTooltip(text);
11284         if(!this.tabPanel.resizeTabs){
11285             this.autoSize();
11286         }
11287     },
11288     /**
11289      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11290      */
11291     activate : function(){
11292         this.tabPanel.activate(this.id);
11293     },
11294
11295     /**
11296      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11297      */
11298     disable : function(){
11299         if(this.tabPanel.active != this){
11300             this.disabled = true;
11301             this.pnode.addClass("disabled");
11302         }
11303     },
11304
11305     /**
11306      * Enables this TabPanelItem if it was previously disabled.
11307      */
11308     enable : function(){
11309         this.disabled = false;
11310         this.pnode.removeClass("disabled");
11311     },
11312
11313     /**
11314      * Sets the content for this TabPanelItem.
11315      * @param {String} content The content
11316      * @param {Boolean} loadScripts true to look for and load scripts
11317      */
11318     setContent : function(content, loadScripts){
11319         this.bodyEl.update(content, loadScripts);
11320     },
11321
11322     /**
11323      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11324      * @return {Roo.UpdateManager} The UpdateManager
11325      */
11326     getUpdateManager : function(){
11327         return this.bodyEl.getUpdateManager();
11328     },
11329
11330     /**
11331      * Set a URL to be used to load the content for this TabPanelItem.
11332      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11333      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11334      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11335      * @return {Roo.UpdateManager} The UpdateManager
11336      */
11337     setUrl : function(url, params, loadOnce){
11338         if(this.refreshDelegate){
11339             this.un('activate', this.refreshDelegate);
11340         }
11341         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11342         this.on("activate", this.refreshDelegate);
11343         return this.bodyEl.getUpdateManager();
11344     },
11345
11346     /** @private */
11347     _handleRefresh : function(url, params, loadOnce){
11348         if(!loadOnce || !this.loaded){
11349             var updater = this.bodyEl.getUpdateManager();
11350             updater.update(url, params, this._setLoaded.createDelegate(this));
11351         }
11352     },
11353
11354     /**
11355      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11356      *   Will fail silently if the setUrl method has not been called.
11357      *   This does not activate the panel, just updates its content.
11358      */
11359     refresh : function(){
11360         if(this.refreshDelegate){
11361            this.loaded = false;
11362            this.refreshDelegate();
11363         }
11364     },
11365
11366     /** @private */
11367     _setLoaded : function(){
11368         this.loaded = true;
11369     },
11370
11371     /** @private */
11372     closeClick : function(e){
11373         var o = {};
11374         e.stopEvent();
11375         this.fireEvent("beforeclose", this, o);
11376         if(o.cancel !== true){
11377             this.tabPanel.removeTab(this.id);
11378         }
11379     },
11380     /**
11381      * The text displayed in the tooltip for the close icon.
11382      * @type String
11383      */
11384     closeText : "Close this tab"
11385 });
11386
11387 /** @private */
11388 Roo.TabPanel.prototype.createStrip = function(container){
11389     var strip = document.createElement("div");
11390     strip.className = "x-tabs-wrap";
11391     container.appendChild(strip);
11392     return strip;
11393 };
11394 /** @private */
11395 Roo.TabPanel.prototype.createStripList = function(strip){
11396     // div wrapper for retard IE
11397     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11398     return strip.firstChild.firstChild.firstChild.firstChild;
11399 };
11400 /** @private */
11401 Roo.TabPanel.prototype.createBody = function(container){
11402     var body = document.createElement("div");
11403     Roo.id(body, "tab-body");
11404     Roo.fly(body).addClass("x-tabs-body");
11405     container.appendChild(body);
11406     return body;
11407 };
11408 /** @private */
11409 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11410     var body = Roo.getDom(id);
11411     if(!body){
11412         body = document.createElement("div");
11413         body.id = id;
11414     }
11415     Roo.fly(body).addClass("x-tabs-item-body");
11416     bodyEl.insertBefore(body, bodyEl.firstChild);
11417     return body;
11418 };
11419 /** @private */
11420 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11421     var td = document.createElement("td");
11422     stripEl.appendChild(td);
11423     if(closable){
11424         td.className = "x-tabs-closable";
11425         if(!this.closeTpl){
11426             this.closeTpl = new Roo.Template(
11427                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11428                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11429                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11430             );
11431         }
11432         var el = this.closeTpl.overwrite(td, {"text": text});
11433         var close = el.getElementsByTagName("div")[0];
11434         var inner = el.getElementsByTagName("em")[0];
11435         return {"el": el, "close": close, "inner": inner};
11436     } else {
11437         if(!this.tabTpl){
11438             this.tabTpl = new Roo.Template(
11439                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11440                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11441             );
11442         }
11443         var el = this.tabTpl.overwrite(td, {"text": text});
11444         var inner = el.getElementsByTagName("em")[0];
11445         return {"el": el, "inner": inner};
11446     }
11447 };/*
11448  * Based on:
11449  * Ext JS Library 1.1.1
11450  * Copyright(c) 2006-2007, Ext JS, LLC.
11451  *
11452  * Originally Released Under LGPL - original licence link has changed is not relivant.
11453  *
11454  * Fork - LGPL
11455  * <script type="text/javascript">
11456  */
11457
11458 /**
11459  * @class Roo.Button
11460  * @extends Roo.util.Observable
11461  * Simple Button class
11462  * @cfg {String} text The button text
11463  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11464  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11465  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11466  * @cfg {Object} scope The scope of the handler
11467  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11468  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11469  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11470  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11471  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11472  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11473    applies if enableToggle = true)
11474  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11475  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11476   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11477  * @constructor
11478  * Create a new button
11479  * @param {Object} config The config object
11480  */
11481 Roo.Button = function(renderTo, config)
11482 {
11483     if (!config) {
11484         config = renderTo;
11485         renderTo = config.renderTo || false;
11486     }
11487     
11488     Roo.apply(this, config);
11489     this.addEvents({
11490         /**
11491              * @event click
11492              * Fires when this button is clicked
11493              * @param {Button} this
11494              * @param {EventObject} e The click event
11495              */
11496             "click" : true,
11497         /**
11498              * @event toggle
11499              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11500              * @param {Button} this
11501              * @param {Boolean} pressed
11502              */
11503             "toggle" : true,
11504         /**
11505              * @event mouseover
11506              * Fires when the mouse hovers over the button
11507              * @param {Button} this
11508              * @param {Event} e The event object
11509              */
11510         'mouseover' : true,
11511         /**
11512              * @event mouseout
11513              * Fires when the mouse exits the button
11514              * @param {Button} this
11515              * @param {Event} e The event object
11516              */
11517         'mouseout': true,
11518          /**
11519              * @event render
11520              * Fires when the button is rendered
11521              * @param {Button} this
11522              */
11523         'render': true
11524     });
11525     if(this.menu){
11526         this.menu = Roo.menu.MenuMgr.get(this.menu);
11527     }
11528     // register listeners first!!  - so render can be captured..
11529     Roo.util.Observable.call(this);
11530     if(renderTo){
11531         this.render(renderTo);
11532     }
11533     
11534   
11535 };
11536
11537 Roo.extend(Roo.Button, Roo.util.Observable, {
11538     /**
11539      * 
11540      */
11541     
11542     /**
11543      * Read-only. True if this button is hidden
11544      * @type Boolean
11545      */
11546     hidden : false,
11547     /**
11548      * Read-only. True if this button is disabled
11549      * @type Boolean
11550      */
11551     disabled : false,
11552     /**
11553      * Read-only. True if this button is pressed (only if enableToggle = true)
11554      * @type Boolean
11555      */
11556     pressed : false,
11557
11558     /**
11559      * @cfg {Number} tabIndex 
11560      * The DOM tabIndex for this button (defaults to undefined)
11561      */
11562     tabIndex : undefined,
11563
11564     /**
11565      * @cfg {Boolean} enableToggle
11566      * True to enable pressed/not pressed toggling (defaults to false)
11567      */
11568     enableToggle: false,
11569     /**
11570      * @cfg {Mixed} menu
11571      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11572      */
11573     menu : undefined,
11574     /**
11575      * @cfg {String} menuAlign
11576      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11577      */
11578     menuAlign : "tl-bl?",
11579
11580     /**
11581      * @cfg {String} iconCls
11582      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11583      */
11584     iconCls : undefined,
11585     /**
11586      * @cfg {String} type
11587      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11588      */
11589     type : 'button',
11590
11591     // private
11592     menuClassTarget: 'tr',
11593
11594     /**
11595      * @cfg {String} clickEvent
11596      * The type of event to map to the button's event handler (defaults to 'click')
11597      */
11598     clickEvent : 'click',
11599
11600     /**
11601      * @cfg {Boolean} handleMouseEvents
11602      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11603      */
11604     handleMouseEvents : true,
11605
11606     /**
11607      * @cfg {String} tooltipType
11608      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11609      */
11610     tooltipType : 'qtip',
11611
11612     /**
11613      * @cfg {String} cls
11614      * A CSS class to apply to the button's main element.
11615      */
11616     
11617     /**
11618      * @cfg {Roo.Template} template (Optional)
11619      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11620      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11621      * require code modifications if required elements (e.g. a button) aren't present.
11622      */
11623
11624     // private
11625     render : function(renderTo){
11626         var btn;
11627         if(this.hideParent){
11628             this.parentEl = Roo.get(renderTo);
11629         }
11630         if(!this.dhconfig){
11631             if(!this.template){
11632                 if(!Roo.Button.buttonTemplate){
11633                     // hideous table template
11634                     Roo.Button.buttonTemplate = new Roo.Template(
11635                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11636                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11637                         "</tr></tbody></table>");
11638                 }
11639                 this.template = Roo.Button.buttonTemplate;
11640             }
11641             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11642             var btnEl = btn.child("button:first");
11643             btnEl.on('focus', this.onFocus, this);
11644             btnEl.on('blur', this.onBlur, this);
11645             if(this.cls){
11646                 btn.addClass(this.cls);
11647             }
11648             if(this.icon){
11649                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11650             }
11651             if(this.iconCls){
11652                 btnEl.addClass(this.iconCls);
11653                 if(!this.cls){
11654                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11655                 }
11656             }
11657             if(this.tabIndex !== undefined){
11658                 btnEl.dom.tabIndex = this.tabIndex;
11659             }
11660             if(this.tooltip){
11661                 if(typeof this.tooltip == 'object'){
11662                     Roo.QuickTips.tips(Roo.apply({
11663                           target: btnEl.id
11664                     }, this.tooltip));
11665                 } else {
11666                     btnEl.dom[this.tooltipType] = this.tooltip;
11667                 }
11668             }
11669         }else{
11670             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11671         }
11672         this.el = btn;
11673         if(this.id){
11674             this.el.dom.id = this.el.id = this.id;
11675         }
11676         if(this.menu){
11677             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11678             this.menu.on("show", this.onMenuShow, this);
11679             this.menu.on("hide", this.onMenuHide, this);
11680         }
11681         btn.addClass("x-btn");
11682         if(Roo.isIE && !Roo.isIE7){
11683             this.autoWidth.defer(1, this);
11684         }else{
11685             this.autoWidth();
11686         }
11687         if(this.handleMouseEvents){
11688             btn.on("mouseover", this.onMouseOver, this);
11689             btn.on("mouseout", this.onMouseOut, this);
11690             btn.on("mousedown", this.onMouseDown, this);
11691         }
11692         btn.on(this.clickEvent, this.onClick, this);
11693         //btn.on("mouseup", this.onMouseUp, this);
11694         if(this.hidden){
11695             this.hide();
11696         }
11697         if(this.disabled){
11698             this.disable();
11699         }
11700         Roo.ButtonToggleMgr.register(this);
11701         if(this.pressed){
11702             this.el.addClass("x-btn-pressed");
11703         }
11704         if(this.repeat){
11705             var repeater = new Roo.util.ClickRepeater(btn,
11706                 typeof this.repeat == "object" ? this.repeat : {}
11707             );
11708             repeater.on("click", this.onClick,  this);
11709         }
11710         
11711         this.fireEvent('render', this);
11712         
11713     },
11714     /**
11715      * Returns the button's underlying element
11716      * @return {Roo.Element} The element
11717      */
11718     getEl : function(){
11719         return this.el;  
11720     },
11721     
11722     /**
11723      * Destroys this Button and removes any listeners.
11724      */
11725     destroy : function(){
11726         Roo.ButtonToggleMgr.unregister(this);
11727         this.el.removeAllListeners();
11728         this.purgeListeners();
11729         this.el.remove();
11730     },
11731
11732     // private
11733     autoWidth : function(){
11734         if(this.el){
11735             this.el.setWidth("auto");
11736             if(Roo.isIE7 && Roo.isStrict){
11737                 var ib = this.el.child('button');
11738                 if(ib && ib.getWidth() > 20){
11739                     ib.clip();
11740                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11741                 }
11742             }
11743             if(this.minWidth){
11744                 if(this.hidden){
11745                     this.el.beginMeasure();
11746                 }
11747                 if(this.el.getWidth() < this.minWidth){
11748                     this.el.setWidth(this.minWidth);
11749                 }
11750                 if(this.hidden){
11751                     this.el.endMeasure();
11752                 }
11753             }
11754         }
11755     },
11756
11757     /**
11758      * Assigns this button's click handler
11759      * @param {Function} handler The function to call when the button is clicked
11760      * @param {Object} scope (optional) Scope for the function passed in
11761      */
11762     setHandler : function(handler, scope){
11763         this.handler = handler;
11764         this.scope = scope;  
11765     },
11766     
11767     /**
11768      * Sets this button's text
11769      * @param {String} text The button text
11770      */
11771     setText : function(text){
11772         this.text = text;
11773         if(this.el){
11774             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11775         }
11776         this.autoWidth();
11777     },
11778     
11779     /**
11780      * Gets the text for this button
11781      * @return {String} The button text
11782      */
11783     getText : function(){
11784         return this.text;  
11785     },
11786     
11787     /**
11788      * Show this button
11789      */
11790     show: function(){
11791         this.hidden = false;
11792         if(this.el){
11793             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11794         }
11795     },
11796     
11797     /**
11798      * Hide this button
11799      */
11800     hide: function(){
11801         this.hidden = true;
11802         if(this.el){
11803             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11804         }
11805     },
11806     
11807     /**
11808      * Convenience function for boolean show/hide
11809      * @param {Boolean} visible True to show, false to hide
11810      */
11811     setVisible: function(visible){
11812         if(visible) {
11813             this.show();
11814         }else{
11815             this.hide();
11816         }
11817     },
11818     
11819     /**
11820      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11821      * @param {Boolean} state (optional) Force a particular state
11822      */
11823     toggle : function(state){
11824         state = state === undefined ? !this.pressed : state;
11825         if(state != this.pressed){
11826             if(state){
11827                 this.el.addClass("x-btn-pressed");
11828                 this.pressed = true;
11829                 this.fireEvent("toggle", this, true);
11830             }else{
11831                 this.el.removeClass("x-btn-pressed");
11832                 this.pressed = false;
11833                 this.fireEvent("toggle", this, false);
11834             }
11835             if(this.toggleHandler){
11836                 this.toggleHandler.call(this.scope || this, this, state);
11837             }
11838         }
11839     },
11840     
11841     /**
11842      * Focus the button
11843      */
11844     focus : function(){
11845         this.el.child('button:first').focus();
11846     },
11847     
11848     /**
11849      * Disable this button
11850      */
11851     disable : function(){
11852         if(this.el){
11853             this.el.addClass("x-btn-disabled");
11854         }
11855         this.disabled = true;
11856     },
11857     
11858     /**
11859      * Enable this button
11860      */
11861     enable : function(){
11862         if(this.el){
11863             this.el.removeClass("x-btn-disabled");
11864         }
11865         this.disabled = false;
11866     },
11867
11868     /**
11869      * Convenience function for boolean enable/disable
11870      * @param {Boolean} enabled True to enable, false to disable
11871      */
11872     setDisabled : function(v){
11873         this[v !== true ? "enable" : "disable"]();
11874     },
11875
11876     // private
11877     onClick : function(e){
11878         if(e){
11879             e.preventDefault();
11880         }
11881         if(e.button != 0){
11882             return;
11883         }
11884         if(!this.disabled){
11885             if(this.enableToggle){
11886                 this.toggle();
11887             }
11888             if(this.menu && !this.menu.isVisible()){
11889                 this.menu.show(this.el, this.menuAlign);
11890             }
11891             this.fireEvent("click", this, e);
11892             if(this.handler){
11893                 this.el.removeClass("x-btn-over");
11894                 this.handler.call(this.scope || this, this, e);
11895             }
11896         }
11897     },
11898     // private
11899     onMouseOver : function(e){
11900         if(!this.disabled){
11901             this.el.addClass("x-btn-over");
11902             this.fireEvent('mouseover', this, e);
11903         }
11904     },
11905     // private
11906     onMouseOut : function(e){
11907         if(!e.within(this.el,  true)){
11908             this.el.removeClass("x-btn-over");
11909             this.fireEvent('mouseout', this, e);
11910         }
11911     },
11912     // private
11913     onFocus : function(e){
11914         if(!this.disabled){
11915             this.el.addClass("x-btn-focus");
11916         }
11917     },
11918     // private
11919     onBlur : function(e){
11920         this.el.removeClass("x-btn-focus");
11921     },
11922     // private
11923     onMouseDown : function(e){
11924         if(!this.disabled && e.button == 0){
11925             this.el.addClass("x-btn-click");
11926             Roo.get(document).on('mouseup', this.onMouseUp, this);
11927         }
11928     },
11929     // private
11930     onMouseUp : function(e){
11931         if(e.button == 0){
11932             this.el.removeClass("x-btn-click");
11933             Roo.get(document).un('mouseup', this.onMouseUp, this);
11934         }
11935     },
11936     // private
11937     onMenuShow : function(e){
11938         this.el.addClass("x-btn-menu-active");
11939     },
11940     // private
11941     onMenuHide : function(e){
11942         this.el.removeClass("x-btn-menu-active");
11943     }   
11944 });
11945
11946 // Private utility class used by Button
11947 Roo.ButtonToggleMgr = function(){
11948    var groups = {};
11949    
11950    function toggleGroup(btn, state){
11951        if(state){
11952            var g = groups[btn.toggleGroup];
11953            for(var i = 0, l = g.length; i < l; i++){
11954                if(g[i] != btn){
11955                    g[i].toggle(false);
11956                }
11957            }
11958        }
11959    }
11960    
11961    return {
11962        register : function(btn){
11963            if(!btn.toggleGroup){
11964                return;
11965            }
11966            var g = groups[btn.toggleGroup];
11967            if(!g){
11968                g = groups[btn.toggleGroup] = [];
11969            }
11970            g.push(btn);
11971            btn.on("toggle", toggleGroup);
11972        },
11973        
11974        unregister : function(btn){
11975            if(!btn.toggleGroup){
11976                return;
11977            }
11978            var g = groups[btn.toggleGroup];
11979            if(g){
11980                g.remove(btn);
11981                btn.un("toggle", toggleGroup);
11982            }
11983        }
11984    };
11985 }();/*
11986  * Based on:
11987  * Ext JS Library 1.1.1
11988  * Copyright(c) 2006-2007, Ext JS, LLC.
11989  *
11990  * Originally Released Under LGPL - original licence link has changed is not relivant.
11991  *
11992  * Fork - LGPL
11993  * <script type="text/javascript">
11994  */
11995  
11996 /**
11997  * @class Roo.SplitButton
11998  * @extends Roo.Button
11999  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12000  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12001  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12002  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12003  * @cfg {String} arrowTooltip The title attribute of the arrow
12004  * @constructor
12005  * Create a new menu button
12006  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12007  * @param {Object} config The config object
12008  */
12009 Roo.SplitButton = function(renderTo, config){
12010     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12011     /**
12012      * @event arrowclick
12013      * Fires when this button's arrow is clicked
12014      * @param {SplitButton} this
12015      * @param {EventObject} e The click event
12016      */
12017     this.addEvents({"arrowclick":true});
12018 };
12019
12020 Roo.extend(Roo.SplitButton, Roo.Button, {
12021     render : function(renderTo){
12022         // this is one sweet looking template!
12023         var tpl = new Roo.Template(
12024             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12025             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12026             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12027             "</tbody></table></td><td>",
12028             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12029             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12030             "</tbody></table></td></tr></table>"
12031         );
12032         var btn = tpl.append(renderTo, [this.text, this.type], true);
12033         var btnEl = btn.child("button");
12034         if(this.cls){
12035             btn.addClass(this.cls);
12036         }
12037         if(this.icon){
12038             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12039         }
12040         if(this.iconCls){
12041             btnEl.addClass(this.iconCls);
12042             if(!this.cls){
12043                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12044             }
12045         }
12046         this.el = btn;
12047         if(this.handleMouseEvents){
12048             btn.on("mouseover", this.onMouseOver, this);
12049             btn.on("mouseout", this.onMouseOut, this);
12050             btn.on("mousedown", this.onMouseDown, this);
12051             btn.on("mouseup", this.onMouseUp, this);
12052         }
12053         btn.on(this.clickEvent, this.onClick, this);
12054         if(this.tooltip){
12055             if(typeof this.tooltip == 'object'){
12056                 Roo.QuickTips.tips(Roo.apply({
12057                       target: btnEl.id
12058                 }, this.tooltip));
12059             } else {
12060                 btnEl.dom[this.tooltipType] = this.tooltip;
12061             }
12062         }
12063         if(this.arrowTooltip){
12064             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12065         }
12066         if(this.hidden){
12067             this.hide();
12068         }
12069         if(this.disabled){
12070             this.disable();
12071         }
12072         if(this.pressed){
12073             this.el.addClass("x-btn-pressed");
12074         }
12075         if(Roo.isIE && !Roo.isIE7){
12076             this.autoWidth.defer(1, this);
12077         }else{
12078             this.autoWidth();
12079         }
12080         if(this.menu){
12081             this.menu.on("show", this.onMenuShow, this);
12082             this.menu.on("hide", this.onMenuHide, this);
12083         }
12084         this.fireEvent('render', this);
12085     },
12086
12087     // private
12088     autoWidth : function(){
12089         if(this.el){
12090             var tbl = this.el.child("table:first");
12091             var tbl2 = this.el.child("table:last");
12092             this.el.setWidth("auto");
12093             tbl.setWidth("auto");
12094             if(Roo.isIE7 && Roo.isStrict){
12095                 var ib = this.el.child('button:first');
12096                 if(ib && ib.getWidth() > 20){
12097                     ib.clip();
12098                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12099                 }
12100             }
12101             if(this.minWidth){
12102                 if(this.hidden){
12103                     this.el.beginMeasure();
12104                 }
12105                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12106                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12107                 }
12108                 if(this.hidden){
12109                     this.el.endMeasure();
12110                 }
12111             }
12112             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12113         } 
12114     },
12115     /**
12116      * Sets this button's click handler
12117      * @param {Function} handler The function to call when the button is clicked
12118      * @param {Object} scope (optional) Scope for the function passed above
12119      */
12120     setHandler : function(handler, scope){
12121         this.handler = handler;
12122         this.scope = scope;  
12123     },
12124     
12125     /**
12126      * Sets this button's arrow click handler
12127      * @param {Function} handler The function to call when the arrow is clicked
12128      * @param {Object} scope (optional) Scope for the function passed above
12129      */
12130     setArrowHandler : function(handler, scope){
12131         this.arrowHandler = handler;
12132         this.scope = scope;  
12133     },
12134     
12135     /**
12136      * Focus the button
12137      */
12138     focus : function(){
12139         if(this.el){
12140             this.el.child("button:first").focus();
12141         }
12142     },
12143
12144     // private
12145     onClick : function(e){
12146         e.preventDefault();
12147         if(!this.disabled){
12148             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12149                 if(this.menu && !this.menu.isVisible()){
12150                     this.menu.show(this.el, this.menuAlign);
12151                 }
12152                 this.fireEvent("arrowclick", this, e);
12153                 if(this.arrowHandler){
12154                     this.arrowHandler.call(this.scope || this, this, e);
12155                 }
12156             }else{
12157                 this.fireEvent("click", this, e);
12158                 if(this.handler){
12159                     this.handler.call(this.scope || this, this, e);
12160                 }
12161             }
12162         }
12163     },
12164     // private
12165     onMouseDown : function(e){
12166         if(!this.disabled){
12167             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12168         }
12169     },
12170     // private
12171     onMouseUp : function(e){
12172         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12173     }   
12174 });
12175
12176
12177 // backwards compat
12178 Roo.MenuButton = Roo.SplitButton;/*
12179  * Based on:
12180  * Ext JS Library 1.1.1
12181  * Copyright(c) 2006-2007, Ext JS, LLC.
12182  *
12183  * Originally Released Under LGPL - original licence link has changed is not relivant.
12184  *
12185  * Fork - LGPL
12186  * <script type="text/javascript">
12187  */
12188
12189 /**
12190  * @class Roo.Toolbar
12191  * Basic Toolbar class.
12192  * @constructor
12193  * Creates a new Toolbar
12194  * @param {Object} config The config object
12195  */ 
12196 Roo.Toolbar = function(container, buttons, config)
12197 {
12198     /// old consturctor format still supported..
12199     if(container instanceof Array){ // omit the container for later rendering
12200         buttons = container;
12201         config = buttons;
12202         container = null;
12203     }
12204     if (typeof(container) == 'object' && container.xtype) {
12205         config = container;
12206         container = config.container;
12207         buttons = config.buttons; // not really - use items!!
12208     }
12209     var xitems = [];
12210     if (config && config.items) {
12211         xitems = config.items;
12212         delete config.items;
12213     }
12214     Roo.apply(this, config);
12215     this.buttons = buttons;
12216     
12217     if(container){
12218         this.render(container);
12219     }
12220     Roo.each(xitems, function(b) {
12221         this.add(b);
12222     }, this);
12223     
12224 };
12225
12226 Roo.Toolbar.prototype = {
12227     /**
12228      * @cfg {Roo.data.Store} items
12229      * array of button configs or elements to add
12230      */
12231     
12232     /**
12233      * @cfg {String/HTMLElement/Element} container
12234      * The id or element that will contain the toolbar
12235      */
12236     // private
12237     render : function(ct){
12238         this.el = Roo.get(ct);
12239         if(this.cls){
12240             this.el.addClass(this.cls);
12241         }
12242         // using a table allows for vertical alignment
12243         // 100% width is needed by Safari...
12244         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12245         this.tr = this.el.child("tr", true);
12246         var autoId = 0;
12247         this.items = new Roo.util.MixedCollection(false, function(o){
12248             return o.id || ("item" + (++autoId));
12249         });
12250         if(this.buttons){
12251             this.add.apply(this, this.buttons);
12252             delete this.buttons;
12253         }
12254     },
12255
12256     /**
12257      * Adds element(s) to the toolbar -- this function takes a variable number of 
12258      * arguments of mixed type and adds them to the toolbar.
12259      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12260      * <ul>
12261      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12262      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12263      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12264      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12265      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12266      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12267      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12268      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12269      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12270      * </ul>
12271      * @param {Mixed} arg2
12272      * @param {Mixed} etc.
12273      */
12274     add : function(){
12275         var a = arguments, l = a.length;
12276         for(var i = 0; i < l; i++){
12277             this._add(a[i]);
12278         }
12279     },
12280     // private..
12281     _add : function(el) {
12282         
12283         if (el.xtype) {
12284             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12285         }
12286         
12287         if (el.applyTo){ // some kind of form field
12288             return this.addField(el);
12289         } 
12290         if (el.render){ // some kind of Toolbar.Item
12291             return this.addItem(el);
12292         }
12293         if (typeof el == "string"){ // string
12294             if(el == "separator" || el == "-"){
12295                 return this.addSeparator();
12296             }
12297             if (el == " "){
12298                 return this.addSpacer();
12299             }
12300             if(el == "->"){
12301                 return this.addFill();
12302             }
12303             return this.addText(el);
12304             
12305         }
12306         if(el.tagName){ // element
12307             return this.addElement(el);
12308         }
12309         if(typeof el == "object"){ // must be button config?
12310             return this.addButton(el);
12311         }
12312         // and now what?!?!
12313         return false;
12314         
12315     },
12316     
12317     /**
12318      * Add an Xtype element
12319      * @param {Object} xtype Xtype Object
12320      * @return {Object} created Object
12321      */
12322     addxtype : function(e){
12323         return this.add(e);  
12324     },
12325     
12326     /**
12327      * Returns the Element for this toolbar.
12328      * @return {Roo.Element}
12329      */
12330     getEl : function(){
12331         return this.el;  
12332     },
12333     
12334     /**
12335      * Adds a separator
12336      * @return {Roo.Toolbar.Item} The separator item
12337      */
12338     addSeparator : function(){
12339         return this.addItem(new Roo.Toolbar.Separator());
12340     },
12341
12342     /**
12343      * Adds a spacer element
12344      * @return {Roo.Toolbar.Spacer} The spacer item
12345      */
12346     addSpacer : function(){
12347         return this.addItem(new Roo.Toolbar.Spacer());
12348     },
12349
12350     /**
12351      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12352      * @return {Roo.Toolbar.Fill} The fill item
12353      */
12354     addFill : function(){
12355         return this.addItem(new Roo.Toolbar.Fill());
12356     },
12357
12358     /**
12359      * Adds any standard HTML element to the toolbar
12360      * @param {String/HTMLElement/Element} el The element or id of the element to add
12361      * @return {Roo.Toolbar.Item} The element's item
12362      */
12363     addElement : function(el){
12364         return this.addItem(new Roo.Toolbar.Item(el));
12365     },
12366     /**
12367      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12368      * @type Roo.util.MixedCollection  
12369      */
12370     items : false,
12371      
12372     /**
12373      * Adds any Toolbar.Item or subclass
12374      * @param {Roo.Toolbar.Item} item
12375      * @return {Roo.Toolbar.Item} The item
12376      */
12377     addItem : function(item){
12378         var td = this.nextBlock();
12379         item.render(td);
12380         this.items.add(item);
12381         return item;
12382     },
12383     
12384     /**
12385      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12386      * @param {Object/Array} config A button config or array of configs
12387      * @return {Roo.Toolbar.Button/Array}
12388      */
12389     addButton : function(config){
12390         if(config instanceof Array){
12391             var buttons = [];
12392             for(var i = 0, len = config.length; i < len; i++) {
12393                 buttons.push(this.addButton(config[i]));
12394             }
12395             return buttons;
12396         }
12397         var b = config;
12398         if(!(config instanceof Roo.Toolbar.Button)){
12399             b = config.split ?
12400                 new Roo.Toolbar.SplitButton(config) :
12401                 new Roo.Toolbar.Button(config);
12402         }
12403         var td = this.nextBlock();
12404         b.render(td);
12405         this.items.add(b);
12406         return b;
12407     },
12408     
12409     /**
12410      * Adds text to the toolbar
12411      * @param {String} text The text to add
12412      * @return {Roo.Toolbar.Item} The element's item
12413      */
12414     addText : function(text){
12415         return this.addItem(new Roo.Toolbar.TextItem(text));
12416     },
12417     
12418     /**
12419      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12420      * @param {Number} index The index where the item is to be inserted
12421      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12422      * @return {Roo.Toolbar.Button/Item}
12423      */
12424     insertButton : function(index, item){
12425         if(item instanceof Array){
12426             var buttons = [];
12427             for(var i = 0, len = item.length; i < len; i++) {
12428                buttons.push(this.insertButton(index + i, item[i]));
12429             }
12430             return buttons;
12431         }
12432         if (!(item instanceof Roo.Toolbar.Button)){
12433            item = new Roo.Toolbar.Button(item);
12434         }
12435         var td = document.createElement("td");
12436         this.tr.insertBefore(td, this.tr.childNodes[index]);
12437         item.render(td);
12438         this.items.insert(index, item);
12439         return item;
12440     },
12441     
12442     /**
12443      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12444      * @param {Object} config
12445      * @return {Roo.Toolbar.Item} The element's item
12446      */
12447     addDom : function(config, returnEl){
12448         var td = this.nextBlock();
12449         Roo.DomHelper.overwrite(td, config);
12450         var ti = new Roo.Toolbar.Item(td.firstChild);
12451         ti.render(td);
12452         this.items.add(ti);
12453         return ti;
12454     },
12455
12456     /**
12457      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12458      * @type Roo.util.MixedCollection  
12459      */
12460     fields : false,
12461     
12462     /**
12463      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12464      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12465      * @param {Roo.form.Field} field
12466      * @return {Roo.ToolbarItem}
12467      */
12468      
12469       
12470     addField : function(field) {
12471         if (!this.fields) {
12472             var autoId = 0;
12473             this.fields = new Roo.util.MixedCollection(false, function(o){
12474                 return o.id || ("item" + (++autoId));
12475             });
12476
12477         }
12478         
12479         var td = this.nextBlock();
12480         field.render(td);
12481         var ti = new Roo.Toolbar.Item(td.firstChild);
12482         ti.render(td);
12483         this.items.add(ti);
12484         this.fields.add(field);
12485         return ti;
12486     },
12487     /**
12488      * Hide the toolbar
12489      * @method hide
12490      */
12491      
12492       
12493     hide : function()
12494     {
12495         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12496         this.el.child('div').hide();
12497     },
12498     /**
12499      * Show the toolbar
12500      * @method show
12501      */
12502     show : function()
12503     {
12504         this.el.child('div').show();
12505     },
12506       
12507     // private
12508     nextBlock : function(){
12509         var td = document.createElement("td");
12510         this.tr.appendChild(td);
12511         return td;
12512     },
12513
12514     // private
12515     destroy : function(){
12516         if(this.items){ // rendered?
12517             Roo.destroy.apply(Roo, this.items.items);
12518         }
12519         if(this.fields){ // rendered?
12520             Roo.destroy.apply(Roo, this.fields.items);
12521         }
12522         Roo.Element.uncache(this.el, this.tr);
12523     }
12524 };
12525
12526 /**
12527  * @class Roo.Toolbar.Item
12528  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12529  * @constructor
12530  * Creates a new Item
12531  * @param {HTMLElement} el 
12532  */
12533 Roo.Toolbar.Item = function(el){
12534     this.el = Roo.getDom(el);
12535     this.id = Roo.id(this.el);
12536     this.hidden = false;
12537 };
12538
12539 Roo.Toolbar.Item.prototype = {
12540     
12541     /**
12542      * Get this item's HTML Element
12543      * @return {HTMLElement}
12544      */
12545     getEl : function(){
12546        return this.el;  
12547     },
12548
12549     // private
12550     render : function(td){
12551         this.td = td;
12552         td.appendChild(this.el);
12553     },
12554     
12555     /**
12556      * Removes and destroys this item.
12557      */
12558     destroy : function(){
12559         this.td.parentNode.removeChild(this.td);
12560     },
12561     
12562     /**
12563      * Shows this item.
12564      */
12565     show: function(){
12566         this.hidden = false;
12567         this.td.style.display = "";
12568     },
12569     
12570     /**
12571      * Hides this item.
12572      */
12573     hide: function(){
12574         this.hidden = true;
12575         this.td.style.display = "none";
12576     },
12577     
12578     /**
12579      * Convenience function for boolean show/hide.
12580      * @param {Boolean} visible true to show/false to hide
12581      */
12582     setVisible: function(visible){
12583         if(visible) {
12584             this.show();
12585         }else{
12586             this.hide();
12587         }
12588     },
12589     
12590     /**
12591      * Try to focus this item.
12592      */
12593     focus : function(){
12594         Roo.fly(this.el).focus();
12595     },
12596     
12597     /**
12598      * Disables this item.
12599      */
12600     disable : function(){
12601         Roo.fly(this.td).addClass("x-item-disabled");
12602         this.disabled = true;
12603         this.el.disabled = true;
12604     },
12605     
12606     /**
12607      * Enables this item.
12608      */
12609     enable : function(){
12610         Roo.fly(this.td).removeClass("x-item-disabled");
12611         this.disabled = false;
12612         this.el.disabled = false;
12613     }
12614 };
12615
12616
12617 /**
12618  * @class Roo.Toolbar.Separator
12619  * @extends Roo.Toolbar.Item
12620  * A simple toolbar separator class
12621  * @constructor
12622  * Creates a new Separator
12623  */
12624 Roo.Toolbar.Separator = function(){
12625     var s = document.createElement("span");
12626     s.className = "ytb-sep";
12627     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12628 };
12629 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12630     enable:Roo.emptyFn,
12631     disable:Roo.emptyFn,
12632     focus:Roo.emptyFn
12633 });
12634
12635 /**
12636  * @class Roo.Toolbar.Spacer
12637  * @extends Roo.Toolbar.Item
12638  * A simple element that adds extra horizontal space to a toolbar.
12639  * @constructor
12640  * Creates a new Spacer
12641  */
12642 Roo.Toolbar.Spacer = function(){
12643     var s = document.createElement("div");
12644     s.className = "ytb-spacer";
12645     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12646 };
12647 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12648     enable:Roo.emptyFn,
12649     disable:Roo.emptyFn,
12650     focus:Roo.emptyFn
12651 });
12652
12653 /**
12654  * @class Roo.Toolbar.Fill
12655  * @extends Roo.Toolbar.Spacer
12656  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12657  * @constructor
12658  * Creates a new Spacer
12659  */
12660 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12661     // private
12662     render : function(td){
12663         td.style.width = '100%';
12664         Roo.Toolbar.Fill.superclass.render.call(this, td);
12665     }
12666 });
12667
12668 /**
12669  * @class Roo.Toolbar.TextItem
12670  * @extends Roo.Toolbar.Item
12671  * A simple class that renders text directly into a toolbar.
12672  * @constructor
12673  * Creates a new TextItem
12674  * @param {String} text
12675  */
12676 Roo.Toolbar.TextItem = function(text){
12677     if (typeof(text) == 'object') {
12678         text = text.text;
12679     }
12680     var s = document.createElement("span");
12681     s.className = "ytb-text";
12682     s.innerHTML = text;
12683     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12684 };
12685 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12686     enable:Roo.emptyFn,
12687     disable:Roo.emptyFn,
12688     focus:Roo.emptyFn
12689 });
12690
12691 /**
12692  * @class Roo.Toolbar.Button
12693  * @extends Roo.Button
12694  * A button that renders into a toolbar.
12695  * @constructor
12696  * Creates a new Button
12697  * @param {Object} config A standard {@link Roo.Button} config object
12698  */
12699 Roo.Toolbar.Button = function(config){
12700     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12701 };
12702 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12703     render : function(td){
12704         this.td = td;
12705         Roo.Toolbar.Button.superclass.render.call(this, td);
12706     },
12707     
12708     /**
12709      * Removes and destroys this button
12710      */
12711     destroy : function(){
12712         Roo.Toolbar.Button.superclass.destroy.call(this);
12713         this.td.parentNode.removeChild(this.td);
12714     },
12715     
12716     /**
12717      * Shows this button
12718      */
12719     show: function(){
12720         this.hidden = false;
12721         this.td.style.display = "";
12722     },
12723     
12724     /**
12725      * Hides this button
12726      */
12727     hide: function(){
12728         this.hidden = true;
12729         this.td.style.display = "none";
12730     },
12731
12732     /**
12733      * Disables this item
12734      */
12735     disable : function(){
12736         Roo.fly(this.td).addClass("x-item-disabled");
12737         this.disabled = true;
12738     },
12739
12740     /**
12741      * Enables this item
12742      */
12743     enable : function(){
12744         Roo.fly(this.td).removeClass("x-item-disabled");
12745         this.disabled = false;
12746     }
12747 });
12748 // backwards compat
12749 Roo.ToolbarButton = Roo.Toolbar.Button;
12750
12751 /**
12752  * @class Roo.Toolbar.SplitButton
12753  * @extends Roo.SplitButton
12754  * A menu button that renders into a toolbar.
12755  * @constructor
12756  * Creates a new SplitButton
12757  * @param {Object} config A standard {@link Roo.SplitButton} config object
12758  */
12759 Roo.Toolbar.SplitButton = function(config){
12760     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12761 };
12762 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12763     render : function(td){
12764         this.td = td;
12765         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12766     },
12767     
12768     /**
12769      * Removes and destroys this button
12770      */
12771     destroy : function(){
12772         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12773         this.td.parentNode.removeChild(this.td);
12774     },
12775     
12776     /**
12777      * Shows this button
12778      */
12779     show: function(){
12780         this.hidden = false;
12781         this.td.style.display = "";
12782     },
12783     
12784     /**
12785      * Hides this button
12786      */
12787     hide: function(){
12788         this.hidden = true;
12789         this.td.style.display = "none";
12790     }
12791 });
12792
12793 // backwards compat
12794 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12795  * Based on:
12796  * Ext JS Library 1.1.1
12797  * Copyright(c) 2006-2007, Ext JS, LLC.
12798  *
12799  * Originally Released Under LGPL - original licence link has changed is not relivant.
12800  *
12801  * Fork - LGPL
12802  * <script type="text/javascript">
12803  */
12804  
12805 /**
12806  * @class Roo.PagingToolbar
12807  * @extends Roo.Toolbar
12808  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12809  * @constructor
12810  * Create a new PagingToolbar
12811  * @param {Object} config The config object
12812  */
12813 Roo.PagingToolbar = function(el, ds, config)
12814 {
12815     // old args format still supported... - xtype is prefered..
12816     if (typeof(el) == 'object' && el.xtype) {
12817         // created from xtype...
12818         config = el;
12819         ds = el.dataSource;
12820         el = config.container;
12821     }
12822     var items = [];
12823     if (config.items) {
12824         items = config.items;
12825         config.items = [];
12826     }
12827     
12828     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12829     this.ds = ds;
12830     this.cursor = 0;
12831     this.renderButtons(this.el);
12832     this.bind(ds);
12833     
12834     // supprot items array.
12835    
12836     Roo.each(items, function(e) {
12837         this.add(Roo.factory(e));
12838     },this);
12839     
12840 };
12841
12842 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12843     /**
12844      * @cfg {Roo.data.Store} dataSource
12845      * The underlying data store providing the paged data
12846      */
12847     /**
12848      * @cfg {String/HTMLElement/Element} container
12849      * container The id or element that will contain the toolbar
12850      */
12851     /**
12852      * @cfg {Boolean} displayInfo
12853      * True to display the displayMsg (defaults to false)
12854      */
12855     /**
12856      * @cfg {Number} pageSize
12857      * The number of records to display per page (defaults to 20)
12858      */
12859     pageSize: 20,
12860     /**
12861      * @cfg {String} displayMsg
12862      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12863      */
12864     displayMsg : 'Displaying {0} - {1} of {2}',
12865     /**
12866      * @cfg {String} emptyMsg
12867      * The message to display when no records are found (defaults to "No data to display")
12868      */
12869     emptyMsg : 'No data to display',
12870     /**
12871      * Customizable piece of the default paging text (defaults to "Page")
12872      * @type String
12873      */
12874     beforePageText : "Page",
12875     /**
12876      * Customizable piece of the default paging text (defaults to "of %0")
12877      * @type String
12878      */
12879     afterPageText : "of {0}",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "First Page")
12882      * @type String
12883      */
12884     firstText : "First Page",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "Previous Page")
12887      * @type String
12888      */
12889     prevText : "Previous Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Next Page")
12892      * @type String
12893      */
12894     nextText : "Next Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Last Page")
12897      * @type String
12898      */
12899     lastText : "Last Page",
12900     /**
12901      * Customizable piece of the default paging text (defaults to "Refresh")
12902      * @type String
12903      */
12904     refreshText : "Refresh",
12905
12906     // private
12907     renderButtons : function(el){
12908         Roo.PagingToolbar.superclass.render.call(this, el);
12909         this.first = this.addButton({
12910             tooltip: this.firstText,
12911             cls: "x-btn-icon x-grid-page-first",
12912             disabled: true,
12913             handler: this.onClick.createDelegate(this, ["first"])
12914         });
12915         this.prev = this.addButton({
12916             tooltip: this.prevText,
12917             cls: "x-btn-icon x-grid-page-prev",
12918             disabled: true,
12919             handler: this.onClick.createDelegate(this, ["prev"])
12920         });
12921         //this.addSeparator();
12922         this.add(this.beforePageText);
12923         this.field = Roo.get(this.addDom({
12924            tag: "input",
12925            type: "text",
12926            size: "3",
12927            value: "1",
12928            cls: "x-grid-page-number"
12929         }).el);
12930         this.field.on("keydown", this.onPagingKeydown, this);
12931         this.field.on("focus", function(){this.dom.select();});
12932         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12933         this.field.setHeight(18);
12934         //this.addSeparator();
12935         this.next = this.addButton({
12936             tooltip: this.nextText,
12937             cls: "x-btn-icon x-grid-page-next",
12938             disabled: true,
12939             handler: this.onClick.createDelegate(this, ["next"])
12940         });
12941         this.last = this.addButton({
12942             tooltip: this.lastText,
12943             cls: "x-btn-icon x-grid-page-last",
12944             disabled: true,
12945             handler: this.onClick.createDelegate(this, ["last"])
12946         });
12947         //this.addSeparator();
12948         this.loading = this.addButton({
12949             tooltip: this.refreshText,
12950             cls: "x-btn-icon x-grid-loading",
12951             handler: this.onClick.createDelegate(this, ["refresh"])
12952         });
12953
12954         if(this.displayInfo){
12955             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12956         }
12957     },
12958
12959     // private
12960     updateInfo : function(){
12961         if(this.displayEl){
12962             var count = this.ds.getCount();
12963             var msg = count == 0 ?
12964                 this.emptyMsg :
12965                 String.format(
12966                     this.displayMsg,
12967                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12968                 );
12969             this.displayEl.update(msg);
12970         }
12971     },
12972
12973     // private
12974     onLoad : function(ds, r, o){
12975        this.cursor = o.params ? o.params.start : 0;
12976        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12977
12978        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12979        this.field.dom.value = ap;
12980        this.first.setDisabled(ap == 1);
12981        this.prev.setDisabled(ap == 1);
12982        this.next.setDisabled(ap == ps);
12983        this.last.setDisabled(ap == ps);
12984        this.loading.enable();
12985        this.updateInfo();
12986     },
12987
12988     // private
12989     getPageData : function(){
12990         var total = this.ds.getTotalCount();
12991         return {
12992             total : total,
12993             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12994             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12995         };
12996     },
12997
12998     // private
12999     onLoadError : function(){
13000         this.loading.enable();
13001     },
13002
13003     // private
13004     onPagingKeydown : function(e){
13005         var k = e.getKey();
13006         var d = this.getPageData();
13007         if(k == e.RETURN){
13008             var v = this.field.dom.value, pageNum;
13009             if(!v || isNaN(pageNum = parseInt(v, 10))){
13010                 this.field.dom.value = d.activePage;
13011                 return;
13012             }
13013             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13014             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13015             e.stopEvent();
13016         }
13017         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13018         {
13019           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13020           this.field.dom.value = pageNum;
13021           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13022           e.stopEvent();
13023         }
13024         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13025         {
13026           var v = this.field.dom.value, pageNum; 
13027           var increment = (e.shiftKey) ? 10 : 1;
13028           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13029             increment *= -1;
13030           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13031             this.field.dom.value = d.activePage;
13032             return;
13033           }
13034           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13035           {
13036             this.field.dom.value = parseInt(v, 10) + increment;
13037             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13038             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13039           }
13040           e.stopEvent();
13041         }
13042     },
13043
13044     // private
13045     beforeLoad : function(){
13046         if(this.loading){
13047             this.loading.disable();
13048         }
13049     },
13050
13051     // private
13052     onClick : function(which){
13053         var ds = this.ds;
13054         switch(which){
13055             case "first":
13056                 ds.load({params:{start: 0, limit: this.pageSize}});
13057             break;
13058             case "prev":
13059                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13060             break;
13061             case "next":
13062                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13063             break;
13064             case "last":
13065                 var total = ds.getTotalCount();
13066                 var extra = total % this.pageSize;
13067                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13068                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13069             break;
13070             case "refresh":
13071                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13072             break;
13073         }
13074     },
13075
13076     /**
13077      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13078      * @param {Roo.data.Store} store The data store to unbind
13079      */
13080     unbind : function(ds){
13081         ds.un("beforeload", this.beforeLoad, this);
13082         ds.un("load", this.onLoad, this);
13083         ds.un("loadexception", this.onLoadError, this);
13084         ds.un("remove", this.updateInfo, this);
13085         ds.un("add", this.updateInfo, this);
13086         this.ds = undefined;
13087     },
13088
13089     /**
13090      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13091      * @param {Roo.data.Store} store The data store to bind
13092      */
13093     bind : function(ds){
13094         ds.on("beforeload", this.beforeLoad, this);
13095         ds.on("load", this.onLoad, this);
13096         ds.on("loadexception", this.onLoadError, this);
13097         ds.on("remove", this.updateInfo, this);
13098         ds.on("add", this.updateInfo, this);
13099         this.ds = ds;
13100     }
13101 });/*
13102  * Based on:
13103  * Ext JS Library 1.1.1
13104  * Copyright(c) 2006-2007, Ext JS, LLC.
13105  *
13106  * Originally Released Under LGPL - original licence link has changed is not relivant.
13107  *
13108  * Fork - LGPL
13109  * <script type="text/javascript">
13110  */
13111
13112 /**
13113  * @class Roo.Resizable
13114  * @extends Roo.util.Observable
13115  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13116  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13117  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13118  * the element will be wrapped for you automatically.</p>
13119  * <p>Here is the list of valid resize handles:</p>
13120  * <pre>
13121 Value   Description
13122 ------  -------------------
13123  'n'     north
13124  's'     south
13125  'e'     east
13126  'w'     west
13127  'nw'    northwest
13128  'sw'    southwest
13129  'se'    southeast
13130  'ne'    northeast
13131  'hd'    horizontal drag
13132  'all'   all
13133 </pre>
13134  * <p>Here's an example showing the creation of a typical Resizable:</p>
13135  * <pre><code>
13136 var resizer = new Roo.Resizable("element-id", {
13137     handles: 'all',
13138     minWidth: 200,
13139     minHeight: 100,
13140     maxWidth: 500,
13141     maxHeight: 400,
13142     pinned: true
13143 });
13144 resizer.on("resize", myHandler);
13145 </code></pre>
13146  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13147  * resizer.east.setDisplayed(false);</p>
13148  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13149  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13150  * resize operation's new size (defaults to [0, 0])
13151  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13152  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13153  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13154  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13155  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13156  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13157  * @cfg {Number} width The width of the element in pixels (defaults to null)
13158  * @cfg {Number} height The height of the element in pixels (defaults to null)
13159  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13160  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13161  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13162  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13163  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13164  * in favor of the handles config option (defaults to false)
13165  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13166  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13167  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13168  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13169  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13170  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13171  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13172  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13173  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13174  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13175  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13176  * @constructor
13177  * Create a new resizable component
13178  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13179  * @param {Object} config configuration options
13180   */
13181 Roo.Resizable = function(el, config)
13182 {
13183     this.el = Roo.get(el);
13184
13185     if(config && config.wrap){
13186         config.resizeChild = this.el;
13187         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13188         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13189         this.el.setStyle("overflow", "hidden");
13190         this.el.setPositioning(config.resizeChild.getPositioning());
13191         config.resizeChild.clearPositioning();
13192         if(!config.width || !config.height){
13193             var csize = config.resizeChild.getSize();
13194             this.el.setSize(csize.width, csize.height);
13195         }
13196         if(config.pinned && !config.adjustments){
13197             config.adjustments = "auto";
13198         }
13199     }
13200
13201     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13202     this.proxy.unselectable();
13203     this.proxy.enableDisplayMode('block');
13204
13205     Roo.apply(this, config);
13206
13207     if(this.pinned){
13208         this.disableTrackOver = true;
13209         this.el.addClass("x-resizable-pinned");
13210     }
13211     // if the element isn't positioned, make it relative
13212     var position = this.el.getStyle("position");
13213     if(position != "absolute" && position != "fixed"){
13214         this.el.setStyle("position", "relative");
13215     }
13216     if(!this.handles){ // no handles passed, must be legacy style
13217         this.handles = 's,e,se';
13218         if(this.multiDirectional){
13219             this.handles += ',n,w';
13220         }
13221     }
13222     if(this.handles == "all"){
13223         this.handles = "n s e w ne nw se sw";
13224     }
13225     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13226     var ps = Roo.Resizable.positions;
13227     for(var i = 0, len = hs.length; i < len; i++){
13228         if(hs[i] && ps[hs[i]]){
13229             var pos = ps[hs[i]];
13230             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13231         }
13232     }
13233     // legacy
13234     this.corner = this.southeast;
13235     
13236     // updateBox = the box can move..
13237     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13238         this.updateBox = true;
13239     }
13240
13241     this.activeHandle = null;
13242
13243     if(this.resizeChild){
13244         if(typeof this.resizeChild == "boolean"){
13245             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13246         }else{
13247             this.resizeChild = Roo.get(this.resizeChild, true);
13248         }
13249     }
13250     
13251     if(this.adjustments == "auto"){
13252         var rc = this.resizeChild;
13253         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13254         if(rc && (hw || hn)){
13255             rc.position("relative");
13256             rc.setLeft(hw ? hw.el.getWidth() : 0);
13257             rc.setTop(hn ? hn.el.getHeight() : 0);
13258         }
13259         this.adjustments = [
13260             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13261             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13262         ];
13263     }
13264
13265     if(this.draggable){
13266         this.dd = this.dynamic ?
13267             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13268         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13269     }
13270
13271     // public events
13272     this.addEvents({
13273         /**
13274          * @event beforeresize
13275          * Fired before resize is allowed. Set enabled to false to cancel resize.
13276          * @param {Roo.Resizable} this
13277          * @param {Roo.EventObject} e The mousedown event
13278          */
13279         "beforeresize" : true,
13280         /**
13281          * @event resize
13282          * Fired after a resize.
13283          * @param {Roo.Resizable} this
13284          * @param {Number} width The new width
13285          * @param {Number} height The new height
13286          * @param {Roo.EventObject} e The mouseup event
13287          */
13288         "resize" : true
13289     });
13290
13291     if(this.width !== null && this.height !== null){
13292         this.resizeTo(this.width, this.height);
13293     }else{
13294         this.updateChildSize();
13295     }
13296     if(Roo.isIE){
13297         this.el.dom.style.zoom = 1;
13298     }
13299     Roo.Resizable.superclass.constructor.call(this);
13300 };
13301
13302 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13303         resizeChild : false,
13304         adjustments : [0, 0],
13305         minWidth : 5,
13306         minHeight : 5,
13307         maxWidth : 10000,
13308         maxHeight : 10000,
13309         enabled : true,
13310         animate : false,
13311         duration : .35,
13312         dynamic : false,
13313         handles : false,
13314         multiDirectional : false,
13315         disableTrackOver : false,
13316         easing : 'easeOutStrong',
13317         widthIncrement : 0,
13318         heightIncrement : 0,
13319         pinned : false,
13320         width : null,
13321         height : null,
13322         preserveRatio : false,
13323         transparent: false,
13324         minX: 0,
13325         minY: 0,
13326         draggable: false,
13327
13328         /**
13329          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13330          */
13331         constrainTo: undefined,
13332         /**
13333          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13334          */
13335         resizeRegion: undefined,
13336
13337
13338     /**
13339      * Perform a manual resize
13340      * @param {Number} width
13341      * @param {Number} height
13342      */
13343     resizeTo : function(width, height){
13344         this.el.setSize(width, height);
13345         this.updateChildSize();
13346         this.fireEvent("resize", this, width, height, null);
13347     },
13348
13349     // private
13350     startSizing : function(e, handle){
13351         this.fireEvent("beforeresize", this, e);
13352         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13353
13354             if(!this.overlay){
13355                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13356                 this.overlay.unselectable();
13357                 this.overlay.enableDisplayMode("block");
13358                 this.overlay.on("mousemove", this.onMouseMove, this);
13359                 this.overlay.on("mouseup", this.onMouseUp, this);
13360             }
13361             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13362
13363             this.resizing = true;
13364             this.startBox = this.el.getBox();
13365             this.startPoint = e.getXY();
13366             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13367                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13368
13369             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13370             this.overlay.show();
13371
13372             if(this.constrainTo) {
13373                 var ct = Roo.get(this.constrainTo);
13374                 this.resizeRegion = ct.getRegion().adjust(
13375                     ct.getFrameWidth('t'),
13376                     ct.getFrameWidth('l'),
13377                     -ct.getFrameWidth('b'),
13378                     -ct.getFrameWidth('r')
13379                 );
13380             }
13381
13382             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13383             this.proxy.show();
13384             this.proxy.setBox(this.startBox);
13385             if(!this.dynamic){
13386                 this.proxy.setStyle('visibility', 'visible');
13387             }
13388         }
13389     },
13390
13391     // private
13392     onMouseDown : function(handle, e){
13393         if(this.enabled){
13394             e.stopEvent();
13395             this.activeHandle = handle;
13396             this.startSizing(e, handle);
13397         }
13398     },
13399
13400     // private
13401     onMouseUp : function(e){
13402         var size = this.resizeElement();
13403         this.resizing = false;
13404         this.handleOut();
13405         this.overlay.hide();
13406         this.proxy.hide();
13407         this.fireEvent("resize", this, size.width, size.height, e);
13408     },
13409
13410     // private
13411     updateChildSize : function(){
13412         if(this.resizeChild){
13413             var el = this.el;
13414             var child = this.resizeChild;
13415             var adj = this.adjustments;
13416             if(el.dom.offsetWidth){
13417                 var b = el.getSize(true);
13418                 child.setSize(b.width+adj[0], b.height+adj[1]);
13419             }
13420             // Second call here for IE
13421             // The first call enables instant resizing and
13422             // the second call corrects scroll bars if they
13423             // exist
13424             if(Roo.isIE){
13425                 setTimeout(function(){
13426                     if(el.dom.offsetWidth){
13427                         var b = el.getSize(true);
13428                         child.setSize(b.width+adj[0], b.height+adj[1]);
13429                     }
13430                 }, 10);
13431             }
13432         }
13433     },
13434
13435     // private
13436     snap : function(value, inc, min){
13437         if(!inc || !value) return value;
13438         var newValue = value;
13439         var m = value % inc;
13440         if(m > 0){
13441             if(m > (inc/2)){
13442                 newValue = value + (inc-m);
13443             }else{
13444                 newValue = value - m;
13445             }
13446         }
13447         return Math.max(min, newValue);
13448     },
13449
13450     // private
13451     resizeElement : function(){
13452         var box = this.proxy.getBox();
13453         if(this.updateBox){
13454             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13455         }else{
13456             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13457         }
13458         this.updateChildSize();
13459         if(!this.dynamic){
13460             this.proxy.hide();
13461         }
13462         return box;
13463     },
13464
13465     // private
13466     constrain : function(v, diff, m, mx){
13467         if(v - diff < m){
13468             diff = v - m;
13469         }else if(v - diff > mx){
13470             diff = mx - v;
13471         }
13472         return diff;
13473     },
13474
13475     // private
13476     onMouseMove : function(e){
13477         if(this.enabled){
13478             try{// try catch so if something goes wrong the user doesn't get hung
13479
13480             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13481                 return;
13482             }
13483
13484             //var curXY = this.startPoint;
13485             var curSize = this.curSize || this.startBox;
13486             var x = this.startBox.x, y = this.startBox.y;
13487             var ox = x, oy = y;
13488             var w = curSize.width, h = curSize.height;
13489             var ow = w, oh = h;
13490             var mw = this.minWidth, mh = this.minHeight;
13491             var mxw = this.maxWidth, mxh = this.maxHeight;
13492             var wi = this.widthIncrement;
13493             var hi = this.heightIncrement;
13494
13495             var eventXY = e.getXY();
13496             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13497             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13498
13499             var pos = this.activeHandle.position;
13500
13501             switch(pos){
13502                 case "east":
13503                     w += diffX;
13504                     w = Math.min(Math.max(mw, w), mxw);
13505                     break;
13506              
13507                 case "south":
13508                     h += diffY;
13509                     h = Math.min(Math.max(mh, h), mxh);
13510                     break;
13511                 case "southeast":
13512                     w += diffX;
13513                     h += diffY;
13514                     w = Math.min(Math.max(mw, w), mxw);
13515                     h = Math.min(Math.max(mh, h), mxh);
13516                     break;
13517                 case "north":
13518                     diffY = this.constrain(h, diffY, mh, mxh);
13519                     y += diffY;
13520                     h -= diffY;
13521                     break;
13522                 case "hdrag":
13523                     
13524                     if (wi) {
13525                         var adiffX = Math.abs(diffX);
13526                         var sub = (adiffX % wi); // how much 
13527                         if (sub > (wi/2)) { // far enough to snap
13528                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13529                         } else {
13530                             // remove difference.. 
13531                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13532                         }
13533                     }
13534                     x += diffX;
13535                     x = Math.max(this.minX, x);
13536                     break;
13537                 case "west":
13538                     diffX = this.constrain(w, diffX, mw, mxw);
13539                     x += diffX;
13540                     w -= diffX;
13541                     break;
13542                 case "northeast":
13543                     w += diffX;
13544                     w = Math.min(Math.max(mw, w), mxw);
13545                     diffY = this.constrain(h, diffY, mh, mxh);
13546                     y += diffY;
13547                     h -= diffY;
13548                     break;
13549                 case "northwest":
13550                     diffX = this.constrain(w, diffX, mw, mxw);
13551                     diffY = this.constrain(h, diffY, mh, mxh);
13552                     y += diffY;
13553                     h -= diffY;
13554                     x += diffX;
13555                     w -= diffX;
13556                     break;
13557                case "southwest":
13558                     diffX = this.constrain(w, diffX, mw, mxw);
13559                     h += diffY;
13560                     h = Math.min(Math.max(mh, h), mxh);
13561                     x += diffX;
13562                     w -= diffX;
13563                     break;
13564             }
13565
13566             var sw = this.snap(w, wi, mw);
13567             var sh = this.snap(h, hi, mh);
13568             if(sw != w || sh != h){
13569                 switch(pos){
13570                     case "northeast":
13571                         y -= sh - h;
13572                     break;
13573                     case "north":
13574                         y -= sh - h;
13575                         break;
13576                     case "southwest":
13577                         x -= sw - w;
13578                     break;
13579                     case "west":
13580                         x -= sw - w;
13581                         break;
13582                     case "northwest":
13583                         x -= sw - w;
13584                         y -= sh - h;
13585                     break;
13586                 }
13587                 w = sw;
13588                 h = sh;
13589             }
13590
13591             if(this.preserveRatio){
13592                 switch(pos){
13593                     case "southeast":
13594                     case "east":
13595                         h = oh * (w/ow);
13596                         h = Math.min(Math.max(mh, h), mxh);
13597                         w = ow * (h/oh);
13598                        break;
13599                     case "south":
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                         break;
13604                     case "northeast":
13605                         w = ow * (h/oh);
13606                         w = Math.min(Math.max(mw, w), mxw);
13607                         h = oh * (w/ow);
13608                     break;
13609                     case "north":
13610                         var tw = w;
13611                         w = ow * (h/oh);
13612                         w = Math.min(Math.max(mw, w), mxw);
13613                         h = oh * (w/ow);
13614                         x += (tw - w) / 2;
13615                         break;
13616                     case "southwest":
13617                         h = oh * (w/ow);
13618                         h = Math.min(Math.max(mh, h), mxh);
13619                         var tw = w;
13620                         w = ow * (h/oh);
13621                         x += tw - w;
13622                         break;
13623                     case "west":
13624                         var th = h;
13625                         h = oh * (w/ow);
13626                         h = Math.min(Math.max(mh, h), mxh);
13627                         y += (th - h) / 2;
13628                         var tw = w;
13629                         w = ow * (h/oh);
13630                         x += tw - w;
13631                        break;
13632                     case "northwest":
13633                         var tw = w;
13634                         var th = h;
13635                         h = oh * (w/ow);
13636                         h = Math.min(Math.max(mh, h), mxh);
13637                         w = ow * (h/oh);
13638                         y += th - h;
13639                         x += tw - w;
13640                        break;
13641
13642                 }
13643             }
13644             if (pos == 'hdrag') {
13645                 w = ow;
13646             }
13647             this.proxy.setBounds(x, y, w, h);
13648             if(this.dynamic){
13649                 this.resizeElement();
13650             }
13651             }catch(e){}
13652         }
13653     },
13654
13655     // private
13656     handleOver : function(){
13657         if(this.enabled){
13658             this.el.addClass("x-resizable-over");
13659         }
13660     },
13661
13662     // private
13663     handleOut : function(){
13664         if(!this.resizing){
13665             this.el.removeClass("x-resizable-over");
13666         }
13667     },
13668
13669     /**
13670      * Returns the element this component is bound to.
13671      * @return {Roo.Element}
13672      */
13673     getEl : function(){
13674         return this.el;
13675     },
13676
13677     /**
13678      * Returns the resizeChild element (or null).
13679      * @return {Roo.Element}
13680      */
13681     getResizeChild : function(){
13682         return this.resizeChild;
13683     },
13684
13685     /**
13686      * Destroys this resizable. If the element was wrapped and
13687      * removeEl is not true then the element remains.
13688      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13689      */
13690     destroy : function(removeEl){
13691         this.proxy.remove();
13692         if(this.overlay){
13693             this.overlay.removeAllListeners();
13694             this.overlay.remove();
13695         }
13696         var ps = Roo.Resizable.positions;
13697         for(var k in ps){
13698             if(typeof ps[k] != "function" && this[ps[k]]){
13699                 var h = this[ps[k]];
13700                 h.el.removeAllListeners();
13701                 h.el.remove();
13702             }
13703         }
13704         if(removeEl){
13705             this.el.update("");
13706             this.el.remove();
13707         }
13708     }
13709 });
13710
13711 // private
13712 // hash to map config positions to true positions
13713 Roo.Resizable.positions = {
13714     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13715     hd: "hdrag"
13716 };
13717
13718 // private
13719 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13720     if(!this.tpl){
13721         // only initialize the template if resizable is used
13722         var tpl = Roo.DomHelper.createTemplate(
13723             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13724         );
13725         tpl.compile();
13726         Roo.Resizable.Handle.prototype.tpl = tpl;
13727     }
13728     this.position = pos;
13729     this.rz = rz;
13730     // show north drag fro topdra
13731     var handlepos = pos == 'hdrag' ? 'north' : pos;
13732     
13733     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13734     if (pos == 'hdrag') {
13735         this.el.setStyle('cursor', 'pointer');
13736     }
13737     this.el.unselectable();
13738     if(transparent){
13739         this.el.setOpacity(0);
13740     }
13741     this.el.on("mousedown", this.onMouseDown, this);
13742     if(!disableTrackOver){
13743         this.el.on("mouseover", this.onMouseOver, this);
13744         this.el.on("mouseout", this.onMouseOut, this);
13745     }
13746 };
13747
13748 // private
13749 Roo.Resizable.Handle.prototype = {
13750     afterResize : function(rz){
13751         // do nothing
13752     },
13753     // private
13754     onMouseDown : function(e){
13755         this.rz.onMouseDown(this, e);
13756     },
13757     // private
13758     onMouseOver : function(e){
13759         this.rz.handleOver(this, e);
13760     },
13761     // private
13762     onMouseOut : function(e){
13763         this.rz.handleOut(this, e);
13764     }
13765 };/*
13766  * Based on:
13767  * Ext JS Library 1.1.1
13768  * Copyright(c) 2006-2007, Ext JS, LLC.
13769  *
13770  * Originally Released Under LGPL - original licence link has changed is not relivant.
13771  *
13772  * Fork - LGPL
13773  * <script type="text/javascript">
13774  */
13775
13776 /**
13777  * @class Roo.Editor
13778  * @extends Roo.Component
13779  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13780  * @constructor
13781  * Create a new Editor
13782  * @param {Roo.form.Field} field The Field object (or descendant)
13783  * @param {Object} config The config object
13784  */
13785 Roo.Editor = function(field, config){
13786     Roo.Editor.superclass.constructor.call(this, config);
13787     this.field = field;
13788     this.addEvents({
13789         /**
13790              * @event beforestartedit
13791              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13792              * false from the handler of this event.
13793              * @param {Editor} this
13794              * @param {Roo.Element} boundEl The underlying element bound to this editor
13795              * @param {Mixed} value The field value being set
13796              */
13797         "beforestartedit" : true,
13798         /**
13799              * @event startedit
13800              * Fires when this editor is displayed
13801              * @param {Roo.Element} boundEl The underlying element bound to this editor
13802              * @param {Mixed} value The starting field value
13803              */
13804         "startedit" : true,
13805         /**
13806              * @event beforecomplete
13807              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13808              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13809              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13810              * event will not fire since no edit actually occurred.
13811              * @param {Editor} this
13812              * @param {Mixed} value The current field value
13813              * @param {Mixed} startValue The original field value
13814              */
13815         "beforecomplete" : true,
13816         /**
13817              * @event complete
13818              * Fires after editing is complete and any changed value has been written to the underlying field.
13819              * @param {Editor} this
13820              * @param {Mixed} value The current field value
13821              * @param {Mixed} startValue The original field value
13822              */
13823         "complete" : true,
13824         /**
13825          * @event specialkey
13826          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13827          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13828          * @param {Roo.form.Field} this
13829          * @param {Roo.EventObject} e The event object
13830          */
13831         "specialkey" : true
13832     });
13833 };
13834
13835 Roo.extend(Roo.Editor, Roo.Component, {
13836     /**
13837      * @cfg {Boolean/String} autosize
13838      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13839      * or "height" to adopt the height only (defaults to false)
13840      */
13841     /**
13842      * @cfg {Boolean} revertInvalid
13843      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13844      * validation fails (defaults to true)
13845      */
13846     /**
13847      * @cfg {Boolean} ignoreNoChange
13848      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13849      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13850      * will never be ignored.
13851      */
13852     /**
13853      * @cfg {Boolean} hideEl
13854      * False to keep the bound element visible while the editor is displayed (defaults to true)
13855      */
13856     /**
13857      * @cfg {Mixed} value
13858      * The data value of the underlying field (defaults to "")
13859      */
13860     value : "",
13861     /**
13862      * @cfg {String} alignment
13863      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13864      */
13865     alignment: "c-c?",
13866     /**
13867      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13868      * for bottom-right shadow (defaults to "frame")
13869      */
13870     shadow : "frame",
13871     /**
13872      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13873      */
13874     constrain : false,
13875     /**
13876      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13877      */
13878     completeOnEnter : false,
13879     /**
13880      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13881      */
13882     cancelOnEsc : false,
13883     /**
13884      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13885      */
13886     updateEl : false,
13887
13888     // private
13889     onRender : function(ct, position){
13890         this.el = new Roo.Layer({
13891             shadow: this.shadow,
13892             cls: "x-editor",
13893             parentEl : ct,
13894             shim : this.shim,
13895             shadowOffset:4,
13896             id: this.id,
13897             constrain: this.constrain
13898         });
13899         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13900         if(this.field.msgTarget != 'title'){
13901             this.field.msgTarget = 'qtip';
13902         }
13903         this.field.render(this.el);
13904         if(Roo.isGecko){
13905             this.field.el.dom.setAttribute('autocomplete', 'off');
13906         }
13907         this.field.on("specialkey", this.onSpecialKey, this);
13908         if(this.swallowKeys){
13909             this.field.el.swallowEvent(['keydown','keypress']);
13910         }
13911         this.field.show();
13912         this.field.on("blur", this.onBlur, this);
13913         if(this.field.grow){
13914             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13915         }
13916     },
13917
13918     onSpecialKey : function(field, e){
13919         //Roo.log('editor onSpecialKey');
13920         if(this.completeOnEnter && e.getKey() == e.ENTER){
13921             e.stopEvent();
13922             this.completeEdit();
13923         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13924             this.cancelEdit();
13925         }else{
13926             this.fireEvent('specialkey', field, e);
13927         }
13928     },
13929
13930     /**
13931      * Starts the editing process and shows the editor.
13932      * @param {String/HTMLElement/Element} el The element to edit
13933      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13934       * to the innerHTML of el.
13935      */
13936     startEdit : function(el, value){
13937         if(this.editing){
13938             this.completeEdit();
13939         }
13940         this.boundEl = Roo.get(el);
13941         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13942         if(!this.rendered){
13943             this.render(this.parentEl || document.body);
13944         }
13945         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13946             return;
13947         }
13948         this.startValue = v;
13949         this.field.setValue(v);
13950         if(this.autoSize){
13951             var sz = this.boundEl.getSize();
13952             switch(this.autoSize){
13953                 case "width":
13954                 this.setSize(sz.width,  "");
13955                 break;
13956                 case "height":
13957                 this.setSize("",  sz.height);
13958                 break;
13959                 default:
13960                 this.setSize(sz.width,  sz.height);
13961             }
13962         }
13963         this.el.alignTo(this.boundEl, this.alignment);
13964         this.editing = true;
13965         if(Roo.QuickTips){
13966             Roo.QuickTips.disable();
13967         }
13968         this.show();
13969     },
13970
13971     /**
13972      * Sets the height and width of this editor.
13973      * @param {Number} width The new width
13974      * @param {Number} height The new height
13975      */
13976     setSize : function(w, h){
13977         this.field.setSize(w, h);
13978         if(this.el){
13979             this.el.sync();
13980         }
13981     },
13982
13983     /**
13984      * Realigns the editor to the bound field based on the current alignment config value.
13985      */
13986     realign : function(){
13987         this.el.alignTo(this.boundEl, this.alignment);
13988     },
13989
13990     /**
13991      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13992      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13993      */
13994     completeEdit : function(remainVisible){
13995         if(!this.editing){
13996             return;
13997         }
13998         var v = this.getValue();
13999         if(this.revertInvalid !== false && !this.field.isValid()){
14000             v = this.startValue;
14001             this.cancelEdit(true);
14002         }
14003         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14004             this.editing = false;
14005             this.hide();
14006             return;
14007         }
14008         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14009             this.editing = false;
14010             if(this.updateEl && this.boundEl){
14011                 this.boundEl.update(v);
14012             }
14013             if(remainVisible !== true){
14014                 this.hide();
14015             }
14016             this.fireEvent("complete", this, v, this.startValue);
14017         }
14018     },
14019
14020     // private
14021     onShow : function(){
14022         this.el.show();
14023         if(this.hideEl !== false){
14024             this.boundEl.hide();
14025         }
14026         this.field.show();
14027         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14028             this.fixIEFocus = true;
14029             this.deferredFocus.defer(50, this);
14030         }else{
14031             this.field.focus();
14032         }
14033         this.fireEvent("startedit", this.boundEl, this.startValue);
14034     },
14035
14036     deferredFocus : function(){
14037         if(this.editing){
14038             this.field.focus();
14039         }
14040     },
14041
14042     /**
14043      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14044      * reverted to the original starting value.
14045      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14046      * cancel (defaults to false)
14047      */
14048     cancelEdit : function(remainVisible){
14049         if(this.editing){
14050             this.setValue(this.startValue);
14051             if(remainVisible !== true){
14052                 this.hide();
14053             }
14054         }
14055     },
14056
14057     // private
14058     onBlur : function(){
14059         if(this.allowBlur !== true && this.editing){
14060             this.completeEdit();
14061         }
14062     },
14063
14064     // private
14065     onHide : function(){
14066         if(this.editing){
14067             this.completeEdit();
14068             return;
14069         }
14070         this.field.blur();
14071         if(this.field.collapse){
14072             this.field.collapse();
14073         }
14074         this.el.hide();
14075         if(this.hideEl !== false){
14076             this.boundEl.show();
14077         }
14078         if(Roo.QuickTips){
14079             Roo.QuickTips.enable();
14080         }
14081     },
14082
14083     /**
14084      * Sets the data value of the editor
14085      * @param {Mixed} value Any valid value supported by the underlying field
14086      */
14087     setValue : function(v){
14088         this.field.setValue(v);
14089     },
14090
14091     /**
14092      * Gets the data value of the editor
14093      * @return {Mixed} The data value
14094      */
14095     getValue : function(){
14096         return this.field.getValue();
14097     }
14098 });/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108  
14109 /**
14110  * @class Roo.BasicDialog
14111  * @extends Roo.util.Observable
14112  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14113  * <pre><code>
14114 var dlg = new Roo.BasicDialog("my-dlg", {
14115     height: 200,
14116     width: 300,
14117     minHeight: 100,
14118     minWidth: 150,
14119     modal: true,
14120     proxyDrag: true,
14121     shadow: true
14122 });
14123 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14124 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14125 dlg.addButton('Cancel', dlg.hide, dlg);
14126 dlg.show();
14127 </code></pre>
14128   <b>A Dialog should always be a direct child of the body element.</b>
14129  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14130  * @cfg {String} title Default text to display in the title bar (defaults to null)
14131  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14132  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14133  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14134  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14135  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14136  * (defaults to null with no animation)
14137  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14138  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14139  * property for valid values (defaults to 'all')
14140  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14141  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14142  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14143  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14144  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14145  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14146  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14147  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14148  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14149  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14150  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14151  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14152  * draggable = true (defaults to false)
14153  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14154  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14155  * shadow (defaults to false)
14156  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14157  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14158  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14159  * @cfg {Array} buttons Array of buttons
14160  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14161  * @constructor
14162  * Create a new BasicDialog.
14163  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14164  * @param {Object} config Configuration options
14165  */
14166 Roo.BasicDialog = function(el, config){
14167     this.el = Roo.get(el);
14168     var dh = Roo.DomHelper;
14169     if(!this.el && config && config.autoCreate){
14170         if(typeof config.autoCreate == "object"){
14171             if(!config.autoCreate.id){
14172                 config.autoCreate.id = el;
14173             }
14174             this.el = dh.append(document.body,
14175                         config.autoCreate, true);
14176         }else{
14177             this.el = dh.append(document.body,
14178                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14179         }
14180     }
14181     el = this.el;
14182     el.setDisplayed(true);
14183     el.hide = this.hideAction;
14184     this.id = el.id;
14185     el.addClass("x-dlg");
14186
14187     Roo.apply(this, config);
14188
14189     this.proxy = el.createProxy("x-dlg-proxy");
14190     this.proxy.hide = this.hideAction;
14191     this.proxy.setOpacity(.5);
14192     this.proxy.hide();
14193
14194     if(config.width){
14195         el.setWidth(config.width);
14196     }
14197     if(config.height){
14198         el.setHeight(config.height);
14199     }
14200     this.size = el.getSize();
14201     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14202         this.xy = [config.x,config.y];
14203     }else{
14204         this.xy = el.getCenterXY(true);
14205     }
14206     /** The header element @type Roo.Element */
14207     this.header = el.child("> .x-dlg-hd");
14208     /** The body element @type Roo.Element */
14209     this.body = el.child("> .x-dlg-bd");
14210     /** The footer element @type Roo.Element */
14211     this.footer = el.child("> .x-dlg-ft");
14212
14213     if(!this.header){
14214         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14215     }
14216     if(!this.body){
14217         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14218     }
14219
14220     this.header.unselectable();
14221     if(this.title){
14222         this.header.update(this.title);
14223     }
14224     // this element allows the dialog to be focused for keyboard event
14225     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14226     this.focusEl.swallowEvent("click", true);
14227
14228     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14229
14230     // wrap the body and footer for special rendering
14231     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14232     if(this.footer){
14233         this.bwrap.dom.appendChild(this.footer.dom);
14234     }
14235
14236     this.bg = this.el.createChild({
14237         tag: "div", cls:"x-dlg-bg",
14238         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14239     });
14240     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14241
14242
14243     if(this.autoScroll !== false && !this.autoTabs){
14244         this.body.setStyle("overflow", "auto");
14245     }
14246
14247     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14248
14249     if(this.closable !== false){
14250         this.el.addClass("x-dlg-closable");
14251         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14252         this.close.on("click", this.closeClick, this);
14253         this.close.addClassOnOver("x-dlg-close-over");
14254     }
14255     if(this.collapsible !== false){
14256         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14257         this.collapseBtn.on("click", this.collapseClick, this);
14258         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14259         this.header.on("dblclick", this.collapseClick, this);
14260     }
14261     if(this.resizable !== false){
14262         this.el.addClass("x-dlg-resizable");
14263         this.resizer = new Roo.Resizable(el, {
14264             minWidth: this.minWidth || 80,
14265             minHeight:this.minHeight || 80,
14266             handles: this.resizeHandles || "all",
14267             pinned: true
14268         });
14269         this.resizer.on("beforeresize", this.beforeResize, this);
14270         this.resizer.on("resize", this.onResize, this);
14271     }
14272     if(this.draggable !== false){
14273         el.addClass("x-dlg-draggable");
14274         if (!this.proxyDrag) {
14275             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14276         }
14277         else {
14278             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14279         }
14280         dd.setHandleElId(this.header.id);
14281         dd.endDrag = this.endMove.createDelegate(this);
14282         dd.startDrag = this.startMove.createDelegate(this);
14283         dd.onDrag = this.onDrag.createDelegate(this);
14284         dd.scroll = false;
14285         this.dd = dd;
14286     }
14287     if(this.modal){
14288         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14289         this.mask.enableDisplayMode("block");
14290         this.mask.hide();
14291         this.el.addClass("x-dlg-modal");
14292     }
14293     if(this.shadow){
14294         this.shadow = new Roo.Shadow({
14295             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14296             offset : this.shadowOffset
14297         });
14298     }else{
14299         this.shadowOffset = 0;
14300     }
14301     if(Roo.useShims && this.shim !== false){
14302         this.shim = this.el.createShim();
14303         this.shim.hide = this.hideAction;
14304         this.shim.hide();
14305     }else{
14306         this.shim = false;
14307     }
14308     if(this.autoTabs){
14309         this.initTabs();
14310     }
14311     if (this.buttons) { 
14312         var bts= this.buttons;
14313         this.buttons = [];
14314         Roo.each(bts, function(b) {
14315             this.addButton(b);
14316         }, this);
14317     }
14318     
14319     
14320     this.addEvents({
14321         /**
14322          * @event keydown
14323          * Fires when a key is pressed
14324          * @param {Roo.BasicDialog} this
14325          * @param {Roo.EventObject} e
14326          */
14327         "keydown" : true,
14328         /**
14329          * @event move
14330          * Fires when this dialog is moved by the user.
14331          * @param {Roo.BasicDialog} this
14332          * @param {Number} x The new page X
14333          * @param {Number} y The new page Y
14334          */
14335         "move" : true,
14336         /**
14337          * @event resize
14338          * Fires when this dialog is resized by the user.
14339          * @param {Roo.BasicDialog} this
14340          * @param {Number} width The new width
14341          * @param {Number} height The new height
14342          */
14343         "resize" : true,
14344         /**
14345          * @event beforehide
14346          * Fires before this dialog is hidden.
14347          * @param {Roo.BasicDialog} this
14348          */
14349         "beforehide" : true,
14350         /**
14351          * @event hide
14352          * Fires when this dialog is hidden.
14353          * @param {Roo.BasicDialog} this
14354          */
14355         "hide" : true,
14356         /**
14357          * @event beforeshow
14358          * Fires before this dialog is shown.
14359          * @param {Roo.BasicDialog} this
14360          */
14361         "beforeshow" : true,
14362         /**
14363          * @event show
14364          * Fires when this dialog is shown.
14365          * @param {Roo.BasicDialog} this
14366          */
14367         "show" : true
14368     });
14369     el.on("keydown", this.onKeyDown, this);
14370     el.on("mousedown", this.toFront, this);
14371     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14372     this.el.hide();
14373     Roo.DialogManager.register(this);
14374     Roo.BasicDialog.superclass.constructor.call(this);
14375 };
14376
14377 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14378     shadowOffset: Roo.isIE ? 6 : 5,
14379     minHeight: 80,
14380     minWidth: 200,
14381     minButtonWidth: 75,
14382     defaultButton: null,
14383     buttonAlign: "right",
14384     tabTag: 'div',
14385     firstShow: true,
14386
14387     /**
14388      * Sets the dialog title text
14389      * @param {String} text The title text to display
14390      * @return {Roo.BasicDialog} this
14391      */
14392     setTitle : function(text){
14393         this.header.update(text);
14394         return this;
14395     },
14396
14397     // private
14398     closeClick : function(){
14399         this.hide();
14400     },
14401
14402     // private
14403     collapseClick : function(){
14404         this[this.collapsed ? "expand" : "collapse"]();
14405     },
14406
14407     /**
14408      * Collapses the dialog to its minimized state (only the title bar is visible).
14409      * Equivalent to the user clicking the collapse dialog button.
14410      */
14411     collapse : function(){
14412         if(!this.collapsed){
14413             this.collapsed = true;
14414             this.el.addClass("x-dlg-collapsed");
14415             this.restoreHeight = this.el.getHeight();
14416             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14417         }
14418     },
14419
14420     /**
14421      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14422      * clicking the expand dialog button.
14423      */
14424     expand : function(){
14425         if(this.collapsed){
14426             this.collapsed = false;
14427             this.el.removeClass("x-dlg-collapsed");
14428             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14429         }
14430     },
14431
14432     /**
14433      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14434      * @return {Roo.TabPanel} The tabs component
14435      */
14436     initTabs : function(){
14437         var tabs = this.getTabs();
14438         while(tabs.getTab(0)){
14439             tabs.removeTab(0);
14440         }
14441         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14442             var dom = el.dom;
14443             tabs.addTab(Roo.id(dom), dom.title);
14444             dom.title = "";
14445         });
14446         tabs.activate(0);
14447         return tabs;
14448     },
14449
14450     // private
14451     beforeResize : function(){
14452         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14453     },
14454
14455     // private
14456     onResize : function(){
14457         this.refreshSize();
14458         this.syncBodyHeight();
14459         this.adjustAssets();
14460         this.focus();
14461         this.fireEvent("resize", this, this.size.width, this.size.height);
14462     },
14463
14464     // private
14465     onKeyDown : function(e){
14466         if(this.isVisible()){
14467             this.fireEvent("keydown", this, e);
14468         }
14469     },
14470
14471     /**
14472      * Resizes the dialog.
14473      * @param {Number} width
14474      * @param {Number} height
14475      * @return {Roo.BasicDialog} this
14476      */
14477     resizeTo : function(width, height){
14478         this.el.setSize(width, height);
14479         this.size = {width: width, height: height};
14480         this.syncBodyHeight();
14481         if(this.fixedcenter){
14482             this.center();
14483         }
14484         if(this.isVisible()){
14485             this.constrainXY();
14486             this.adjustAssets();
14487         }
14488         this.fireEvent("resize", this, width, height);
14489         return this;
14490     },
14491
14492
14493     /**
14494      * Resizes the dialog to fit the specified content size.
14495      * @param {Number} width
14496      * @param {Number} height
14497      * @return {Roo.BasicDialog} this
14498      */
14499     setContentSize : function(w, h){
14500         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14501         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14502         //if(!this.el.isBorderBox()){
14503             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14504             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14505         //}
14506         if(this.tabs){
14507             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14508             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14509         }
14510         this.resizeTo(w, h);
14511         return this;
14512     },
14513
14514     /**
14515      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14516      * executed in response to a particular key being pressed while the dialog is active.
14517      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14518      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14519      * @param {Function} fn The function to call
14520      * @param {Object} scope (optional) The scope of the function
14521      * @return {Roo.BasicDialog} this
14522      */
14523     addKeyListener : function(key, fn, scope){
14524         var keyCode, shift, ctrl, alt;
14525         if(typeof key == "object" && !(key instanceof Array)){
14526             keyCode = key["key"];
14527             shift = key["shift"];
14528             ctrl = key["ctrl"];
14529             alt = key["alt"];
14530         }else{
14531             keyCode = key;
14532         }
14533         var handler = function(dlg, e){
14534             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14535                 var k = e.getKey();
14536                 if(keyCode instanceof Array){
14537                     for(var i = 0, len = keyCode.length; i < len; i++){
14538                         if(keyCode[i] == k){
14539                           fn.call(scope || window, dlg, k, e);
14540                           return;
14541                         }
14542                     }
14543                 }else{
14544                     if(k == keyCode){
14545                         fn.call(scope || window, dlg, k, e);
14546                     }
14547                 }
14548             }
14549         };
14550         this.on("keydown", handler);
14551         return this;
14552     },
14553
14554     /**
14555      * Returns the TabPanel component (creates it if it doesn't exist).
14556      * Note: If you wish to simply check for the existence of tabs without creating them,
14557      * check for a null 'tabs' property.
14558      * @return {Roo.TabPanel} The tabs component
14559      */
14560     getTabs : function(){
14561         if(!this.tabs){
14562             this.el.addClass("x-dlg-auto-tabs");
14563             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14564             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14565         }
14566         return this.tabs;
14567     },
14568
14569     /**
14570      * Adds a button to the footer section of the dialog.
14571      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14572      * object or a valid Roo.DomHelper element config
14573      * @param {Function} handler The function called when the button is clicked
14574      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14575      * @return {Roo.Button} The new button
14576      */
14577     addButton : function(config, handler, scope){
14578         var dh = Roo.DomHelper;
14579         if(!this.footer){
14580             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14581         }
14582         if(!this.btnContainer){
14583             var tb = this.footer.createChild({
14584
14585                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14586                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14587             }, null, true);
14588             this.btnContainer = tb.firstChild.firstChild.firstChild;
14589         }
14590         var bconfig = {
14591             handler: handler,
14592             scope: scope,
14593             minWidth: this.minButtonWidth,
14594             hideParent:true
14595         };
14596         if(typeof config == "string"){
14597             bconfig.text = config;
14598         }else{
14599             if(config.tag){
14600                 bconfig.dhconfig = config;
14601             }else{
14602                 Roo.apply(bconfig, config);
14603             }
14604         }
14605         var fc = false;
14606         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14607             bconfig.position = Math.max(0, bconfig.position);
14608             fc = this.btnContainer.childNodes[bconfig.position];
14609         }
14610          
14611         var btn = new Roo.Button(
14612             fc ? 
14613                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14614                 : this.btnContainer.appendChild(document.createElement("td")),
14615             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14616             bconfig
14617         );
14618         this.syncBodyHeight();
14619         if(!this.buttons){
14620             /**
14621              * Array of all the buttons that have been added to this dialog via addButton
14622              * @type Array
14623              */
14624             this.buttons = [];
14625         }
14626         this.buttons.push(btn);
14627         return btn;
14628     },
14629
14630     /**
14631      * Sets the default button to be focused when the dialog is displayed.
14632      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14633      * @return {Roo.BasicDialog} this
14634      */
14635     setDefaultButton : function(btn){
14636         this.defaultButton = btn;
14637         return this;
14638     },
14639
14640     // private
14641     getHeaderFooterHeight : function(safe){
14642         var height = 0;
14643         if(this.header){
14644            height += this.header.getHeight();
14645         }
14646         if(this.footer){
14647            var fm = this.footer.getMargins();
14648             height += (this.footer.getHeight()+fm.top+fm.bottom);
14649         }
14650         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14651         height += this.centerBg.getPadding("tb");
14652         return height;
14653     },
14654
14655     // private
14656     syncBodyHeight : function(){
14657         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14658         var height = this.size.height - this.getHeaderFooterHeight(false);
14659         bd.setHeight(height-bd.getMargins("tb"));
14660         var hh = this.header.getHeight();
14661         var h = this.size.height-hh;
14662         cb.setHeight(h);
14663         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14664         bw.setHeight(h-cb.getPadding("tb"));
14665         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14666         bd.setWidth(bw.getWidth(true));
14667         if(this.tabs){
14668             this.tabs.syncHeight();
14669             if(Roo.isIE){
14670                 this.tabs.el.repaint();
14671             }
14672         }
14673     },
14674
14675     /**
14676      * Restores the previous state of the dialog if Roo.state is configured.
14677      * @return {Roo.BasicDialog} this
14678      */
14679     restoreState : function(){
14680         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14681         if(box && box.width){
14682             this.xy = [box.x, box.y];
14683             this.resizeTo(box.width, box.height);
14684         }
14685         return this;
14686     },
14687
14688     // private
14689     beforeShow : function(){
14690         this.expand();
14691         if(this.fixedcenter){
14692             this.xy = this.el.getCenterXY(true);
14693         }
14694         if(this.modal){
14695             Roo.get(document.body).addClass("x-body-masked");
14696             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14697             this.mask.show();
14698         }
14699         this.constrainXY();
14700     },
14701
14702     // private
14703     animShow : function(){
14704         var b = Roo.get(this.animateTarget).getBox();
14705         this.proxy.setSize(b.width, b.height);
14706         this.proxy.setLocation(b.x, b.y);
14707         this.proxy.show();
14708         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14709                     true, .35, this.showEl.createDelegate(this));
14710     },
14711
14712     /**
14713      * Shows the dialog.
14714      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14715      * @return {Roo.BasicDialog} this
14716      */
14717     show : function(animateTarget){
14718         if (this.fireEvent("beforeshow", this) === false){
14719             return;
14720         }
14721         if(this.syncHeightBeforeShow){
14722             this.syncBodyHeight();
14723         }else if(this.firstShow){
14724             this.firstShow = false;
14725             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14726         }
14727         this.animateTarget = animateTarget || this.animateTarget;
14728         if(!this.el.isVisible()){
14729             this.beforeShow();
14730             if(this.animateTarget && Roo.get(this.animateTarget)){
14731                 this.animShow();
14732             }else{
14733                 this.showEl();
14734             }
14735         }
14736         return this;
14737     },
14738
14739     // private
14740     showEl : function(){
14741         this.proxy.hide();
14742         this.el.setXY(this.xy);
14743         this.el.show();
14744         this.adjustAssets(true);
14745         this.toFront();
14746         this.focus();
14747         // IE peekaboo bug - fix found by Dave Fenwick
14748         if(Roo.isIE){
14749             this.el.repaint();
14750         }
14751         this.fireEvent("show", this);
14752     },
14753
14754     /**
14755      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14756      * dialog itself will receive focus.
14757      */
14758     focus : function(){
14759         if(this.defaultButton){
14760             this.defaultButton.focus();
14761         }else{
14762             this.focusEl.focus();
14763         }
14764     },
14765
14766     // private
14767     constrainXY : function(){
14768         if(this.constraintoviewport !== false){
14769             if(!this.viewSize){
14770                 if(this.container){
14771                     var s = this.container.getSize();
14772                     this.viewSize = [s.width, s.height];
14773                 }else{
14774                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14775                 }
14776             }
14777             var s = Roo.get(this.container||document).getScroll();
14778
14779             var x = this.xy[0], y = this.xy[1];
14780             var w = this.size.width, h = this.size.height;
14781             var vw = this.viewSize[0], vh = this.viewSize[1];
14782             // only move it if it needs it
14783             var moved = false;
14784             // first validate right/bottom
14785             if(x + w > vw+s.left){
14786                 x = vw - w;
14787                 moved = true;
14788             }
14789             if(y + h > vh+s.top){
14790                 y = vh - h;
14791                 moved = true;
14792             }
14793             // then make sure top/left isn't negative
14794             if(x < s.left){
14795                 x = s.left;
14796                 moved = true;
14797             }
14798             if(y < s.top){
14799                 y = s.top;
14800                 moved = true;
14801             }
14802             if(moved){
14803                 // cache xy
14804                 this.xy = [x, y];
14805                 if(this.isVisible()){
14806                     this.el.setLocation(x, y);
14807                     this.adjustAssets();
14808                 }
14809             }
14810         }
14811     },
14812
14813     // private
14814     onDrag : function(){
14815         if(!this.proxyDrag){
14816             this.xy = this.el.getXY();
14817             this.adjustAssets();
14818         }
14819     },
14820
14821     // private
14822     adjustAssets : function(doShow){
14823         var x = this.xy[0], y = this.xy[1];
14824         var w = this.size.width, h = this.size.height;
14825         if(doShow === true){
14826             if(this.shadow){
14827                 this.shadow.show(this.el);
14828             }
14829             if(this.shim){
14830                 this.shim.show();
14831             }
14832         }
14833         if(this.shadow && this.shadow.isVisible()){
14834             this.shadow.show(this.el);
14835         }
14836         if(this.shim && this.shim.isVisible()){
14837             this.shim.setBounds(x, y, w, h);
14838         }
14839     },
14840
14841     // private
14842     adjustViewport : function(w, h){
14843         if(!w || !h){
14844             w = Roo.lib.Dom.getViewWidth();
14845             h = Roo.lib.Dom.getViewHeight();
14846         }
14847         // cache the size
14848         this.viewSize = [w, h];
14849         if(this.modal && this.mask.isVisible()){
14850             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14851             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14852         }
14853         if(this.isVisible()){
14854             this.constrainXY();
14855         }
14856     },
14857
14858     /**
14859      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14860      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14861      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14862      */
14863     destroy : function(removeEl){
14864         if(this.isVisible()){
14865             this.animateTarget = null;
14866             this.hide();
14867         }
14868         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14869         if(this.tabs){
14870             this.tabs.destroy(removeEl);
14871         }
14872         Roo.destroy(
14873              this.shim,
14874              this.proxy,
14875              this.resizer,
14876              this.close,
14877              this.mask
14878         );
14879         if(this.dd){
14880             this.dd.unreg();
14881         }
14882         if(this.buttons){
14883            for(var i = 0, len = this.buttons.length; i < len; i++){
14884                this.buttons[i].destroy();
14885            }
14886         }
14887         this.el.removeAllListeners();
14888         if(removeEl === true){
14889             this.el.update("");
14890             this.el.remove();
14891         }
14892         Roo.DialogManager.unregister(this);
14893     },
14894
14895     // private
14896     startMove : function(){
14897         if(this.proxyDrag){
14898             this.proxy.show();
14899         }
14900         if(this.constraintoviewport !== false){
14901             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14902         }
14903     },
14904
14905     // private
14906     endMove : function(){
14907         if(!this.proxyDrag){
14908             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14909         }else{
14910             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14911             this.proxy.hide();
14912         }
14913         this.refreshSize();
14914         this.adjustAssets();
14915         this.focus();
14916         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14917     },
14918
14919     /**
14920      * Brings this dialog to the front of any other visible dialogs
14921      * @return {Roo.BasicDialog} this
14922      */
14923     toFront : function(){
14924         Roo.DialogManager.bringToFront(this);
14925         return this;
14926     },
14927
14928     /**
14929      * Sends this dialog to the back (under) of any other visible dialogs
14930      * @return {Roo.BasicDialog} this
14931      */
14932     toBack : function(){
14933         Roo.DialogManager.sendToBack(this);
14934         return this;
14935     },
14936
14937     /**
14938      * Centers this dialog in the viewport
14939      * @return {Roo.BasicDialog} this
14940      */
14941     center : function(){
14942         var xy = this.el.getCenterXY(true);
14943         this.moveTo(xy[0], xy[1]);
14944         return this;
14945     },
14946
14947     /**
14948      * Moves the dialog's top-left corner to the specified point
14949      * @param {Number} x
14950      * @param {Number} y
14951      * @return {Roo.BasicDialog} this
14952      */
14953     moveTo : function(x, y){
14954         this.xy = [x,y];
14955         if(this.isVisible()){
14956             this.el.setXY(this.xy);
14957             this.adjustAssets();
14958         }
14959         return this;
14960     },
14961
14962     /**
14963      * Aligns the dialog to the specified element
14964      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14965      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14966      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14967      * @return {Roo.BasicDialog} this
14968      */
14969     alignTo : function(element, position, offsets){
14970         this.xy = this.el.getAlignToXY(element, position, offsets);
14971         if(this.isVisible()){
14972             this.el.setXY(this.xy);
14973             this.adjustAssets();
14974         }
14975         return this;
14976     },
14977
14978     /**
14979      * Anchors an element to another element and realigns it when the window is resized.
14980      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14981      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14982      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14983      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14984      * is a number, it is used as the buffer delay (defaults to 50ms).
14985      * @return {Roo.BasicDialog} this
14986      */
14987     anchorTo : function(el, alignment, offsets, monitorScroll){
14988         var action = function(){
14989             this.alignTo(el, alignment, offsets);
14990         };
14991         Roo.EventManager.onWindowResize(action, this);
14992         var tm = typeof monitorScroll;
14993         if(tm != 'undefined'){
14994             Roo.EventManager.on(window, 'scroll', action, this,
14995                 {buffer: tm == 'number' ? monitorScroll : 50});
14996         }
14997         action.call(this);
14998         return this;
14999     },
15000
15001     /**
15002      * Returns true if the dialog is visible
15003      * @return {Boolean}
15004      */
15005     isVisible : function(){
15006         return this.el.isVisible();
15007     },
15008
15009     // private
15010     animHide : function(callback){
15011         var b = Roo.get(this.animateTarget).getBox();
15012         this.proxy.show();
15013         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15014         this.el.hide();
15015         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15016                     this.hideEl.createDelegate(this, [callback]));
15017     },
15018
15019     /**
15020      * Hides the dialog.
15021      * @param {Function} callback (optional) Function to call when the dialog is hidden
15022      * @return {Roo.BasicDialog} this
15023      */
15024     hide : function(callback){
15025         if (this.fireEvent("beforehide", this) === false){
15026             return;
15027         }
15028         if(this.shadow){
15029             this.shadow.hide();
15030         }
15031         if(this.shim) {
15032           this.shim.hide();
15033         }
15034         // sometimes animateTarget seems to get set.. causing problems...
15035         // this just double checks..
15036         if(this.animateTarget && Roo.get(this.animateTarget)) {
15037            this.animHide(callback);
15038         }else{
15039             this.el.hide();
15040             this.hideEl(callback);
15041         }
15042         return this;
15043     },
15044
15045     // private
15046     hideEl : function(callback){
15047         this.proxy.hide();
15048         if(this.modal){
15049             this.mask.hide();
15050             Roo.get(document.body).removeClass("x-body-masked");
15051         }
15052         this.fireEvent("hide", this);
15053         if(typeof callback == "function"){
15054             callback();
15055         }
15056     },
15057
15058     // private
15059     hideAction : function(){
15060         this.setLeft("-10000px");
15061         this.setTop("-10000px");
15062         this.setStyle("visibility", "hidden");
15063     },
15064
15065     // private
15066     refreshSize : function(){
15067         this.size = this.el.getSize();
15068         this.xy = this.el.getXY();
15069         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15070     },
15071
15072     // private
15073     // z-index is managed by the DialogManager and may be overwritten at any time
15074     setZIndex : function(index){
15075         if(this.modal){
15076             this.mask.setStyle("z-index", index);
15077         }
15078         if(this.shim){
15079             this.shim.setStyle("z-index", ++index);
15080         }
15081         if(this.shadow){
15082             this.shadow.setZIndex(++index);
15083         }
15084         this.el.setStyle("z-index", ++index);
15085         if(this.proxy){
15086             this.proxy.setStyle("z-index", ++index);
15087         }
15088         if(this.resizer){
15089             this.resizer.proxy.setStyle("z-index", ++index);
15090         }
15091
15092         this.lastZIndex = index;
15093     },
15094
15095     /**
15096      * Returns the element for this dialog
15097      * @return {Roo.Element} The underlying dialog Element
15098      */
15099     getEl : function(){
15100         return this.el;
15101     }
15102 });
15103
15104 /**
15105  * @class Roo.DialogManager
15106  * Provides global access to BasicDialogs that have been created and
15107  * support for z-indexing (layering) multiple open dialogs.
15108  */
15109 Roo.DialogManager = function(){
15110     var list = {};
15111     var accessList = [];
15112     var front = null;
15113
15114     // private
15115     var sortDialogs = function(d1, d2){
15116         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15117     };
15118
15119     // private
15120     var orderDialogs = function(){
15121         accessList.sort(sortDialogs);
15122         var seed = Roo.DialogManager.zseed;
15123         for(var i = 0, len = accessList.length; i < len; i++){
15124             var dlg = accessList[i];
15125             if(dlg){
15126                 dlg.setZIndex(seed + (i*10));
15127             }
15128         }
15129     };
15130
15131     return {
15132         /**
15133          * The starting z-index for BasicDialogs (defaults to 9000)
15134          * @type Number The z-index value
15135          */
15136         zseed : 9000,
15137
15138         // private
15139         register : function(dlg){
15140             list[dlg.id] = dlg;
15141             accessList.push(dlg);
15142         },
15143
15144         // private
15145         unregister : function(dlg){
15146             delete list[dlg.id];
15147             var i=0;
15148             var len=0;
15149             if(!accessList.indexOf){
15150                 for(  i = 0, len = accessList.length; i < len; i++){
15151                     if(accessList[i] == dlg){
15152                         accessList.splice(i, 1);
15153                         return;
15154                     }
15155                 }
15156             }else{
15157                  i = accessList.indexOf(dlg);
15158                 if(i != -1){
15159                     accessList.splice(i, 1);
15160                 }
15161             }
15162         },
15163
15164         /**
15165          * Gets a registered dialog by id
15166          * @param {String/Object} id The id of the dialog or a dialog
15167          * @return {Roo.BasicDialog} this
15168          */
15169         get : function(id){
15170             return typeof id == "object" ? id : list[id];
15171         },
15172
15173         /**
15174          * Brings the specified dialog to the front
15175          * @param {String/Object} dlg The id of the dialog or a dialog
15176          * @return {Roo.BasicDialog} this
15177          */
15178         bringToFront : function(dlg){
15179             dlg = this.get(dlg);
15180             if(dlg != front){
15181                 front = dlg;
15182                 dlg._lastAccess = new Date().getTime();
15183                 orderDialogs();
15184             }
15185             return dlg;
15186         },
15187
15188         /**
15189          * Sends the specified dialog to the back
15190          * @param {String/Object} dlg The id of the dialog or a dialog
15191          * @return {Roo.BasicDialog} this
15192          */
15193         sendToBack : function(dlg){
15194             dlg = this.get(dlg);
15195             dlg._lastAccess = -(new Date().getTime());
15196             orderDialogs();
15197             return dlg;
15198         },
15199
15200         /**
15201          * Hides all dialogs
15202          */
15203         hideAll : function(){
15204             for(var id in list){
15205                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15206                     list[id].hide();
15207                 }
15208             }
15209         }
15210     };
15211 }();
15212
15213 /**
15214  * @class Roo.LayoutDialog
15215  * @extends Roo.BasicDialog
15216  * Dialog which provides adjustments for working with a layout in a Dialog.
15217  * Add your necessary layout config options to the dialog's config.<br>
15218  * Example usage (including a nested layout):
15219  * <pre><code>
15220 if(!dialog){
15221     dialog = new Roo.LayoutDialog("download-dlg", {
15222         modal: true,
15223         width:600,
15224         height:450,
15225         shadow:true,
15226         minWidth:500,
15227         minHeight:350,
15228         autoTabs:true,
15229         proxyDrag:true,
15230         // layout config merges with the dialog config
15231         center:{
15232             tabPosition: "top",
15233             alwaysShowTabs: true
15234         }
15235     });
15236     dialog.addKeyListener(27, dialog.hide, dialog);
15237     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15238     dialog.addButton("Build It!", this.getDownload, this);
15239
15240     // we can even add nested layouts
15241     var innerLayout = new Roo.BorderLayout("dl-inner", {
15242         east: {
15243             initialSize: 200,
15244             autoScroll:true,
15245             split:true
15246         },
15247         center: {
15248             autoScroll:true
15249         }
15250     });
15251     innerLayout.beginUpdate();
15252     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15253     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15254     innerLayout.endUpdate(true);
15255
15256     var layout = dialog.getLayout();
15257     layout.beginUpdate();
15258     layout.add("center", new Roo.ContentPanel("standard-panel",
15259                         {title: "Download the Source", fitToFrame:true}));
15260     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15261                {title: "Build your own roo.js"}));
15262     layout.getRegion("center").showPanel(sp);
15263     layout.endUpdate();
15264 }
15265 </code></pre>
15266     * @constructor
15267     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15268     * @param {Object} config configuration options
15269   */
15270 Roo.LayoutDialog = function(el, cfg){
15271     
15272     var config=  cfg;
15273     if (typeof(cfg) == 'undefined') {
15274         config = Roo.apply({}, el);
15275         // not sure why we use documentElement here.. - it should always be body.
15276         // IE7 borks horribly if we use documentElement.
15277         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15278         //config.autoCreate = true;
15279     }
15280     
15281     
15282     config.autoTabs = false;
15283     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15284     this.body.setStyle({overflow:"hidden", position:"relative"});
15285     this.layout = new Roo.BorderLayout(this.body.dom, config);
15286     this.layout.monitorWindowResize = false;
15287     this.el.addClass("x-dlg-auto-layout");
15288     // fix case when center region overwrites center function
15289     this.center = Roo.BasicDialog.prototype.center;
15290     this.on("show", this.layout.layout, this.layout, true);
15291     if (config.items) {
15292         var xitems = config.items;
15293         delete config.items;
15294         Roo.each(xitems, this.addxtype, this);
15295     }
15296     
15297     
15298 };
15299 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15300     /**
15301      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15302      * @deprecated
15303      */
15304     endUpdate : function(){
15305         this.layout.endUpdate();
15306     },
15307
15308     /**
15309      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15310      *  @deprecated
15311      */
15312     beginUpdate : function(){
15313         this.layout.beginUpdate();
15314     },
15315
15316     /**
15317      * Get the BorderLayout for this dialog
15318      * @return {Roo.BorderLayout}
15319      */
15320     getLayout : function(){
15321         return this.layout;
15322     },
15323
15324     showEl : function(){
15325         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15326         if(Roo.isIE7){
15327             this.layout.layout();
15328         }
15329     },
15330
15331     // private
15332     // Use the syncHeightBeforeShow config option to control this automatically
15333     syncBodyHeight : function(){
15334         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15335         if(this.layout){this.layout.layout();}
15336     },
15337     
15338       /**
15339      * Add an xtype element (actually adds to the layout.)
15340      * @return {Object} xdata xtype object data.
15341      */
15342     
15343     addxtype : function(c) {
15344         return this.layout.addxtype(c);
15345     }
15346 });/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356  
15357 /**
15358  * @class Roo.MessageBox
15359  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15360  * Example usage:
15361  *<pre><code>
15362 // Basic alert:
15363 Roo.Msg.alert('Status', 'Changes saved successfully.');
15364
15365 // Prompt for user data:
15366 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15367     if (btn == 'ok'){
15368         // process text value...
15369     }
15370 });
15371
15372 // Show a dialog using config options:
15373 Roo.Msg.show({
15374    title:'Save Changes?',
15375    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15376    buttons: Roo.Msg.YESNOCANCEL,
15377    fn: processResult,
15378    animEl: 'elId'
15379 });
15380 </code></pre>
15381  * @singleton
15382  */
15383 Roo.MessageBox = function(){
15384     var dlg, opt, mask, waitTimer;
15385     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15386     var buttons, activeTextEl, bwidth;
15387
15388     // private
15389     var handleButton = function(button){
15390         dlg.hide();
15391         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15392     };
15393
15394     // private
15395     var handleHide = function(){
15396         if(opt && opt.cls){
15397             dlg.el.removeClass(opt.cls);
15398         }
15399         if(waitTimer){
15400             Roo.TaskMgr.stop(waitTimer);
15401             waitTimer = null;
15402         }
15403     };
15404
15405     // private
15406     var updateButtons = function(b){
15407         var width = 0;
15408         if(!b){
15409             buttons["ok"].hide();
15410             buttons["cancel"].hide();
15411             buttons["yes"].hide();
15412             buttons["no"].hide();
15413             dlg.footer.dom.style.display = 'none';
15414             return width;
15415         }
15416         dlg.footer.dom.style.display = '';
15417         for(var k in buttons){
15418             if(typeof buttons[k] != "function"){
15419                 if(b[k]){
15420                     buttons[k].show();
15421                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15422                     width += buttons[k].el.getWidth()+15;
15423                 }else{
15424                     buttons[k].hide();
15425                 }
15426             }
15427         }
15428         return width;
15429     };
15430
15431     // private
15432     var handleEsc = function(d, k, e){
15433         if(opt && opt.closable !== false){
15434             dlg.hide();
15435         }
15436         if(e){
15437             e.stopEvent();
15438         }
15439     };
15440
15441     return {
15442         /**
15443          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15444          * @return {Roo.BasicDialog} The BasicDialog element
15445          */
15446         getDialog : function(){
15447            if(!dlg){
15448                 dlg = new Roo.BasicDialog("x-msg-box", {
15449                     autoCreate : true,
15450                     shadow: true,
15451                     draggable: true,
15452                     resizable:false,
15453                     constraintoviewport:false,
15454                     fixedcenter:true,
15455                     collapsible : false,
15456                     shim:true,
15457                     modal: true,
15458                     width:400, height:100,
15459                     buttonAlign:"center",
15460                     closeClick : function(){
15461                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15462                             handleButton("no");
15463                         }else{
15464                             handleButton("cancel");
15465                         }
15466                     }
15467                 });
15468                 dlg.on("hide", handleHide);
15469                 mask = dlg.mask;
15470                 dlg.addKeyListener(27, handleEsc);
15471                 buttons = {};
15472                 var bt = this.buttonText;
15473                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15474                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15475                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15476                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15477                 bodyEl = dlg.body.createChild({
15478
15479                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15480                 });
15481                 msgEl = bodyEl.dom.firstChild;
15482                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15483                 textboxEl.enableDisplayMode();
15484                 textboxEl.addKeyListener([10,13], function(){
15485                     if(dlg.isVisible() && opt && opt.buttons){
15486                         if(opt.buttons.ok){
15487                             handleButton("ok");
15488                         }else if(opt.buttons.yes){
15489                             handleButton("yes");
15490                         }
15491                     }
15492                 });
15493                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15494                 textareaEl.enableDisplayMode();
15495                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15496                 progressEl.enableDisplayMode();
15497                 var pf = progressEl.dom.firstChild;
15498                 if (pf) {
15499                     pp = Roo.get(pf.firstChild);
15500                     pp.setHeight(pf.offsetHeight);
15501                 }
15502                 
15503             }
15504             return dlg;
15505         },
15506
15507         /**
15508          * Updates the message box body text
15509          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15510          * the XHTML-compliant non-breaking space character '&amp;#160;')
15511          * @return {Roo.MessageBox} This message box
15512          */
15513         updateText : function(text){
15514             if(!dlg.isVisible() && !opt.width){
15515                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15516             }
15517             msgEl.innerHTML = text || '&#160;';
15518             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15519                         Math.max(opt.minWidth || this.minWidth, bwidth));
15520             if(opt.prompt){
15521                 activeTextEl.setWidth(w);
15522             }
15523             if(dlg.isVisible()){
15524                 dlg.fixedcenter = false;
15525             }
15526             dlg.setContentSize(w, bodyEl.getHeight());
15527             if(dlg.isVisible()){
15528                 dlg.fixedcenter = true;
15529             }
15530             return this;
15531         },
15532
15533         /**
15534          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15535          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15536          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15537          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15538          * @return {Roo.MessageBox} This message box
15539          */
15540         updateProgress : function(value, text){
15541             if(text){
15542                 this.updateText(text);
15543             }
15544             if (pp) { // weird bug on my firefox - for some reason this is not defined
15545                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15546             }
15547             return this;
15548         },        
15549
15550         /**
15551          * Returns true if the message box is currently displayed
15552          * @return {Boolean} True if the message box is visible, else false
15553          */
15554         isVisible : function(){
15555             return dlg && dlg.isVisible();  
15556         },
15557
15558         /**
15559          * Hides the message box if it is displayed
15560          */
15561         hide : function(){
15562             if(this.isVisible()){
15563                 dlg.hide();
15564             }  
15565         },
15566
15567         /**
15568          * Displays a new message box, or reinitializes an existing message box, based on the config options
15569          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15570          * The following config object properties are supported:
15571          * <pre>
15572 Property    Type             Description
15573 ----------  ---------------  ------------------------------------------------------------------------------------
15574 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15575                                    closes (defaults to undefined)
15576 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15577                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15578 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15579                                    progress and wait dialogs will ignore this property and always hide the
15580                                    close button as they can only be closed programmatically.
15581 cls               String           A custom CSS class to apply to the message box element
15582 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15583                                    displayed (defaults to 75)
15584 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15585                                    function will be btn (the name of the button that was clicked, if applicable,
15586                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15587                                    Progress and wait dialogs will ignore this option since they do not respond to
15588                                    user actions and can only be closed programmatically, so any required function
15589                                    should be called by the same code after it closes the dialog.
15590 icon              String           A CSS class that provides a background image to be used as an icon for
15591                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15592 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15593 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15594 modal             Boolean          False to allow user interaction with the page while the message box is
15595                                    displayed (defaults to true)
15596 msg               String           A string that will replace the existing message box body text (defaults
15597                                    to the XHTML-compliant non-breaking space character '&#160;')
15598 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15599 progress          Boolean          True to display a progress bar (defaults to false)
15600 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15601 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15602 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15603 title             String           The title text
15604 value             String           The string value to set into the active textbox element if displayed
15605 wait              Boolean          True to display a progress bar (defaults to false)
15606 width             Number           The width of the dialog in pixels
15607 </pre>
15608          *
15609          * Example usage:
15610          * <pre><code>
15611 Roo.Msg.show({
15612    title: 'Address',
15613    msg: 'Please enter your address:',
15614    width: 300,
15615    buttons: Roo.MessageBox.OKCANCEL,
15616    multiline: true,
15617    fn: saveAddress,
15618    animEl: 'addAddressBtn'
15619 });
15620 </code></pre>
15621          * @param {Object} config Configuration options
15622          * @return {Roo.MessageBox} This message box
15623          */
15624         show : function(options){
15625             if(this.isVisible()){
15626                 this.hide();
15627             }
15628             var d = this.getDialog();
15629             opt = options;
15630             d.setTitle(opt.title || "&#160;");
15631             d.close.setDisplayed(opt.closable !== false);
15632             activeTextEl = textboxEl;
15633             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15634             if(opt.prompt){
15635                 if(opt.multiline){
15636                     textboxEl.hide();
15637                     textareaEl.show();
15638                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15639                         opt.multiline : this.defaultTextHeight);
15640                     activeTextEl = textareaEl;
15641                 }else{
15642                     textboxEl.show();
15643                     textareaEl.hide();
15644                 }
15645             }else{
15646                 textboxEl.hide();
15647                 textareaEl.hide();
15648             }
15649             progressEl.setDisplayed(opt.progress === true);
15650             this.updateProgress(0);
15651             activeTextEl.dom.value = opt.value || "";
15652             if(opt.prompt){
15653                 dlg.setDefaultButton(activeTextEl);
15654             }else{
15655                 var bs = opt.buttons;
15656                 var db = null;
15657                 if(bs && bs.ok){
15658                     db = buttons["ok"];
15659                 }else if(bs && bs.yes){
15660                     db = buttons["yes"];
15661                 }
15662                 dlg.setDefaultButton(db);
15663             }
15664             bwidth = updateButtons(opt.buttons);
15665             this.updateText(opt.msg);
15666             if(opt.cls){
15667                 d.el.addClass(opt.cls);
15668             }
15669             d.proxyDrag = opt.proxyDrag === true;
15670             d.modal = opt.modal !== false;
15671             d.mask = opt.modal !== false ? mask : false;
15672             if(!d.isVisible()){
15673                 // force it to the end of the z-index stack so it gets a cursor in FF
15674                 document.body.appendChild(dlg.el.dom);
15675                 d.animateTarget = null;
15676                 d.show(options.animEl);
15677             }
15678             return this;
15679         },
15680
15681         /**
15682          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15683          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15684          * and closing the message box when the process is complete.
15685          * @param {String} title The title bar text
15686          * @param {String} msg The message box body text
15687          * @return {Roo.MessageBox} This message box
15688          */
15689         progress : function(title, msg){
15690             this.show({
15691                 title : title,
15692                 msg : msg,
15693                 buttons: false,
15694                 progress:true,
15695                 closable:false,
15696                 minWidth: this.minProgressWidth,
15697                 modal : true
15698             });
15699             return this;
15700         },
15701
15702         /**
15703          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15704          * If a callback function is passed it will be called after the user clicks the button, and the
15705          * id of the button that was clicked will be passed as the only parameter to the callback
15706          * (could also be the top-right close button).
15707          * @param {String} title The title bar text
15708          * @param {String} msg The message box body text
15709          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15710          * @param {Object} scope (optional) The scope of the callback function
15711          * @return {Roo.MessageBox} This message box
15712          */
15713         alert : function(title, msg, fn, scope){
15714             this.show({
15715                 title : title,
15716                 msg : msg,
15717                 buttons: this.OK,
15718                 fn: fn,
15719                 scope : scope,
15720                 modal : true
15721             });
15722             return this;
15723         },
15724
15725         /**
15726          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15727          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15728          * You are responsible for closing the message box when the process is complete.
15729          * @param {String} msg The message box body text
15730          * @param {String} title (optional) The title bar text
15731          * @return {Roo.MessageBox} This message box
15732          */
15733         wait : function(msg, title){
15734             this.show({
15735                 title : title,
15736                 msg : msg,
15737                 buttons: false,
15738                 closable:false,
15739                 progress:true,
15740                 modal:true,
15741                 width:300,
15742                 wait:true
15743             });
15744             waitTimer = Roo.TaskMgr.start({
15745                 run: function(i){
15746                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15747                 },
15748                 interval: 1000
15749             });
15750             return this;
15751         },
15752
15753         /**
15754          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15755          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15756          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15757          * @param {String} title The title bar text
15758          * @param {String} msg The message box body text
15759          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15760          * @param {Object} scope (optional) The scope of the callback function
15761          * @return {Roo.MessageBox} This message box
15762          */
15763         confirm : function(title, msg, fn, scope){
15764             this.show({
15765                 title : title,
15766                 msg : msg,
15767                 buttons: this.YESNO,
15768                 fn: fn,
15769                 scope : scope,
15770                 modal : true
15771             });
15772             return this;
15773         },
15774
15775         /**
15776          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15777          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15778          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15779          * (could also be the top-right close button) and the text that was entered will be passed as the two
15780          * parameters to the callback.
15781          * @param {String} title The title bar text
15782          * @param {String} msg The message box body text
15783          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15784          * @param {Object} scope (optional) The scope of the callback function
15785          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15786          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15787          * @return {Roo.MessageBox} This message box
15788          */
15789         prompt : function(title, msg, fn, scope, multiline){
15790             this.show({
15791                 title : title,
15792                 msg : msg,
15793                 buttons: this.OKCANCEL,
15794                 fn: fn,
15795                 minWidth:250,
15796                 scope : scope,
15797                 prompt:true,
15798                 multiline: multiline,
15799                 modal : true
15800             });
15801             return this;
15802         },
15803
15804         /**
15805          * Button config that displays a single OK button
15806          * @type Object
15807          */
15808         OK : {ok:true},
15809         /**
15810          * Button config that displays Yes and No buttons
15811          * @type Object
15812          */
15813         YESNO : {yes:true, no:true},
15814         /**
15815          * Button config that displays OK and Cancel buttons
15816          * @type Object
15817          */
15818         OKCANCEL : {ok:true, cancel:true},
15819         /**
15820          * Button config that displays Yes, No and Cancel buttons
15821          * @type Object
15822          */
15823         YESNOCANCEL : {yes:true, no:true, cancel:true},
15824
15825         /**
15826          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15827          * @type Number
15828          */
15829         defaultTextHeight : 75,
15830         /**
15831          * The maximum width in pixels of the message box (defaults to 600)
15832          * @type Number
15833          */
15834         maxWidth : 600,
15835         /**
15836          * The minimum width in pixels of the message box (defaults to 100)
15837          * @type Number
15838          */
15839         minWidth : 100,
15840         /**
15841          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15842          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15843          * @type Number
15844          */
15845         minProgressWidth : 250,
15846         /**
15847          * An object containing the default button text strings that can be overriden for localized language support.
15848          * Supported properties are: ok, cancel, yes and no.
15849          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15850          * @type Object
15851          */
15852         buttonText : {
15853             ok : "OK",
15854             cancel : "Cancel",
15855             yes : "Yes",
15856             no : "No"
15857         }
15858     };
15859 }();
15860
15861 /**
15862  * Shorthand for {@link Roo.MessageBox}
15863  */
15864 Roo.Msg = Roo.MessageBox;/*
15865  * Based on:
15866  * Ext JS Library 1.1.1
15867  * Copyright(c) 2006-2007, Ext JS, LLC.
15868  *
15869  * Originally Released Under LGPL - original licence link has changed is not relivant.
15870  *
15871  * Fork - LGPL
15872  * <script type="text/javascript">
15873  */
15874 /**
15875  * @class Roo.QuickTips
15876  * Provides attractive and customizable tooltips for any element.
15877  * @singleton
15878  */
15879 Roo.QuickTips = function(){
15880     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15881     var ce, bd, xy, dd;
15882     var visible = false, disabled = true, inited = false;
15883     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15884     
15885     var onOver = function(e){
15886         if(disabled){
15887             return;
15888         }
15889         var t = e.getTarget();
15890         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15891             return;
15892         }
15893         if(ce && t == ce.el){
15894             clearTimeout(hideProc);
15895             return;
15896         }
15897         if(t && tagEls[t.id]){
15898             tagEls[t.id].el = t;
15899             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15900             return;
15901         }
15902         var ttp, et = Roo.fly(t);
15903         var ns = cfg.namespace;
15904         if(tm.interceptTitles && t.title){
15905             ttp = t.title;
15906             t.qtip = ttp;
15907             t.removeAttribute("title");
15908             e.preventDefault();
15909         }else{
15910             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15911         }
15912         if(ttp){
15913             showProc = show.defer(tm.showDelay, tm, [{
15914                 el: t, 
15915                 text: ttp, 
15916                 width: et.getAttributeNS(ns, cfg.width),
15917                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15918                 title: et.getAttributeNS(ns, cfg.title),
15919                     cls: et.getAttributeNS(ns, cfg.cls)
15920             }]);
15921         }
15922     };
15923     
15924     var onOut = function(e){
15925         clearTimeout(showProc);
15926         var t = e.getTarget();
15927         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15928             hideProc = setTimeout(hide, tm.hideDelay);
15929         }
15930     };
15931     
15932     var onMove = function(e){
15933         if(disabled){
15934             return;
15935         }
15936         xy = e.getXY();
15937         xy[1] += 18;
15938         if(tm.trackMouse && ce){
15939             el.setXY(xy);
15940         }
15941     };
15942     
15943     var onDown = function(e){
15944         clearTimeout(showProc);
15945         clearTimeout(hideProc);
15946         if(!e.within(el)){
15947             if(tm.hideOnClick){
15948                 hide();
15949                 tm.disable();
15950                 tm.enable.defer(100, tm);
15951             }
15952         }
15953     };
15954     
15955     var getPad = function(){
15956         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15957     };
15958
15959     var show = function(o){
15960         if(disabled){
15961             return;
15962         }
15963         clearTimeout(dismissProc);
15964         ce = o;
15965         if(removeCls){ // in case manually hidden
15966             el.removeClass(removeCls);
15967             removeCls = null;
15968         }
15969         if(ce.cls){
15970             el.addClass(ce.cls);
15971             removeCls = ce.cls;
15972         }
15973         if(ce.title){
15974             tipTitle.update(ce.title);
15975             tipTitle.show();
15976         }else{
15977             tipTitle.update('');
15978             tipTitle.hide();
15979         }
15980         el.dom.style.width  = tm.maxWidth+'px';
15981         //tipBody.dom.style.width = '';
15982         tipBodyText.update(o.text);
15983         var p = getPad(), w = ce.width;
15984         if(!w){
15985             var td = tipBodyText.dom;
15986             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15987             if(aw > tm.maxWidth){
15988                 w = tm.maxWidth;
15989             }else if(aw < tm.minWidth){
15990                 w = tm.minWidth;
15991             }else{
15992                 w = aw;
15993             }
15994         }
15995         //tipBody.setWidth(w);
15996         el.setWidth(parseInt(w, 10) + p);
15997         if(ce.autoHide === false){
15998             close.setDisplayed(true);
15999             if(dd){
16000                 dd.unlock();
16001             }
16002         }else{
16003             close.setDisplayed(false);
16004             if(dd){
16005                 dd.lock();
16006             }
16007         }
16008         if(xy){
16009             el.avoidY = xy[1]-18;
16010             el.setXY(xy);
16011         }
16012         if(tm.animate){
16013             el.setOpacity(.1);
16014             el.setStyle("visibility", "visible");
16015             el.fadeIn({callback: afterShow});
16016         }else{
16017             afterShow();
16018         }
16019     };
16020     
16021     var afterShow = function(){
16022         if(ce){
16023             el.show();
16024             esc.enable();
16025             if(tm.autoDismiss && ce.autoHide !== false){
16026                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16027             }
16028         }
16029     };
16030     
16031     var hide = function(noanim){
16032         clearTimeout(dismissProc);
16033         clearTimeout(hideProc);
16034         ce = null;
16035         if(el.isVisible()){
16036             esc.disable();
16037             if(noanim !== true && tm.animate){
16038                 el.fadeOut({callback: afterHide});
16039             }else{
16040                 afterHide();
16041             } 
16042         }
16043     };
16044     
16045     var afterHide = function(){
16046         el.hide();
16047         if(removeCls){
16048             el.removeClass(removeCls);
16049             removeCls = null;
16050         }
16051     };
16052     
16053     return {
16054         /**
16055         * @cfg {Number} minWidth
16056         * The minimum width of the quick tip (defaults to 40)
16057         */
16058        minWidth : 40,
16059         /**
16060         * @cfg {Number} maxWidth
16061         * The maximum width of the quick tip (defaults to 300)
16062         */
16063        maxWidth : 300,
16064         /**
16065         * @cfg {Boolean} interceptTitles
16066         * True to automatically use the element's DOM title value if available (defaults to false)
16067         */
16068        interceptTitles : false,
16069         /**
16070         * @cfg {Boolean} trackMouse
16071         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16072         */
16073        trackMouse : false,
16074         /**
16075         * @cfg {Boolean} hideOnClick
16076         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16077         */
16078        hideOnClick : true,
16079         /**
16080         * @cfg {Number} showDelay
16081         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16082         */
16083        showDelay : 500,
16084         /**
16085         * @cfg {Number} hideDelay
16086         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16087         */
16088        hideDelay : 200,
16089         /**
16090         * @cfg {Boolean} autoHide
16091         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16092         * Used in conjunction with hideDelay.
16093         */
16094        autoHide : true,
16095         /**
16096         * @cfg {Boolean}
16097         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16098         * (defaults to true).  Used in conjunction with autoDismissDelay.
16099         */
16100        autoDismiss : true,
16101         /**
16102         * @cfg {Number}
16103         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16104         */
16105        autoDismissDelay : 5000,
16106        /**
16107         * @cfg {Boolean} animate
16108         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16109         */
16110        animate : false,
16111
16112        /**
16113         * @cfg {String} title
16114         * Title text to display (defaults to '').  This can be any valid HTML markup.
16115         */
16116         title: '',
16117        /**
16118         * @cfg {String} text
16119         * Body text to display (defaults to '').  This can be any valid HTML markup.
16120         */
16121         text : '',
16122        /**
16123         * @cfg {String} cls
16124         * A CSS class to apply to the base quick tip element (defaults to '').
16125         */
16126         cls : '',
16127        /**
16128         * @cfg {Number} width
16129         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16130         * minWidth or maxWidth.
16131         */
16132         width : null,
16133
16134     /**
16135      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16136      * or display QuickTips in a page.
16137      */
16138        init : function(){
16139           tm = Roo.QuickTips;
16140           cfg = tm.tagConfig;
16141           if(!inited){
16142               if(!Roo.isReady){ // allow calling of init() before onReady
16143                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16144                   return;
16145               }
16146               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16147               el.fxDefaults = {stopFx: true};
16148               // maximum custom styling
16149               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16150               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16151               tipTitle = el.child('h3');
16152               tipTitle.enableDisplayMode("block");
16153               tipBody = el.child('div.x-tip-bd');
16154               tipBodyText = el.child('div.x-tip-bd-inner');
16155               //bdLeft = el.child('div.x-tip-bd-left');
16156               //bdRight = el.child('div.x-tip-bd-right');
16157               close = el.child('div.x-tip-close');
16158               close.enableDisplayMode("block");
16159               close.on("click", hide);
16160               var d = Roo.get(document);
16161               d.on("mousedown", onDown);
16162               d.on("mouseover", onOver);
16163               d.on("mouseout", onOut);
16164               d.on("mousemove", onMove);
16165               esc = d.addKeyListener(27, hide);
16166               esc.disable();
16167               if(Roo.dd.DD){
16168                   dd = el.initDD("default", null, {
16169                       onDrag : function(){
16170                           el.sync();  
16171                       }
16172                   });
16173                   dd.setHandleElId(tipTitle.id);
16174                   dd.lock();
16175               }
16176               inited = true;
16177           }
16178           this.enable(); 
16179        },
16180
16181     /**
16182      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16183      * are supported:
16184      * <pre>
16185 Property    Type                   Description
16186 ----------  ---------------------  ------------------------------------------------------------------------
16187 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16188      * </ul>
16189      * @param {Object} config The config object
16190      */
16191        register : function(config){
16192            var cs = config instanceof Array ? config : arguments;
16193            for(var i = 0, len = cs.length; i < len; i++) {
16194                var c = cs[i];
16195                var target = c.target;
16196                if(target){
16197                    if(target instanceof Array){
16198                        for(var j = 0, jlen = target.length; j < jlen; j++){
16199                            tagEls[target[j]] = c;
16200                        }
16201                    }else{
16202                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16203                    }
16204                }
16205            }
16206        },
16207
16208     /**
16209      * Removes this quick tip from its element and destroys it.
16210      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16211      */
16212        unregister : function(el){
16213            delete tagEls[Roo.id(el)];
16214        },
16215
16216     /**
16217      * Enable this quick tip.
16218      */
16219        enable : function(){
16220            if(inited && disabled){
16221                locks.pop();
16222                if(locks.length < 1){
16223                    disabled = false;
16224                }
16225            }
16226        },
16227
16228     /**
16229      * Disable this quick tip.
16230      */
16231        disable : function(){
16232           disabled = true;
16233           clearTimeout(showProc);
16234           clearTimeout(hideProc);
16235           clearTimeout(dismissProc);
16236           if(ce){
16237               hide(true);
16238           }
16239           locks.push(1);
16240        },
16241
16242     /**
16243      * Returns true if the quick tip is enabled, else false.
16244      */
16245        isEnabled : function(){
16246             return !disabled;
16247        },
16248
16249         // private
16250        tagConfig : {
16251            namespace : "ext",
16252            attribute : "qtip",
16253            width : "width",
16254            target : "target",
16255            title : "qtitle",
16256            hide : "hide",
16257            cls : "qclass"
16258        }
16259    };
16260 }();
16261
16262 // backwards compat
16263 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16264  * Based on:
16265  * Ext JS Library 1.1.1
16266  * Copyright(c) 2006-2007, Ext JS, LLC.
16267  *
16268  * Originally Released Under LGPL - original licence link has changed is not relivant.
16269  *
16270  * Fork - LGPL
16271  * <script type="text/javascript">
16272  */
16273  
16274
16275 /**
16276  * @class Roo.tree.TreePanel
16277  * @extends Roo.data.Tree
16278
16279  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16280  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16281  * @cfg {Boolean} enableDD true to enable drag and drop
16282  * @cfg {Boolean} enableDrag true to enable just drag
16283  * @cfg {Boolean} enableDrop true to enable just drop
16284  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16285  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16286  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16287  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16288  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16289  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16290  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16291  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16292  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16293  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16294  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16295  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16296  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16297  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16298  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16299  * 
16300  * @constructor
16301  * @param {String/HTMLElement/Element} el The container element
16302  * @param {Object} config
16303  */
16304 Roo.tree.TreePanel = function(el, config){
16305     var root = false;
16306     var loader = false;
16307     if (config.root) {
16308         root = config.root;
16309         delete config.root;
16310     }
16311     if (config.loader) {
16312         loader = config.loader;
16313         delete config.loader;
16314     }
16315     
16316     Roo.apply(this, config);
16317     Roo.tree.TreePanel.superclass.constructor.call(this);
16318     this.el = Roo.get(el);
16319     this.el.addClass('x-tree');
16320     //console.log(root);
16321     if (root) {
16322         this.setRootNode( Roo.factory(root, Roo.tree));
16323     }
16324     if (loader) {
16325         this.loader = Roo.factory(loader, Roo.tree);
16326     }
16327    /**
16328     * Read-only. The id of the container element becomes this TreePanel's id.
16329     */
16330    this.id = this.el.id;
16331    this.addEvents({
16332         /**
16333         * @event beforeload
16334         * Fires before a node is loaded, return false to cancel
16335         * @param {Node} node The node being loaded
16336         */
16337         "beforeload" : true,
16338         /**
16339         * @event load
16340         * Fires when a node is loaded
16341         * @param {Node} node The node that was loaded
16342         */
16343         "load" : true,
16344         /**
16345         * @event textchange
16346         * Fires when the text for a node is changed
16347         * @param {Node} node The node
16348         * @param {String} text The new text
16349         * @param {String} oldText The old text
16350         */
16351         "textchange" : true,
16352         /**
16353         * @event beforeexpand
16354         * Fires before a node is expanded, return false to cancel.
16355         * @param {Node} node The node
16356         * @param {Boolean} deep
16357         * @param {Boolean} anim
16358         */
16359         "beforeexpand" : true,
16360         /**
16361         * @event beforecollapse
16362         * Fires before a node is collapsed, return false to cancel.
16363         * @param {Node} node The node
16364         * @param {Boolean} deep
16365         * @param {Boolean} anim
16366         */
16367         "beforecollapse" : true,
16368         /**
16369         * @event expand
16370         * Fires when a node is expanded
16371         * @param {Node} node The node
16372         */
16373         "expand" : true,
16374         /**
16375         * @event disabledchange
16376         * Fires when the disabled status of a node changes
16377         * @param {Node} node The node
16378         * @param {Boolean} disabled
16379         */
16380         "disabledchange" : true,
16381         /**
16382         * @event collapse
16383         * Fires when a node is collapsed
16384         * @param {Node} node The node
16385         */
16386         "collapse" : true,
16387         /**
16388         * @event beforeclick
16389         * Fires before click processing on a node. Return false to cancel the default action.
16390         * @param {Node} node The node
16391         * @param {Roo.EventObject} e The event object
16392         */
16393         "beforeclick":true,
16394         /**
16395         * @event checkchange
16396         * Fires when a node with a checkbox's checked property changes
16397         * @param {Node} this This node
16398         * @param {Boolean} checked
16399         */
16400         "checkchange":true,
16401         /**
16402         * @event click
16403         * Fires when a node is clicked
16404         * @param {Node} node The node
16405         * @param {Roo.EventObject} e The event object
16406         */
16407         "click":true,
16408         /**
16409         * @event dblclick
16410         * Fires when a node is double clicked
16411         * @param {Node} node The node
16412         * @param {Roo.EventObject} e The event object
16413         */
16414         "dblclick":true,
16415         /**
16416         * @event contextmenu
16417         * Fires when a node is right clicked
16418         * @param {Node} node The node
16419         * @param {Roo.EventObject} e The event object
16420         */
16421         "contextmenu":true,
16422         /**
16423         * @event beforechildrenrendered
16424         * Fires right before the child nodes for a node are rendered
16425         * @param {Node} node The node
16426         */
16427         "beforechildrenrendered":true,
16428        /**
16429              * @event startdrag
16430              * Fires when a node starts being dragged
16431              * @param {Roo.tree.TreePanel} this
16432              * @param {Roo.tree.TreeNode} node
16433              * @param {event} e The raw browser event
16434              */ 
16435             "startdrag" : true,
16436             /**
16437              * @event enddrag
16438              * Fires when a drag operation is complete
16439              * @param {Roo.tree.TreePanel} this
16440              * @param {Roo.tree.TreeNode} node
16441              * @param {event} e The raw browser event
16442              */
16443             "enddrag" : true,
16444             /**
16445              * @event dragdrop
16446              * Fires when a dragged node is dropped on a valid DD target
16447              * @param {Roo.tree.TreePanel} this
16448              * @param {Roo.tree.TreeNode} node
16449              * @param {DD} dd The dd it was dropped on
16450              * @param {event} e The raw browser event
16451              */
16452             "dragdrop" : true,
16453             /**
16454              * @event beforenodedrop
16455              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16456              * passed to handlers has the following properties:<br />
16457              * <ul style="padding:5px;padding-left:16px;">
16458              * <li>tree - The TreePanel</li>
16459              * <li>target - The node being targeted for the drop</li>
16460              * <li>data - The drag data from the drag source</li>
16461              * <li>point - The point of the drop - append, above or below</li>
16462              * <li>source - The drag source</li>
16463              * <li>rawEvent - Raw mouse event</li>
16464              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16465              * to be inserted by setting them on this object.</li>
16466              * <li>cancel - Set this to true to cancel the drop.</li>
16467              * </ul>
16468              * @param {Object} dropEvent
16469              */
16470             "beforenodedrop" : true,
16471             /**
16472              * @event nodedrop
16473              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16474              * passed to handlers has the following properties:<br />
16475              * <ul style="padding:5px;padding-left:16px;">
16476              * <li>tree - The TreePanel</li>
16477              * <li>target - The node being targeted for the drop</li>
16478              * <li>data - The drag data from the drag source</li>
16479              * <li>point - The point of the drop - append, above or below</li>
16480              * <li>source - The drag source</li>
16481              * <li>rawEvent - Raw mouse event</li>
16482              * <li>dropNode - Dropped node(s).</li>
16483              * </ul>
16484              * @param {Object} dropEvent
16485              */
16486             "nodedrop" : true,
16487              /**
16488              * @event nodedragover
16489              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16490              * passed to handlers has the following properties:<br />
16491              * <ul style="padding:5px;padding-left:16px;">
16492              * <li>tree - The TreePanel</li>
16493              * <li>target - The node being targeted for the drop</li>
16494              * <li>data - The drag data from the drag source</li>
16495              * <li>point - The point of the drop - append, above or below</li>
16496              * <li>source - The drag source</li>
16497              * <li>rawEvent - Raw mouse event</li>
16498              * <li>dropNode - Drop node(s) provided by the source.</li>
16499              * <li>cancel - Set this to true to signal drop not allowed.</li>
16500              * </ul>
16501              * @param {Object} dragOverEvent
16502              */
16503             "nodedragover" : true
16504         
16505    });
16506    if(this.singleExpand){
16507        this.on("beforeexpand", this.restrictExpand, this);
16508    }
16509 };
16510 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16511     rootVisible : true,
16512     animate: Roo.enableFx,
16513     lines : true,
16514     enableDD : false,
16515     hlDrop : Roo.enableFx,
16516   
16517     renderer: false,
16518     
16519     rendererTip: false,
16520     // private
16521     restrictExpand : function(node){
16522         var p = node.parentNode;
16523         if(p){
16524             if(p.expandedChild && p.expandedChild.parentNode == p){
16525                 p.expandedChild.collapse();
16526             }
16527             p.expandedChild = node;
16528         }
16529     },
16530
16531     // private override
16532     setRootNode : function(node){
16533         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16534         if(!this.rootVisible){
16535             node.ui = new Roo.tree.RootTreeNodeUI(node);
16536         }
16537         return node;
16538     },
16539
16540     /**
16541      * Returns the container element for this TreePanel
16542      */
16543     getEl : function(){
16544         return this.el;
16545     },
16546
16547     /**
16548      * Returns the default TreeLoader for this TreePanel
16549      */
16550     getLoader : function(){
16551         return this.loader;
16552     },
16553
16554     /**
16555      * Expand all nodes
16556      */
16557     expandAll : function(){
16558         this.root.expand(true);
16559     },
16560
16561     /**
16562      * Collapse all nodes
16563      */
16564     collapseAll : function(){
16565         this.root.collapse(true);
16566     },
16567
16568     /**
16569      * Returns the selection model used by this TreePanel
16570      */
16571     getSelectionModel : function(){
16572         if(!this.selModel){
16573             this.selModel = new Roo.tree.DefaultSelectionModel();
16574         }
16575         return this.selModel;
16576     },
16577
16578     /**
16579      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16580      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16581      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16582      * @return {Array}
16583      */
16584     getChecked : function(a, startNode){
16585         startNode = startNode || this.root;
16586         var r = [];
16587         var f = function(){
16588             if(this.attributes.checked){
16589                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16590             }
16591         }
16592         startNode.cascade(f);
16593         return r;
16594     },
16595
16596     /**
16597      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16598      * @param {String} path
16599      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16600      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16601      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16602      */
16603     expandPath : function(path, attr, callback){
16604         attr = attr || "id";
16605         var keys = path.split(this.pathSeparator);
16606         var curNode = this.root;
16607         if(curNode.attributes[attr] != keys[1]){ // invalid root
16608             if(callback){
16609                 callback(false, null);
16610             }
16611             return;
16612         }
16613         var index = 1;
16614         var f = function(){
16615             if(++index == keys.length){
16616                 if(callback){
16617                     callback(true, curNode);
16618                 }
16619                 return;
16620             }
16621             var c = curNode.findChild(attr, keys[index]);
16622             if(!c){
16623                 if(callback){
16624                     callback(false, curNode);
16625                 }
16626                 return;
16627             }
16628             curNode = c;
16629             c.expand(false, false, f);
16630         };
16631         curNode.expand(false, false, f);
16632     },
16633
16634     /**
16635      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16636      * @param {String} path
16637      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16638      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16639      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16640      */
16641     selectPath : function(path, attr, callback){
16642         attr = attr || "id";
16643         var keys = path.split(this.pathSeparator);
16644         var v = keys.pop();
16645         if(keys.length > 0){
16646             var f = function(success, node){
16647                 if(success && node){
16648                     var n = node.findChild(attr, v);
16649                     if(n){
16650                         n.select();
16651                         if(callback){
16652                             callback(true, n);
16653                         }
16654                     }else if(callback){
16655                         callback(false, n);
16656                     }
16657                 }else{
16658                     if(callback){
16659                         callback(false, n);
16660                     }
16661                 }
16662             };
16663             this.expandPath(keys.join(this.pathSeparator), attr, f);
16664         }else{
16665             this.root.select();
16666             if(callback){
16667                 callback(true, this.root);
16668             }
16669         }
16670     },
16671
16672     getTreeEl : function(){
16673         return this.el;
16674     },
16675
16676     /**
16677      * Trigger rendering of this TreePanel
16678      */
16679     render : function(){
16680         if (this.innerCt) {
16681             return this; // stop it rendering more than once!!
16682         }
16683         
16684         this.innerCt = this.el.createChild({tag:"ul",
16685                cls:"x-tree-root-ct " +
16686                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16687
16688         if(this.containerScroll){
16689             Roo.dd.ScrollManager.register(this.el);
16690         }
16691         if((this.enableDD || this.enableDrop) && !this.dropZone){
16692            /**
16693             * The dropZone used by this tree if drop is enabled
16694             * @type Roo.tree.TreeDropZone
16695             */
16696              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16697                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16698            });
16699         }
16700         if((this.enableDD || this.enableDrag) && !this.dragZone){
16701            /**
16702             * The dragZone used by this tree if drag is enabled
16703             * @type Roo.tree.TreeDragZone
16704             */
16705             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16706                ddGroup: this.ddGroup || "TreeDD",
16707                scroll: this.ddScroll
16708            });
16709         }
16710         this.getSelectionModel().init(this);
16711         if (!this.root) {
16712             console.log("ROOT not set in tree");
16713             return;
16714         }
16715         this.root.render();
16716         if(!this.rootVisible){
16717             this.root.renderChildren();
16718         }
16719         return this;
16720     }
16721 });/*
16722  * Based on:
16723  * Ext JS Library 1.1.1
16724  * Copyright(c) 2006-2007, Ext JS, LLC.
16725  *
16726  * Originally Released Under LGPL - original licence link has changed is not relivant.
16727  *
16728  * Fork - LGPL
16729  * <script type="text/javascript">
16730  */
16731  
16732
16733 /**
16734  * @class Roo.tree.DefaultSelectionModel
16735  * @extends Roo.util.Observable
16736  * The default single selection for a TreePanel.
16737  */
16738 Roo.tree.DefaultSelectionModel = function(){
16739    this.selNode = null;
16740    
16741    this.addEvents({
16742        /**
16743         * @event selectionchange
16744         * Fires when the selected node changes
16745         * @param {DefaultSelectionModel} this
16746         * @param {TreeNode} node the new selection
16747         */
16748        "selectionchange" : true,
16749
16750        /**
16751         * @event beforeselect
16752         * Fires before the selected node changes, return false to cancel the change
16753         * @param {DefaultSelectionModel} this
16754         * @param {TreeNode} node the new selection
16755         * @param {TreeNode} node the old selection
16756         */
16757        "beforeselect" : true
16758    });
16759 };
16760
16761 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16762     init : function(tree){
16763         this.tree = tree;
16764         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16765         tree.on("click", this.onNodeClick, this);
16766     },
16767     
16768     onNodeClick : function(node, e){
16769         if (e.ctrlKey && this.selNode == node)  {
16770             this.unselect(node);
16771             return;
16772         }
16773         this.select(node);
16774     },
16775     
16776     /**
16777      * Select a node.
16778      * @param {TreeNode} node The node to select
16779      * @return {TreeNode} The selected node
16780      */
16781     select : function(node){
16782         var last = this.selNode;
16783         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16784             if(last){
16785                 last.ui.onSelectedChange(false);
16786             }
16787             this.selNode = node;
16788             node.ui.onSelectedChange(true);
16789             this.fireEvent("selectionchange", this, node, last);
16790         }
16791         return node;
16792     },
16793     
16794     /**
16795      * Deselect a node.
16796      * @param {TreeNode} node The node to unselect
16797      */
16798     unselect : function(node){
16799         if(this.selNode == node){
16800             this.clearSelections();
16801         }    
16802     },
16803     
16804     /**
16805      * Clear all selections
16806      */
16807     clearSelections : function(){
16808         var n = this.selNode;
16809         if(n){
16810             n.ui.onSelectedChange(false);
16811             this.selNode = null;
16812             this.fireEvent("selectionchange", this, null);
16813         }
16814         return n;
16815     },
16816     
16817     /**
16818      * Get the selected node
16819      * @return {TreeNode} The selected node
16820      */
16821     getSelectedNode : function(){
16822         return this.selNode;    
16823     },
16824     
16825     /**
16826      * Returns true if the node is selected
16827      * @param {TreeNode} node The node to check
16828      * @return {Boolean}
16829      */
16830     isSelected : function(node){
16831         return this.selNode == node;  
16832     },
16833
16834     /**
16835      * Selects the node above the selected node in the tree, intelligently walking the nodes
16836      * @return TreeNode The new selection
16837      */
16838     selectPrevious : function(){
16839         var s = this.selNode || this.lastSelNode;
16840         if(!s){
16841             return null;
16842         }
16843         var ps = s.previousSibling;
16844         if(ps){
16845             if(!ps.isExpanded() || ps.childNodes.length < 1){
16846                 return this.select(ps);
16847             } else{
16848                 var lc = ps.lastChild;
16849                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16850                     lc = lc.lastChild;
16851                 }
16852                 return this.select(lc);
16853             }
16854         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16855             return this.select(s.parentNode);
16856         }
16857         return null;
16858     },
16859
16860     /**
16861      * Selects the node above the selected node in the tree, intelligently walking the nodes
16862      * @return TreeNode The new selection
16863      */
16864     selectNext : function(){
16865         var s = this.selNode || this.lastSelNode;
16866         if(!s){
16867             return null;
16868         }
16869         if(s.firstChild && s.isExpanded()){
16870              return this.select(s.firstChild);
16871          }else if(s.nextSibling){
16872              return this.select(s.nextSibling);
16873          }else if(s.parentNode){
16874             var newS = null;
16875             s.parentNode.bubble(function(){
16876                 if(this.nextSibling){
16877                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16878                     return false;
16879                 }
16880             });
16881             return newS;
16882          }
16883         return null;
16884     },
16885
16886     onKeyDown : function(e){
16887         var s = this.selNode || this.lastSelNode;
16888         // undesirable, but required
16889         var sm = this;
16890         if(!s){
16891             return;
16892         }
16893         var k = e.getKey();
16894         switch(k){
16895              case e.DOWN:
16896                  e.stopEvent();
16897                  this.selectNext();
16898              break;
16899              case e.UP:
16900                  e.stopEvent();
16901                  this.selectPrevious();
16902              break;
16903              case e.RIGHT:
16904                  e.preventDefault();
16905                  if(s.hasChildNodes()){
16906                      if(!s.isExpanded()){
16907                          s.expand();
16908                      }else if(s.firstChild){
16909                          this.select(s.firstChild, e);
16910                      }
16911                  }
16912              break;
16913              case e.LEFT:
16914                  e.preventDefault();
16915                  if(s.hasChildNodes() && s.isExpanded()){
16916                      s.collapse();
16917                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16918                      this.select(s.parentNode, e);
16919                  }
16920              break;
16921         };
16922     }
16923 });
16924
16925 /**
16926  * @class Roo.tree.MultiSelectionModel
16927  * @extends Roo.util.Observable
16928  * Multi selection for a TreePanel.
16929  */
16930 Roo.tree.MultiSelectionModel = function(){
16931    this.selNodes = [];
16932    this.selMap = {};
16933    this.addEvents({
16934        /**
16935         * @event selectionchange
16936         * Fires when the selected nodes change
16937         * @param {MultiSelectionModel} this
16938         * @param {Array} nodes Array of the selected nodes
16939         */
16940        "selectionchange" : true
16941    });
16942 };
16943
16944 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16945     init : function(tree){
16946         this.tree = tree;
16947         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16948         tree.on("click", this.onNodeClick, this);
16949     },
16950     
16951     onNodeClick : function(node, e){
16952         this.select(node, e, e.ctrlKey);
16953     },
16954     
16955     /**
16956      * Select a node.
16957      * @param {TreeNode} node The node to select
16958      * @param {EventObject} e (optional) An event associated with the selection
16959      * @param {Boolean} keepExisting True to retain existing selections
16960      * @return {TreeNode} The selected node
16961      */
16962     select : function(node, e, keepExisting){
16963         if(keepExisting !== true){
16964             this.clearSelections(true);
16965         }
16966         if(this.isSelected(node)){
16967             this.lastSelNode = node;
16968             return node;
16969         }
16970         this.selNodes.push(node);
16971         this.selMap[node.id] = node;
16972         this.lastSelNode = node;
16973         node.ui.onSelectedChange(true);
16974         this.fireEvent("selectionchange", this, this.selNodes);
16975         return node;
16976     },
16977     
16978     /**
16979      * Deselect a node.
16980      * @param {TreeNode} node The node to unselect
16981      */
16982     unselect : function(node){
16983         if(this.selMap[node.id]){
16984             node.ui.onSelectedChange(false);
16985             var sn = this.selNodes;
16986             var index = -1;
16987             if(sn.indexOf){
16988                 index = sn.indexOf(node);
16989             }else{
16990                 for(var i = 0, len = sn.length; i < len; i++){
16991                     if(sn[i] == node){
16992                         index = i;
16993                         break;
16994                     }
16995                 }
16996             }
16997             if(index != -1){
16998                 this.selNodes.splice(index, 1);
16999             }
17000             delete this.selMap[node.id];
17001             this.fireEvent("selectionchange", this, this.selNodes);
17002         }
17003     },
17004     
17005     /**
17006      * Clear all selections
17007      */
17008     clearSelections : function(suppressEvent){
17009         var sn = this.selNodes;
17010         if(sn.length > 0){
17011             for(var i = 0, len = sn.length; i < len; i++){
17012                 sn[i].ui.onSelectedChange(false);
17013             }
17014             this.selNodes = [];
17015             this.selMap = {};
17016             if(suppressEvent !== true){
17017                 this.fireEvent("selectionchange", this, this.selNodes);
17018             }
17019         }
17020     },
17021     
17022     /**
17023      * Returns true if the node is selected
17024      * @param {TreeNode} node The node to check
17025      * @return {Boolean}
17026      */
17027     isSelected : function(node){
17028         return this.selMap[node.id] ? true : false;  
17029     },
17030     
17031     /**
17032      * Returns an array of the selected nodes
17033      * @return {Array}
17034      */
17035     getSelectedNodes : function(){
17036         return this.selNodes;    
17037     },
17038
17039     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17040
17041     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17042
17043     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17044 });/*
17045  * Based on:
17046  * Ext JS Library 1.1.1
17047  * Copyright(c) 2006-2007, Ext JS, LLC.
17048  *
17049  * Originally Released Under LGPL - original licence link has changed is not relivant.
17050  *
17051  * Fork - LGPL
17052  * <script type="text/javascript">
17053  */
17054  
17055 /**
17056  * @class Roo.tree.TreeNode
17057  * @extends Roo.data.Node
17058  * @cfg {String} text The text for this node
17059  * @cfg {Boolean} expanded true to start the node expanded
17060  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17061  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17062  * @cfg {Boolean} disabled true to start the node disabled
17063  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17064  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17065  * @cfg {String} cls A css class to be added to the node
17066  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17067  * @cfg {String} href URL of the link used for the node (defaults to #)
17068  * @cfg {String} hrefTarget target frame for the link
17069  * @cfg {String} qtip An Ext QuickTip for the node
17070  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17071  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17072  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17073  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17074  * (defaults to undefined with no checkbox rendered)
17075  * @constructor
17076  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17077  */
17078 Roo.tree.TreeNode = function(attributes){
17079     attributes = attributes || {};
17080     if(typeof attributes == "string"){
17081         attributes = {text: attributes};
17082     }
17083     this.childrenRendered = false;
17084     this.rendered = false;
17085     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17086     this.expanded = attributes.expanded === true;
17087     this.isTarget = attributes.isTarget !== false;
17088     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17089     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17090
17091     /**
17092      * Read-only. The text for this node. To change it use setText().
17093      * @type String
17094      */
17095     this.text = attributes.text;
17096     /**
17097      * True if this node is disabled.
17098      * @type Boolean
17099      */
17100     this.disabled = attributes.disabled === true;
17101
17102     this.addEvents({
17103         /**
17104         * @event textchange
17105         * Fires when the text for this node is changed
17106         * @param {Node} this This node
17107         * @param {String} text The new text
17108         * @param {String} oldText The old text
17109         */
17110         "textchange" : true,
17111         /**
17112         * @event beforeexpand
17113         * Fires before this node is expanded, return false to cancel.
17114         * @param {Node} this This node
17115         * @param {Boolean} deep
17116         * @param {Boolean} anim
17117         */
17118         "beforeexpand" : true,
17119         /**
17120         * @event beforecollapse
17121         * Fires before this node is collapsed, return false to cancel.
17122         * @param {Node} this This node
17123         * @param {Boolean} deep
17124         * @param {Boolean} anim
17125         */
17126         "beforecollapse" : true,
17127         /**
17128         * @event expand
17129         * Fires when this node is expanded
17130         * @param {Node} this This node
17131         */
17132         "expand" : true,
17133         /**
17134         * @event disabledchange
17135         * Fires when the disabled status of this node changes
17136         * @param {Node} this This node
17137         * @param {Boolean} disabled
17138         */
17139         "disabledchange" : true,
17140         /**
17141         * @event collapse
17142         * Fires when this node is collapsed
17143         * @param {Node} this This node
17144         */
17145         "collapse" : true,
17146         /**
17147         * @event beforeclick
17148         * Fires before click processing. Return false to cancel the default action.
17149         * @param {Node} this This node
17150         * @param {Roo.EventObject} e The event object
17151         */
17152         "beforeclick":true,
17153         /**
17154         * @event checkchange
17155         * Fires when a node with a checkbox's checked property changes
17156         * @param {Node} this This node
17157         * @param {Boolean} checked
17158         */
17159         "checkchange":true,
17160         /**
17161         * @event click
17162         * Fires when this node is clicked
17163         * @param {Node} this This node
17164         * @param {Roo.EventObject} e The event object
17165         */
17166         "click":true,
17167         /**
17168         * @event dblclick
17169         * Fires when this node is double clicked
17170         * @param {Node} this This node
17171         * @param {Roo.EventObject} e The event object
17172         */
17173         "dblclick":true,
17174         /**
17175         * @event contextmenu
17176         * Fires when this node is right clicked
17177         * @param {Node} this This node
17178         * @param {Roo.EventObject} e The event object
17179         */
17180         "contextmenu":true,
17181         /**
17182         * @event beforechildrenrendered
17183         * Fires right before the child nodes for this node are rendered
17184         * @param {Node} this This node
17185         */
17186         "beforechildrenrendered":true
17187     });
17188
17189     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17190
17191     /**
17192      * Read-only. The UI for this node
17193      * @type TreeNodeUI
17194      */
17195     this.ui = new uiClass(this);
17196 };
17197 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17198     preventHScroll: true,
17199     /**
17200      * Returns true if this node is expanded
17201      * @return {Boolean}
17202      */
17203     isExpanded : function(){
17204         return this.expanded;
17205     },
17206
17207     /**
17208      * Returns the UI object for this node
17209      * @return {TreeNodeUI}
17210      */
17211     getUI : function(){
17212         return this.ui;
17213     },
17214
17215     // private override
17216     setFirstChild : function(node){
17217         var of = this.firstChild;
17218         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17219         if(this.childrenRendered && of && node != of){
17220             of.renderIndent(true, true);
17221         }
17222         if(this.rendered){
17223             this.renderIndent(true, true);
17224         }
17225     },
17226
17227     // private override
17228     setLastChild : function(node){
17229         var ol = this.lastChild;
17230         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17231         if(this.childrenRendered && ol && node != ol){
17232             ol.renderIndent(true, true);
17233         }
17234         if(this.rendered){
17235             this.renderIndent(true, true);
17236         }
17237     },
17238
17239     // these methods are overridden to provide lazy rendering support
17240     // private override
17241     appendChild : function(){
17242         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17243         if(node && this.childrenRendered){
17244             node.render();
17245         }
17246         this.ui.updateExpandIcon();
17247         return node;
17248     },
17249
17250     // private override
17251     removeChild : function(node){
17252         this.ownerTree.getSelectionModel().unselect(node);
17253         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17254         // if it's been rendered remove dom node
17255         if(this.childrenRendered){
17256             node.ui.remove();
17257         }
17258         if(this.childNodes.length < 1){
17259             this.collapse(false, false);
17260         }else{
17261             this.ui.updateExpandIcon();
17262         }
17263         if(!this.firstChild) {
17264             this.childrenRendered = false;
17265         }
17266         return node;
17267     },
17268
17269     // private override
17270     insertBefore : function(node, refNode){
17271         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17272         if(newNode && refNode && this.childrenRendered){
17273             node.render();
17274         }
17275         this.ui.updateExpandIcon();
17276         return newNode;
17277     },
17278
17279     /**
17280      * Sets the text for this node
17281      * @param {String} text
17282      */
17283     setText : function(text){
17284         var oldText = this.text;
17285         this.text = text;
17286         this.attributes.text = text;
17287         if(this.rendered){ // event without subscribing
17288             this.ui.onTextChange(this, text, oldText);
17289         }
17290         this.fireEvent("textchange", this, text, oldText);
17291     },
17292
17293     /**
17294      * Triggers selection of this node
17295      */
17296     select : function(){
17297         this.getOwnerTree().getSelectionModel().select(this);
17298     },
17299
17300     /**
17301      * Triggers deselection of this node
17302      */
17303     unselect : function(){
17304         this.getOwnerTree().getSelectionModel().unselect(this);
17305     },
17306
17307     /**
17308      * Returns true if this node is selected
17309      * @return {Boolean}
17310      */
17311     isSelected : function(){
17312         return this.getOwnerTree().getSelectionModel().isSelected(this);
17313     },
17314
17315     /**
17316      * Expand this node.
17317      * @param {Boolean} deep (optional) True to expand all children as well
17318      * @param {Boolean} anim (optional) false to cancel the default animation
17319      * @param {Function} callback (optional) A callback to be called when
17320      * expanding this node completes (does not wait for deep expand to complete).
17321      * Called with 1 parameter, this node.
17322      */
17323     expand : function(deep, anim, callback){
17324         if(!this.expanded){
17325             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17326                 return;
17327             }
17328             if(!this.childrenRendered){
17329                 this.renderChildren();
17330             }
17331             this.expanded = true;
17332             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17333                 this.ui.animExpand(function(){
17334                     this.fireEvent("expand", this);
17335                     if(typeof callback == "function"){
17336                         callback(this);
17337                     }
17338                     if(deep === true){
17339                         this.expandChildNodes(true);
17340                     }
17341                 }.createDelegate(this));
17342                 return;
17343             }else{
17344                 this.ui.expand();
17345                 this.fireEvent("expand", this);
17346                 if(typeof callback == "function"){
17347                     callback(this);
17348                 }
17349             }
17350         }else{
17351            if(typeof callback == "function"){
17352                callback(this);
17353            }
17354         }
17355         if(deep === true){
17356             this.expandChildNodes(true);
17357         }
17358     },
17359
17360     isHiddenRoot : function(){
17361         return this.isRoot && !this.getOwnerTree().rootVisible;
17362     },
17363
17364     /**
17365      * Collapse this node.
17366      * @param {Boolean} deep (optional) True to collapse all children as well
17367      * @param {Boolean} anim (optional) false to cancel the default animation
17368      */
17369     collapse : function(deep, anim){
17370         if(this.expanded && !this.isHiddenRoot()){
17371             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17372                 return;
17373             }
17374             this.expanded = false;
17375             if((this.getOwnerTree().animate && anim !== false) || anim){
17376                 this.ui.animCollapse(function(){
17377                     this.fireEvent("collapse", this);
17378                     if(deep === true){
17379                         this.collapseChildNodes(true);
17380                     }
17381                 }.createDelegate(this));
17382                 return;
17383             }else{
17384                 this.ui.collapse();
17385                 this.fireEvent("collapse", this);
17386             }
17387         }
17388         if(deep === true){
17389             var cs = this.childNodes;
17390             for(var i = 0, len = cs.length; i < len; i++) {
17391                 cs[i].collapse(true, false);
17392             }
17393         }
17394     },
17395
17396     // private
17397     delayedExpand : function(delay){
17398         if(!this.expandProcId){
17399             this.expandProcId = this.expand.defer(delay, this);
17400         }
17401     },
17402
17403     // private
17404     cancelExpand : function(){
17405         if(this.expandProcId){
17406             clearTimeout(this.expandProcId);
17407         }
17408         this.expandProcId = false;
17409     },
17410
17411     /**
17412      * Toggles expanded/collapsed state of the node
17413      */
17414     toggle : function(){
17415         if(this.expanded){
17416             this.collapse();
17417         }else{
17418             this.expand();
17419         }
17420     },
17421
17422     /**
17423      * Ensures all parent nodes are expanded
17424      */
17425     ensureVisible : function(callback){
17426         var tree = this.getOwnerTree();
17427         tree.expandPath(this.parentNode.getPath(), false, function(){
17428             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17429             Roo.callback(callback);
17430         }.createDelegate(this));
17431     },
17432
17433     /**
17434      * Expand all child nodes
17435      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17436      */
17437     expandChildNodes : function(deep){
17438         var cs = this.childNodes;
17439         for(var i = 0, len = cs.length; i < len; i++) {
17440                 cs[i].expand(deep);
17441         }
17442     },
17443
17444     /**
17445      * Collapse all child nodes
17446      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17447      */
17448     collapseChildNodes : function(deep){
17449         var cs = this.childNodes;
17450         for(var i = 0, len = cs.length; i < len; i++) {
17451                 cs[i].collapse(deep);
17452         }
17453     },
17454
17455     /**
17456      * Disables this node
17457      */
17458     disable : function(){
17459         this.disabled = true;
17460         this.unselect();
17461         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17462             this.ui.onDisableChange(this, true);
17463         }
17464         this.fireEvent("disabledchange", this, true);
17465     },
17466
17467     /**
17468      * Enables this node
17469      */
17470     enable : function(){
17471         this.disabled = false;
17472         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17473             this.ui.onDisableChange(this, false);
17474         }
17475         this.fireEvent("disabledchange", this, false);
17476     },
17477
17478     // private
17479     renderChildren : function(suppressEvent){
17480         if(suppressEvent !== false){
17481             this.fireEvent("beforechildrenrendered", this);
17482         }
17483         var cs = this.childNodes;
17484         for(var i = 0, len = cs.length; i < len; i++){
17485             cs[i].render(true);
17486         }
17487         this.childrenRendered = true;
17488     },
17489
17490     // private
17491     sort : function(fn, scope){
17492         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17493         if(this.childrenRendered){
17494             var cs = this.childNodes;
17495             for(var i = 0, len = cs.length; i < len; i++){
17496                 cs[i].render(true);
17497             }
17498         }
17499     },
17500
17501     // private
17502     render : function(bulkRender){
17503         this.ui.render(bulkRender);
17504         if(!this.rendered){
17505             this.rendered = true;
17506             if(this.expanded){
17507                 this.expanded = false;
17508                 this.expand(false, false);
17509             }
17510         }
17511     },
17512
17513     // private
17514     renderIndent : function(deep, refresh){
17515         if(refresh){
17516             this.ui.childIndent = null;
17517         }
17518         this.ui.renderIndent();
17519         if(deep === true && this.childrenRendered){
17520             var cs = this.childNodes;
17521             for(var i = 0, len = cs.length; i < len; i++){
17522                 cs[i].renderIndent(true, refresh);
17523             }
17524         }
17525     }
17526 });/*
17527  * Based on:
17528  * Ext JS Library 1.1.1
17529  * Copyright(c) 2006-2007, Ext JS, LLC.
17530  *
17531  * Originally Released Under LGPL - original licence link has changed is not relivant.
17532  *
17533  * Fork - LGPL
17534  * <script type="text/javascript">
17535  */
17536  
17537 /**
17538  * @class Roo.tree.AsyncTreeNode
17539  * @extends Roo.tree.TreeNode
17540  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17541  * @constructor
17542  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17543  */
17544  Roo.tree.AsyncTreeNode = function(config){
17545     this.loaded = false;
17546     this.loading = false;
17547     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17548     /**
17549     * @event beforeload
17550     * Fires before this node is loaded, return false to cancel
17551     * @param {Node} this This node
17552     */
17553     this.addEvents({'beforeload':true, 'load': true});
17554     /**
17555     * @event load
17556     * Fires when this node is loaded
17557     * @param {Node} this This node
17558     */
17559     /**
17560      * The loader used by this node (defaults to using the tree's defined loader)
17561      * @type TreeLoader
17562      * @property loader
17563      */
17564 };
17565 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17566     expand : function(deep, anim, callback){
17567         if(this.loading){ // if an async load is already running, waiting til it's done
17568             var timer;
17569             var f = function(){
17570                 if(!this.loading){ // done loading
17571                     clearInterval(timer);
17572                     this.expand(deep, anim, callback);
17573                 }
17574             }.createDelegate(this);
17575             timer = setInterval(f, 200);
17576             return;
17577         }
17578         if(!this.loaded){
17579             if(this.fireEvent("beforeload", this) === false){
17580                 return;
17581             }
17582             this.loading = true;
17583             this.ui.beforeLoad(this);
17584             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17585             if(loader){
17586                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17587                 return;
17588             }
17589         }
17590         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17591     },
17592     
17593     /**
17594      * Returns true if this node is currently loading
17595      * @return {Boolean}
17596      */
17597     isLoading : function(){
17598         return this.loading;  
17599     },
17600     
17601     loadComplete : function(deep, anim, callback){
17602         this.loading = false;
17603         this.loaded = true;
17604         this.ui.afterLoad(this);
17605         this.fireEvent("load", this);
17606         this.expand(deep, anim, callback);
17607     },
17608     
17609     /**
17610      * Returns true if this node has been loaded
17611      * @return {Boolean}
17612      */
17613     isLoaded : function(){
17614         return this.loaded;
17615     },
17616     
17617     hasChildNodes : function(){
17618         if(!this.isLeaf() && !this.loaded){
17619             return true;
17620         }else{
17621             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17622         }
17623     },
17624
17625     /**
17626      * Trigger a reload for this node
17627      * @param {Function} callback
17628      */
17629     reload : function(callback){
17630         this.collapse(false, false);
17631         while(this.firstChild){
17632             this.removeChild(this.firstChild);
17633         }
17634         this.childrenRendered = false;
17635         this.loaded = false;
17636         if(this.isHiddenRoot()){
17637             this.expanded = false;
17638         }
17639         this.expand(false, false, callback);
17640     }
17641 });/*
17642  * Based on:
17643  * Ext JS Library 1.1.1
17644  * Copyright(c) 2006-2007, Ext JS, LLC.
17645  *
17646  * Originally Released Under LGPL - original licence link has changed is not relivant.
17647  *
17648  * Fork - LGPL
17649  * <script type="text/javascript">
17650  */
17651  
17652 /**
17653  * @class Roo.tree.TreeNodeUI
17654  * @constructor
17655  * @param {Object} node The node to render
17656  * The TreeNode UI implementation is separate from the
17657  * tree implementation. Unless you are customizing the tree UI,
17658  * you should never have to use this directly.
17659  */
17660 Roo.tree.TreeNodeUI = function(node){
17661     this.node = node;
17662     this.rendered = false;
17663     this.animating = false;
17664     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17665 };
17666
17667 Roo.tree.TreeNodeUI.prototype = {
17668     removeChild : function(node){
17669         if(this.rendered){
17670             this.ctNode.removeChild(node.ui.getEl());
17671         }
17672     },
17673
17674     beforeLoad : function(){
17675          this.addClass("x-tree-node-loading");
17676     },
17677
17678     afterLoad : function(){
17679          this.removeClass("x-tree-node-loading");
17680     },
17681
17682     onTextChange : function(node, text, oldText){
17683         if(this.rendered){
17684             this.textNode.innerHTML = text;
17685         }
17686     },
17687
17688     onDisableChange : function(node, state){
17689         this.disabled = state;
17690         if(state){
17691             this.addClass("x-tree-node-disabled");
17692         }else{
17693             this.removeClass("x-tree-node-disabled");
17694         }
17695     },
17696
17697     onSelectedChange : function(state){
17698         if(state){
17699             this.focus();
17700             this.addClass("x-tree-selected");
17701         }else{
17702             //this.blur();
17703             this.removeClass("x-tree-selected");
17704         }
17705     },
17706
17707     onMove : function(tree, node, oldParent, newParent, index, refNode){
17708         this.childIndent = null;
17709         if(this.rendered){
17710             var targetNode = newParent.ui.getContainer();
17711             if(!targetNode){//target not rendered
17712                 this.holder = document.createElement("div");
17713                 this.holder.appendChild(this.wrap);
17714                 return;
17715             }
17716             var insertBefore = refNode ? refNode.ui.getEl() : null;
17717             if(insertBefore){
17718                 targetNode.insertBefore(this.wrap, insertBefore);
17719             }else{
17720                 targetNode.appendChild(this.wrap);
17721             }
17722             this.node.renderIndent(true);
17723         }
17724     },
17725
17726     addClass : function(cls){
17727         if(this.elNode){
17728             Roo.fly(this.elNode).addClass(cls);
17729         }
17730     },
17731
17732     removeClass : function(cls){
17733         if(this.elNode){
17734             Roo.fly(this.elNode).removeClass(cls);
17735         }
17736     },
17737
17738     remove : function(){
17739         if(this.rendered){
17740             this.holder = document.createElement("div");
17741             this.holder.appendChild(this.wrap);
17742         }
17743     },
17744
17745     fireEvent : function(){
17746         return this.node.fireEvent.apply(this.node, arguments);
17747     },
17748
17749     initEvents : function(){
17750         this.node.on("move", this.onMove, this);
17751         var E = Roo.EventManager;
17752         var a = this.anchor;
17753
17754         var el = Roo.fly(a, '_treeui');
17755
17756         if(Roo.isOpera){ // opera render bug ignores the CSS
17757             el.setStyle("text-decoration", "none");
17758         }
17759
17760         el.on("click", this.onClick, this);
17761         el.on("dblclick", this.onDblClick, this);
17762
17763         if(this.checkbox){
17764             Roo.EventManager.on(this.checkbox,
17765                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17766         }
17767
17768         el.on("contextmenu", this.onContextMenu, this);
17769
17770         var icon = Roo.fly(this.iconNode);
17771         icon.on("click", this.onClick, this);
17772         icon.on("dblclick", this.onDblClick, this);
17773         icon.on("contextmenu", this.onContextMenu, this);
17774         E.on(this.ecNode, "click", this.ecClick, this, true);
17775
17776         if(this.node.disabled){
17777             this.addClass("x-tree-node-disabled");
17778         }
17779         if(this.node.hidden){
17780             this.addClass("x-tree-node-disabled");
17781         }
17782         var ot = this.node.getOwnerTree();
17783         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17784         if(dd && (!this.node.isRoot || ot.rootVisible)){
17785             Roo.dd.Registry.register(this.elNode, {
17786                 node: this.node,
17787                 handles: this.getDDHandles(),
17788                 isHandle: false
17789             });
17790         }
17791     },
17792
17793     getDDHandles : function(){
17794         return [this.iconNode, this.textNode];
17795     },
17796
17797     hide : function(){
17798         if(this.rendered){
17799             this.wrap.style.display = "none";
17800         }
17801     },
17802
17803     show : function(){
17804         if(this.rendered){
17805             this.wrap.style.display = "";
17806         }
17807     },
17808
17809     onContextMenu : function(e){
17810         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17811             e.preventDefault();
17812             this.focus();
17813             this.fireEvent("contextmenu", this.node, e);
17814         }
17815     },
17816
17817     onClick : function(e){
17818         if(this.dropping){
17819             e.stopEvent();
17820             return;
17821         }
17822         if(this.fireEvent("beforeclick", this.node, e) !== false){
17823             if(!this.disabled && this.node.attributes.href){
17824                 this.fireEvent("click", this.node, e);
17825                 return;
17826             }
17827             e.preventDefault();
17828             if(this.disabled){
17829                 return;
17830             }
17831
17832             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17833                 this.node.toggle();
17834             }
17835
17836             this.fireEvent("click", this.node, e);
17837         }else{
17838             e.stopEvent();
17839         }
17840     },
17841
17842     onDblClick : function(e){
17843         e.preventDefault();
17844         if(this.disabled){
17845             return;
17846         }
17847         if(this.checkbox){
17848             this.toggleCheck();
17849         }
17850         if(!this.animating && this.node.hasChildNodes()){
17851             this.node.toggle();
17852         }
17853         this.fireEvent("dblclick", this.node, e);
17854     },
17855
17856     onCheckChange : function(){
17857         var checked = this.checkbox.checked;
17858         this.node.attributes.checked = checked;
17859         this.fireEvent('checkchange', this.node, checked);
17860     },
17861
17862     ecClick : function(e){
17863         if(!this.animating && this.node.hasChildNodes()){
17864             this.node.toggle();
17865         }
17866     },
17867
17868     startDrop : function(){
17869         this.dropping = true;
17870     },
17871
17872     // delayed drop so the click event doesn't get fired on a drop
17873     endDrop : function(){
17874        setTimeout(function(){
17875            this.dropping = false;
17876        }.createDelegate(this), 50);
17877     },
17878
17879     expand : function(){
17880         this.updateExpandIcon();
17881         this.ctNode.style.display = "";
17882     },
17883
17884     focus : function(){
17885         if(!this.node.preventHScroll){
17886             try{this.anchor.focus();
17887             }catch(e){}
17888         }else if(!Roo.isIE){
17889             try{
17890                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17891                 var l = noscroll.scrollLeft;
17892                 this.anchor.focus();
17893                 noscroll.scrollLeft = l;
17894             }catch(e){}
17895         }
17896     },
17897
17898     toggleCheck : function(value){
17899         var cb = this.checkbox;
17900         if(cb){
17901             cb.checked = (value === undefined ? !cb.checked : value);
17902         }
17903     },
17904
17905     blur : function(){
17906         try{
17907             this.anchor.blur();
17908         }catch(e){}
17909     },
17910
17911     animExpand : function(callback){
17912         var ct = Roo.get(this.ctNode);
17913         ct.stopFx();
17914         if(!this.node.hasChildNodes()){
17915             this.updateExpandIcon();
17916             this.ctNode.style.display = "";
17917             Roo.callback(callback);
17918             return;
17919         }
17920         this.animating = true;
17921         this.updateExpandIcon();
17922
17923         ct.slideIn('t', {
17924            callback : function(){
17925                this.animating = false;
17926                Roo.callback(callback);
17927             },
17928             scope: this,
17929             duration: this.node.ownerTree.duration || .25
17930         });
17931     },
17932
17933     highlight : function(){
17934         var tree = this.node.getOwnerTree();
17935         Roo.fly(this.wrap).highlight(
17936             tree.hlColor || "C3DAF9",
17937             {endColor: tree.hlBaseColor}
17938         );
17939     },
17940
17941     collapse : function(){
17942         this.updateExpandIcon();
17943         this.ctNode.style.display = "none";
17944     },
17945
17946     animCollapse : function(callback){
17947         var ct = Roo.get(this.ctNode);
17948         ct.enableDisplayMode('block');
17949         ct.stopFx();
17950
17951         this.animating = true;
17952         this.updateExpandIcon();
17953
17954         ct.slideOut('t', {
17955             callback : function(){
17956                this.animating = false;
17957                Roo.callback(callback);
17958             },
17959             scope: this,
17960             duration: this.node.ownerTree.duration || .25
17961         });
17962     },
17963
17964     getContainer : function(){
17965         return this.ctNode;
17966     },
17967
17968     getEl : function(){
17969         return this.wrap;
17970     },
17971
17972     appendDDGhost : function(ghostNode){
17973         ghostNode.appendChild(this.elNode.cloneNode(true));
17974     },
17975
17976     getDDRepairXY : function(){
17977         return Roo.lib.Dom.getXY(this.iconNode);
17978     },
17979
17980     onRender : function(){
17981         this.render();
17982     },
17983
17984     render : function(bulkRender){
17985         var n = this.node, a = n.attributes;
17986         var targetNode = n.parentNode ?
17987               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17988
17989         if(!this.rendered){
17990             this.rendered = true;
17991
17992             this.renderElements(n, a, targetNode, bulkRender);
17993
17994             if(a.qtip){
17995                if(this.textNode.setAttributeNS){
17996                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17997                    if(a.qtipTitle){
17998                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17999                    }
18000                }else{
18001                    this.textNode.setAttribute("ext:qtip", a.qtip);
18002                    if(a.qtipTitle){
18003                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18004                    }
18005                }
18006             }else if(a.qtipCfg){
18007                 a.qtipCfg.target = Roo.id(this.textNode);
18008                 Roo.QuickTips.register(a.qtipCfg);
18009             }
18010             this.initEvents();
18011             if(!this.node.expanded){
18012                 this.updateExpandIcon();
18013             }
18014         }else{
18015             if(bulkRender === true) {
18016                 targetNode.appendChild(this.wrap);
18017             }
18018         }
18019     },
18020
18021     renderElements : function(n, a, targetNode, bulkRender){
18022         // add some indent caching, this helps performance when rendering a large tree
18023         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18024         var t = n.getOwnerTree();
18025         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18026         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18027         var cb = typeof a.checked == 'boolean';
18028         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18029         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18030             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18031             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18032             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18033             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18034             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18035              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18036                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18037             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18038             "</li>"];
18039
18040         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18041             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18042                                 n.nextSibling.ui.getEl(), buf.join(""));
18043         }else{
18044             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18045         }
18046
18047         this.elNode = this.wrap.childNodes[0];
18048         this.ctNode = this.wrap.childNodes[1];
18049         var cs = this.elNode.childNodes;
18050         this.indentNode = cs[0];
18051         this.ecNode = cs[1];
18052         this.iconNode = cs[2];
18053         var index = 3;
18054         if(cb){
18055             this.checkbox = cs[3];
18056             index++;
18057         }
18058         this.anchor = cs[index];
18059         this.textNode = cs[index].firstChild;
18060     },
18061
18062     getAnchor : function(){
18063         return this.anchor;
18064     },
18065
18066     getTextEl : function(){
18067         return this.textNode;
18068     },
18069
18070     getIconEl : function(){
18071         return this.iconNode;
18072     },
18073
18074     isChecked : function(){
18075         return this.checkbox ? this.checkbox.checked : false;
18076     },
18077
18078     updateExpandIcon : function(){
18079         if(this.rendered){
18080             var n = this.node, c1, c2;
18081             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18082             var hasChild = n.hasChildNodes();
18083             if(hasChild){
18084                 if(n.expanded){
18085                     cls += "-minus";
18086                     c1 = "x-tree-node-collapsed";
18087                     c2 = "x-tree-node-expanded";
18088                 }else{
18089                     cls += "-plus";
18090                     c1 = "x-tree-node-expanded";
18091                     c2 = "x-tree-node-collapsed";
18092                 }
18093                 if(this.wasLeaf){
18094                     this.removeClass("x-tree-node-leaf");
18095                     this.wasLeaf = false;
18096                 }
18097                 if(this.c1 != c1 || this.c2 != c2){
18098                     Roo.fly(this.elNode).replaceClass(c1, c2);
18099                     this.c1 = c1; this.c2 = c2;
18100                 }
18101             }else{
18102                 if(!this.wasLeaf){
18103                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18104                     delete this.c1;
18105                     delete this.c2;
18106                     this.wasLeaf = true;
18107                 }
18108             }
18109             var ecc = "x-tree-ec-icon "+cls;
18110             if(this.ecc != ecc){
18111                 this.ecNode.className = ecc;
18112                 this.ecc = ecc;
18113             }
18114         }
18115     },
18116
18117     getChildIndent : function(){
18118         if(!this.childIndent){
18119             var buf = [];
18120             var p = this.node;
18121             while(p){
18122                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18123                     if(!p.isLast()) {
18124                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18125                     } else {
18126                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18127                     }
18128                 }
18129                 p = p.parentNode;
18130             }
18131             this.childIndent = buf.join("");
18132         }
18133         return this.childIndent;
18134     },
18135
18136     renderIndent : function(){
18137         if(this.rendered){
18138             var indent = "";
18139             var p = this.node.parentNode;
18140             if(p){
18141                 indent = p.ui.getChildIndent();
18142             }
18143             if(this.indentMarkup != indent){ // don't rerender if not required
18144                 this.indentNode.innerHTML = indent;
18145                 this.indentMarkup = indent;
18146             }
18147             this.updateExpandIcon();
18148         }
18149     }
18150 };
18151
18152 Roo.tree.RootTreeNodeUI = function(){
18153     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18154 };
18155 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18156     render : function(){
18157         if(!this.rendered){
18158             var targetNode = this.node.ownerTree.innerCt.dom;
18159             this.node.expanded = true;
18160             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18161             this.wrap = this.ctNode = targetNode.firstChild;
18162         }
18163     },
18164     collapse : function(){
18165     },
18166     expand : function(){
18167     }
18168 });/*
18169  * Based on:
18170  * Ext JS Library 1.1.1
18171  * Copyright(c) 2006-2007, Ext JS, LLC.
18172  *
18173  * Originally Released Under LGPL - original licence link has changed is not relivant.
18174  *
18175  * Fork - LGPL
18176  * <script type="text/javascript">
18177  */
18178 /**
18179  * @class Roo.tree.TreeLoader
18180  * @extends Roo.util.Observable
18181  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18182  * nodes from a specified URL. The response must be a javascript Array definition
18183  * who's elements are node definition objects. eg:
18184  * <pre><code>
18185    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18186     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18187 </code></pre>
18188  * <br><br>
18189  * A server request is sent, and child nodes are loaded only when a node is expanded.
18190  * The loading node's id is passed to the server under the parameter name "node" to
18191  * enable the server to produce the correct child nodes.
18192  * <br><br>
18193  * To pass extra parameters, an event handler may be attached to the "beforeload"
18194  * event, and the parameters specified in the TreeLoader's baseParams property:
18195  * <pre><code>
18196     myTreeLoader.on("beforeload", function(treeLoader, node) {
18197         this.baseParams.category = node.attributes.category;
18198     }, this);
18199 </code></pre><
18200  * This would pass an HTTP parameter called "category" to the server containing
18201  * the value of the Node's "category" attribute.
18202  * @constructor
18203  * Creates a new Treeloader.
18204  * @param {Object} config A config object containing config properties.
18205  */
18206 Roo.tree.TreeLoader = function(config){
18207     this.baseParams = {};
18208     this.requestMethod = "POST";
18209     Roo.apply(this, config);
18210
18211     this.addEvents({
18212     
18213         /**
18214          * @event beforeload
18215          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18216          * @param {Object} This TreeLoader object.
18217          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18218          * @param {Object} callback The callback function specified in the {@link #load} call.
18219          */
18220         beforeload : true,
18221         /**
18222          * @event load
18223          * Fires when the node has been successfuly loaded.
18224          * @param {Object} This TreeLoader object.
18225          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18226          * @param {Object} response The response object containing the data from the server.
18227          */
18228         load : true,
18229         /**
18230          * @event loadexception
18231          * Fires if the network request failed.
18232          * @param {Object} This TreeLoader object.
18233          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18234          * @param {Object} response The response object containing the data from the server.
18235          */
18236         loadexception : true,
18237         /**
18238          * @event create
18239          * Fires before a node is created, enabling you to return custom Node types 
18240          * @param {Object} This TreeLoader object.
18241          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18242          */
18243         create : true
18244     });
18245
18246     Roo.tree.TreeLoader.superclass.constructor.call(this);
18247 };
18248
18249 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18250     /**
18251     * @cfg {String} dataUrl The URL from which to request a Json string which
18252     * specifies an array of node definition object representing the child nodes
18253     * to be loaded.
18254     */
18255     /**
18256     * @cfg {Object} baseParams (optional) An object containing properties which
18257     * specify HTTP parameters to be passed to each request for child nodes.
18258     */
18259     /**
18260     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18261     * created by this loader. If the attributes sent by the server have an attribute in this object,
18262     * they take priority.
18263     */
18264     /**
18265     * @cfg {Object} uiProviders (optional) An object containing properties which
18266     * 
18267     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18268     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18269     * <i>uiProvider</i> attribute of a returned child node is a string rather
18270     * than a reference to a TreeNodeUI implementation, this that string value
18271     * is used as a property name in the uiProviders object. You can define the provider named
18272     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18273     */
18274     uiProviders : {},
18275
18276     /**
18277     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18278     * child nodes before loading.
18279     */
18280     clearOnLoad : true,
18281
18282     /**
18283     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18284     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18285     * Grid query { data : [ .....] }
18286     */
18287     
18288     root : false,
18289      /**
18290     * @cfg {String} queryParam (optional) 
18291     * Name of the query as it will be passed on the querystring (defaults to 'node')
18292     * eg. the request will be ?node=[id]
18293     */
18294     
18295     
18296     queryParam: false,
18297     
18298     /**
18299      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18300      * This is called automatically when a node is expanded, but may be used to reload
18301      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18302      * @param {Roo.tree.TreeNode} node
18303      * @param {Function} callback
18304      */
18305     load : function(node, callback){
18306         if(this.clearOnLoad){
18307             while(node.firstChild){
18308                 node.removeChild(node.firstChild);
18309             }
18310         }
18311         if(node.attributes.children){ // preloaded json children
18312             var cs = node.attributes.children;
18313             for(var i = 0, len = cs.length; i < len; i++){
18314                 node.appendChild(this.createNode(cs[i]));
18315             }
18316             if(typeof callback == "function"){
18317                 callback();
18318             }
18319         }else if(this.dataUrl){
18320             this.requestData(node, callback);
18321         }
18322     },
18323
18324     getParams: function(node){
18325         var buf = [], bp = this.baseParams;
18326         for(var key in bp){
18327             if(typeof bp[key] != "function"){
18328                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18329             }
18330         }
18331         var n = this.queryParam === false ? 'node' : this.queryParam;
18332         buf.push(n + "=", encodeURIComponent(node.id));
18333         return buf.join("");
18334     },
18335
18336     requestData : function(node, callback){
18337         if(this.fireEvent("beforeload", this, node, callback) !== false){
18338             this.transId = Roo.Ajax.request({
18339                 method:this.requestMethod,
18340                 url: this.dataUrl||this.url,
18341                 success: this.handleResponse,
18342                 failure: this.handleFailure,
18343                 scope: this,
18344                 argument: {callback: callback, node: node},
18345                 params: this.getParams(node)
18346             });
18347         }else{
18348             // if the load is cancelled, make sure we notify
18349             // the node that we are done
18350             if(typeof callback == "function"){
18351                 callback();
18352             }
18353         }
18354     },
18355
18356     isLoading : function(){
18357         return this.transId ? true : false;
18358     },
18359
18360     abort : function(){
18361         if(this.isLoading()){
18362             Roo.Ajax.abort(this.transId);
18363         }
18364     },
18365
18366     // private
18367     createNode : function(attr){
18368         // apply baseAttrs, nice idea Corey!
18369         if(this.baseAttrs){
18370             Roo.applyIf(attr, this.baseAttrs);
18371         }
18372         if(this.applyLoader !== false){
18373             attr.loader = this;
18374         }
18375         // uiProvider = depreciated..
18376         
18377         if(typeof(attr.uiProvider) == 'string'){
18378            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18379                 /**  eval:var:attr */ eval(attr.uiProvider);
18380         }
18381         if(typeof(this.uiProviders['default']) != 'undefined') {
18382             attr.uiProvider = this.uiProviders['default'];
18383         }
18384         
18385         this.fireEvent('create', this, attr);
18386         
18387         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18388         return(attr.leaf ?
18389                         new Roo.tree.TreeNode(attr) :
18390                         new Roo.tree.AsyncTreeNode(attr));
18391     },
18392
18393     processResponse : function(response, node, callback){
18394         var json = response.responseText;
18395         try {
18396             
18397             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18398             if (this.root !== false) {
18399                 o = o[this.root];
18400             }
18401             
18402             for(var i = 0, len = o.length; i < len; i++){
18403                 var n = this.createNode(o[i]);
18404                 if(n){
18405                     node.appendChild(n);
18406                 }
18407             }
18408             if(typeof callback == "function"){
18409                 callback(this, node);
18410             }
18411         }catch(e){
18412             this.handleFailure(response);
18413         }
18414     },
18415
18416     handleResponse : function(response){
18417         this.transId = false;
18418         var a = response.argument;
18419         this.processResponse(response, a.node, a.callback);
18420         this.fireEvent("load", this, a.node, response);
18421     },
18422
18423     handleFailure : function(response){
18424         this.transId = false;
18425         var a = response.argument;
18426         this.fireEvent("loadexception", this, a.node, response);
18427         if(typeof a.callback == "function"){
18428             a.callback(this, a.node);
18429         }
18430     }
18431 });/*
18432  * Based on:
18433  * Ext JS Library 1.1.1
18434  * Copyright(c) 2006-2007, Ext JS, LLC.
18435  *
18436  * Originally Released Under LGPL - original licence link has changed is not relivant.
18437  *
18438  * Fork - LGPL
18439  * <script type="text/javascript">
18440  */
18441
18442 /**
18443 * @class Roo.tree.TreeFilter
18444 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18445 * @param {TreePanel} tree
18446 * @param {Object} config (optional)
18447  */
18448 Roo.tree.TreeFilter = function(tree, config){
18449     this.tree = tree;
18450     this.filtered = {};
18451     Roo.apply(this, config);
18452 };
18453
18454 Roo.tree.TreeFilter.prototype = {
18455     clearBlank:false,
18456     reverse:false,
18457     autoClear:false,
18458     remove:false,
18459
18460      /**
18461      * Filter the data by a specific attribute.
18462      * @param {String/RegExp} value Either string that the attribute value
18463      * should start with or a RegExp to test against the attribute
18464      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18465      * @param {TreeNode} startNode (optional) The node to start the filter at.
18466      */
18467     filter : function(value, attr, startNode){
18468         attr = attr || "text";
18469         var f;
18470         if(typeof value == "string"){
18471             var vlen = value.length;
18472             // auto clear empty filter
18473             if(vlen == 0 && this.clearBlank){
18474                 this.clear();
18475                 return;
18476             }
18477             value = value.toLowerCase();
18478             f = function(n){
18479                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18480             };
18481         }else if(value.exec){ // regex?
18482             f = function(n){
18483                 return value.test(n.attributes[attr]);
18484             };
18485         }else{
18486             throw 'Illegal filter type, must be string or regex';
18487         }
18488         this.filterBy(f, null, startNode);
18489         },
18490
18491     /**
18492      * Filter by a function. The passed function will be called with each
18493      * node in the tree (or from the startNode). If the function returns true, the node is kept
18494      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18495      * @param {Function} fn The filter function
18496      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18497      */
18498     filterBy : function(fn, scope, startNode){
18499         startNode = startNode || this.tree.root;
18500         if(this.autoClear){
18501             this.clear();
18502         }
18503         var af = this.filtered, rv = this.reverse;
18504         var f = function(n){
18505             if(n == startNode){
18506                 return true;
18507             }
18508             if(af[n.id]){
18509                 return false;
18510             }
18511             var m = fn.call(scope || n, n);
18512             if(!m || rv){
18513                 af[n.id] = n;
18514                 n.ui.hide();
18515                 return false;
18516             }
18517             return true;
18518         };
18519         startNode.cascade(f);
18520         if(this.remove){
18521            for(var id in af){
18522                if(typeof id != "function"){
18523                    var n = af[id];
18524                    if(n && n.parentNode){
18525                        n.parentNode.removeChild(n);
18526                    }
18527                }
18528            }
18529         }
18530     },
18531
18532     /**
18533      * Clears the current filter. Note: with the "remove" option
18534      * set a filter cannot be cleared.
18535      */
18536     clear : function(){
18537         var t = this.tree;
18538         var af = this.filtered;
18539         for(var id in af){
18540             if(typeof id != "function"){
18541                 var n = af[id];
18542                 if(n){
18543                     n.ui.show();
18544                 }
18545             }
18546         }
18547         this.filtered = {};
18548     }
18549 };
18550 /*
18551  * Based on:
18552  * Ext JS Library 1.1.1
18553  * Copyright(c) 2006-2007, Ext JS, LLC.
18554  *
18555  * Originally Released Under LGPL - original licence link has changed is not relivant.
18556  *
18557  * Fork - LGPL
18558  * <script type="text/javascript">
18559  */
18560  
18561
18562 /**
18563  * @class Roo.tree.TreeSorter
18564  * Provides sorting of nodes in a TreePanel
18565  * 
18566  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18567  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18568  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18569  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18570  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18571  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18572  * @constructor
18573  * @param {TreePanel} tree
18574  * @param {Object} config
18575  */
18576 Roo.tree.TreeSorter = function(tree, config){
18577     Roo.apply(this, config);
18578     tree.on("beforechildrenrendered", this.doSort, this);
18579     tree.on("append", this.updateSort, this);
18580     tree.on("insert", this.updateSort, this);
18581     
18582     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18583     var p = this.property || "text";
18584     var sortType = this.sortType;
18585     var fs = this.folderSort;
18586     var cs = this.caseSensitive === true;
18587     var leafAttr = this.leafAttr || 'leaf';
18588
18589     this.sortFn = function(n1, n2){
18590         if(fs){
18591             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18592                 return 1;
18593             }
18594             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18595                 return -1;
18596             }
18597         }
18598         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18599         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18600         if(v1 < v2){
18601                         return dsc ? +1 : -1;
18602                 }else if(v1 > v2){
18603                         return dsc ? -1 : +1;
18604         }else{
18605                 return 0;
18606         }
18607     };
18608 };
18609
18610 Roo.tree.TreeSorter.prototype = {
18611     doSort : function(node){
18612         node.sort(this.sortFn);
18613     },
18614     
18615     compareNodes : function(n1, n2){
18616         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18617     },
18618     
18619     updateSort : function(tree, node){
18620         if(node.childrenRendered){
18621             this.doSort.defer(1, this, [node]);
18622         }
18623     }
18624 };/*
18625  * Based on:
18626  * Ext JS Library 1.1.1
18627  * Copyright(c) 2006-2007, Ext JS, LLC.
18628  *
18629  * Originally Released Under LGPL - original licence link has changed is not relivant.
18630  *
18631  * Fork - LGPL
18632  * <script type="text/javascript">
18633  */
18634
18635 if(Roo.dd.DropZone){
18636     
18637 Roo.tree.TreeDropZone = function(tree, config){
18638     this.allowParentInsert = false;
18639     this.allowContainerDrop = false;
18640     this.appendOnly = false;
18641     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18642     this.tree = tree;
18643     this.lastInsertClass = "x-tree-no-status";
18644     this.dragOverData = {};
18645 };
18646
18647 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18648     ddGroup : "TreeDD",
18649     
18650     expandDelay : 1000,
18651     
18652     expandNode : function(node){
18653         if(node.hasChildNodes() && !node.isExpanded()){
18654             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18655         }
18656     },
18657     
18658     queueExpand : function(node){
18659         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18660     },
18661     
18662     cancelExpand : function(){
18663         if(this.expandProcId){
18664             clearTimeout(this.expandProcId);
18665             this.expandProcId = false;
18666         }
18667     },
18668     
18669     isValidDropPoint : function(n, pt, dd, e, data){
18670         if(!n || !data){ return false; }
18671         var targetNode = n.node;
18672         var dropNode = data.node;
18673         // default drop rules
18674         if(!(targetNode && targetNode.isTarget && pt)){
18675             return false;
18676         }
18677         if(pt == "append" && targetNode.allowChildren === false){
18678             return false;
18679         }
18680         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18681             return false;
18682         }
18683         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18684             return false;
18685         }
18686         // reuse the object
18687         var overEvent = this.dragOverData;
18688         overEvent.tree = this.tree;
18689         overEvent.target = targetNode;
18690         overEvent.data = data;
18691         overEvent.point = pt;
18692         overEvent.source = dd;
18693         overEvent.rawEvent = e;
18694         overEvent.dropNode = dropNode;
18695         overEvent.cancel = false;  
18696         var result = this.tree.fireEvent("nodedragover", overEvent);
18697         return overEvent.cancel === false && result !== false;
18698     },
18699     
18700     getDropPoint : function(e, n, dd){
18701         var tn = n.node;
18702         if(tn.isRoot){
18703             return tn.allowChildren !== false ? "append" : false; // always append for root
18704         }
18705         var dragEl = n.ddel;
18706         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18707         var y = Roo.lib.Event.getPageY(e);
18708         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18709         
18710         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18711         var noAppend = tn.allowChildren === false;
18712         if(this.appendOnly || tn.parentNode.allowChildren === false){
18713             return noAppend ? false : "append";
18714         }
18715         var noBelow = false;
18716         if(!this.allowParentInsert){
18717             noBelow = tn.hasChildNodes() && tn.isExpanded();
18718         }
18719         var q = (b - t) / (noAppend ? 2 : 3);
18720         if(y >= t && y < (t + q)){
18721             return "above";
18722         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18723             return "below";
18724         }else{
18725             return "append";
18726         }
18727     },
18728     
18729     onNodeEnter : function(n, dd, e, data){
18730         this.cancelExpand();
18731     },
18732     
18733     onNodeOver : function(n, dd, e, data){
18734         var pt = this.getDropPoint(e, n, dd);
18735         var node = n.node;
18736         
18737         // auto node expand check
18738         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18739             this.queueExpand(node);
18740         }else if(pt != "append"){
18741             this.cancelExpand();
18742         }
18743         
18744         // set the insert point style on the target node
18745         var returnCls = this.dropNotAllowed;
18746         if(this.isValidDropPoint(n, pt, dd, e, data)){
18747            if(pt){
18748                var el = n.ddel;
18749                var cls;
18750                if(pt == "above"){
18751                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18752                    cls = "x-tree-drag-insert-above";
18753                }else if(pt == "below"){
18754                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18755                    cls = "x-tree-drag-insert-below";
18756                }else{
18757                    returnCls = "x-tree-drop-ok-append";
18758                    cls = "x-tree-drag-append";
18759                }
18760                if(this.lastInsertClass != cls){
18761                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18762                    this.lastInsertClass = cls;
18763                }
18764            }
18765        }
18766        return returnCls;
18767     },
18768     
18769     onNodeOut : function(n, dd, e, data){
18770         this.cancelExpand();
18771         this.removeDropIndicators(n);
18772     },
18773     
18774     onNodeDrop : function(n, dd, e, data){
18775         var point = this.getDropPoint(e, n, dd);
18776         var targetNode = n.node;
18777         targetNode.ui.startDrop();
18778         if(!this.isValidDropPoint(n, point, dd, e, data)){
18779             targetNode.ui.endDrop();
18780             return false;
18781         }
18782         // first try to find the drop node
18783         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18784         var dropEvent = {
18785             tree : this.tree,
18786             target: targetNode,
18787             data: data,
18788             point: point,
18789             source: dd,
18790             rawEvent: e,
18791             dropNode: dropNode,
18792             cancel: !dropNode   
18793         };
18794         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18795         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18796             targetNode.ui.endDrop();
18797             return false;
18798         }
18799         // allow target changing
18800         targetNode = dropEvent.target;
18801         if(point == "append" && !targetNode.isExpanded()){
18802             targetNode.expand(false, null, function(){
18803                 this.completeDrop(dropEvent);
18804             }.createDelegate(this));
18805         }else{
18806             this.completeDrop(dropEvent);
18807         }
18808         return true;
18809     },
18810     
18811     completeDrop : function(de){
18812         var ns = de.dropNode, p = de.point, t = de.target;
18813         if(!(ns instanceof Array)){
18814             ns = [ns];
18815         }
18816         var n;
18817         for(var i = 0, len = ns.length; i < len; i++){
18818             n = ns[i];
18819             if(p == "above"){
18820                 t.parentNode.insertBefore(n, t);
18821             }else if(p == "below"){
18822                 t.parentNode.insertBefore(n, t.nextSibling);
18823             }else{
18824                 t.appendChild(n);
18825             }
18826         }
18827         n.ui.focus();
18828         if(this.tree.hlDrop){
18829             n.ui.highlight();
18830         }
18831         t.ui.endDrop();
18832         this.tree.fireEvent("nodedrop", de);
18833     },
18834     
18835     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18836         if(this.tree.hlDrop){
18837             dropNode.ui.focus();
18838             dropNode.ui.highlight();
18839         }
18840         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18841     },
18842     
18843     getTree : function(){
18844         return this.tree;
18845     },
18846     
18847     removeDropIndicators : function(n){
18848         if(n && n.ddel){
18849             var el = n.ddel;
18850             Roo.fly(el).removeClass([
18851                     "x-tree-drag-insert-above",
18852                     "x-tree-drag-insert-below",
18853                     "x-tree-drag-append"]);
18854             this.lastInsertClass = "_noclass";
18855         }
18856     },
18857     
18858     beforeDragDrop : function(target, e, id){
18859         this.cancelExpand();
18860         return true;
18861     },
18862     
18863     afterRepair : function(data){
18864         if(data && Roo.enableFx){
18865             data.node.ui.highlight();
18866         }
18867         this.hideProxy();
18868     }    
18869 });
18870
18871 }
18872 /*
18873  * Based on:
18874  * Ext JS Library 1.1.1
18875  * Copyright(c) 2006-2007, Ext JS, LLC.
18876  *
18877  * Originally Released Under LGPL - original licence link has changed is not relivant.
18878  *
18879  * Fork - LGPL
18880  * <script type="text/javascript">
18881  */
18882  
18883
18884 if(Roo.dd.DragZone){
18885 Roo.tree.TreeDragZone = function(tree, config){
18886     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18887     this.tree = tree;
18888 };
18889
18890 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18891     ddGroup : "TreeDD",
18892     
18893     onBeforeDrag : function(data, e){
18894         var n = data.node;
18895         return n && n.draggable && !n.disabled;
18896     },
18897     
18898     onInitDrag : function(e){
18899         var data = this.dragData;
18900         this.tree.getSelectionModel().select(data.node);
18901         this.proxy.update("");
18902         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18903         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18904     },
18905     
18906     getRepairXY : function(e, data){
18907         return data.node.ui.getDDRepairXY();
18908     },
18909     
18910     onEndDrag : function(data, e){
18911         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18912     },
18913     
18914     onValidDrop : function(dd, e, id){
18915         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18916         this.hideProxy();
18917     },
18918     
18919     beforeInvalidDrop : function(e, id){
18920         // this scrolls the original position back into view
18921         var sm = this.tree.getSelectionModel();
18922         sm.clearSelections();
18923         sm.select(this.dragData.node);
18924     }
18925 });
18926 }/*
18927  * Based on:
18928  * Ext JS Library 1.1.1
18929  * Copyright(c) 2006-2007, Ext JS, LLC.
18930  *
18931  * Originally Released Under LGPL - original licence link has changed is not relivant.
18932  *
18933  * Fork - LGPL
18934  * <script type="text/javascript">
18935  */
18936 /**
18937  * @class Roo.tree.TreeEditor
18938  * @extends Roo.Editor
18939  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18940  * as the editor field.
18941  * @constructor
18942  * @param {TreePanel} tree
18943  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18944  */
18945 Roo.tree.TreeEditor = function(tree, config){
18946     config = config || {};
18947     var field = config.events ? config : new Roo.form.TextField(config);
18948     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18949
18950     this.tree = tree;
18951
18952     tree.on('beforeclick', this.beforeNodeClick, this);
18953     tree.getTreeEl().on('mousedown', this.hide, this);
18954     this.on('complete', this.updateNode, this);
18955     this.on('beforestartedit', this.fitToTree, this);
18956     this.on('startedit', this.bindScroll, this, {delay:10});
18957     this.on('specialkey', this.onSpecialKey, this);
18958 };
18959
18960 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18961     /**
18962      * @cfg {String} alignment
18963      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18964      */
18965     alignment: "l-l",
18966     // inherit
18967     autoSize: false,
18968     /**
18969      * @cfg {Boolean} hideEl
18970      * True to hide the bound element while the editor is displayed (defaults to false)
18971      */
18972     hideEl : false,
18973     /**
18974      * @cfg {String} cls
18975      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18976      */
18977     cls: "x-small-editor x-tree-editor",
18978     /**
18979      * @cfg {Boolean} shim
18980      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18981      */
18982     shim:false,
18983     // inherit
18984     shadow:"frame",
18985     /**
18986      * @cfg {Number} maxWidth
18987      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18988      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18989      * scroll and client offsets into account prior to each edit.
18990      */
18991     maxWidth: 250,
18992
18993     editDelay : 350,
18994
18995     // private
18996     fitToTree : function(ed, el){
18997         var td = this.tree.getTreeEl().dom, nd = el.dom;
18998         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18999             td.scrollLeft = nd.offsetLeft;
19000         }
19001         var w = Math.min(
19002                 this.maxWidth,
19003                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19004         this.setSize(w, '');
19005     },
19006
19007     // private
19008     triggerEdit : function(node){
19009         this.completeEdit();
19010         this.editNode = node;
19011         this.startEdit(node.ui.textNode, node.text);
19012     },
19013
19014     // private
19015     bindScroll : function(){
19016         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19017     },
19018
19019     // private
19020     beforeNodeClick : function(node, e){
19021         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19022         this.lastClick = new Date();
19023         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19024             e.stopEvent();
19025             this.triggerEdit(node);
19026             return false;
19027         }
19028     },
19029
19030     // private
19031     updateNode : function(ed, value){
19032         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19033         this.editNode.setText(value);
19034     },
19035
19036     // private
19037     onHide : function(){
19038         Roo.tree.TreeEditor.superclass.onHide.call(this);
19039         if(this.editNode){
19040             this.editNode.ui.focus();
19041         }
19042     },
19043
19044     // private
19045     onSpecialKey : function(field, e){
19046         var k = e.getKey();
19047         if(k == e.ESC){
19048             e.stopEvent();
19049             this.cancelEdit();
19050         }else if(k == e.ENTER && !e.hasModifier()){
19051             e.stopEvent();
19052             this.completeEdit();
19053         }
19054     }
19055 });//<Script type="text/javascript">
19056 /*
19057  * Based on:
19058  * Ext JS Library 1.1.1
19059  * Copyright(c) 2006-2007, Ext JS, LLC.
19060  *
19061  * Originally Released Under LGPL - original licence link has changed is not relivant.
19062  *
19063  * Fork - LGPL
19064  * <script type="text/javascript">
19065  */
19066  
19067 /**
19068  * Not documented??? - probably should be...
19069  */
19070
19071 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19072     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19073     
19074     renderElements : function(n, a, targetNode, bulkRender){
19075         //consel.log("renderElements?");
19076         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19077
19078         var t = n.getOwnerTree();
19079         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19080         
19081         var cols = t.columns;
19082         var bw = t.borderWidth;
19083         var c = cols[0];
19084         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19085          var cb = typeof a.checked == "boolean";
19086         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19087         var colcls = 'x-t-' + tid + '-c0';
19088         var buf = [
19089             '<li class="x-tree-node">',
19090             
19091                 
19092                 '<div class="x-tree-node-el ', a.cls,'">',
19093                     // extran...
19094                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19095                 
19096                 
19097                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19098                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19099                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19100                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19101                            (a.iconCls ? ' '+a.iconCls : ''),
19102                            '" unselectable="on" />',
19103                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19104                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19105                              
19106                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19107                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19108                             '<span unselectable="on" qtip="' + tx + '">',
19109                              tx,
19110                              '</span></a>' ,
19111                     '</div>',
19112                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19113                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19114                  ];
19115         for(var i = 1, len = cols.length; i < len; i++){
19116             c = cols[i];
19117             colcls = 'x-t-' + tid + '-c' +i;
19118             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19119             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19120                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19121                       "</div>");
19122          }
19123          
19124          buf.push(
19125             '</a>',
19126             '<div class="x-clear"></div></div>',
19127             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19128             "</li>");
19129         
19130         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19131             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19132                                 n.nextSibling.ui.getEl(), buf.join(""));
19133         }else{
19134             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19135         }
19136         var el = this.wrap.firstChild;
19137         this.elRow = el;
19138         this.elNode = el.firstChild;
19139         this.ranchor = el.childNodes[1];
19140         this.ctNode = this.wrap.childNodes[1];
19141         var cs = el.firstChild.childNodes;
19142         this.indentNode = cs[0];
19143         this.ecNode = cs[1];
19144         this.iconNode = cs[2];
19145         var index = 3;
19146         if(cb){
19147             this.checkbox = cs[3];
19148             index++;
19149         }
19150         this.anchor = cs[index];
19151         
19152         this.textNode = cs[index].firstChild;
19153         
19154         //el.on("click", this.onClick, this);
19155         //el.on("dblclick", this.onDblClick, this);
19156         
19157         
19158        // console.log(this);
19159     },
19160     initEvents : function(){
19161         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19162         
19163             
19164         var a = this.ranchor;
19165
19166         var el = Roo.get(a);
19167
19168         if(Roo.isOpera){ // opera render bug ignores the CSS
19169             el.setStyle("text-decoration", "none");
19170         }
19171
19172         el.on("click", this.onClick, this);
19173         el.on("dblclick", this.onDblClick, this);
19174         el.on("contextmenu", this.onContextMenu, this);
19175         
19176     },
19177     
19178     /*onSelectedChange : function(state){
19179         if(state){
19180             this.focus();
19181             this.addClass("x-tree-selected");
19182         }else{
19183             //this.blur();
19184             this.removeClass("x-tree-selected");
19185         }
19186     },*/
19187     addClass : function(cls){
19188         if(this.elRow){
19189             Roo.fly(this.elRow).addClass(cls);
19190         }
19191         
19192     },
19193     
19194     
19195     removeClass : function(cls){
19196         if(this.elRow){
19197             Roo.fly(this.elRow).removeClass(cls);
19198         }
19199     }
19200
19201     
19202     
19203 });//<Script type="text/javascript">
19204
19205 /*
19206  * Based on:
19207  * Ext JS Library 1.1.1
19208  * Copyright(c) 2006-2007, Ext JS, LLC.
19209  *
19210  * Originally Released Under LGPL - original licence link has changed is not relivant.
19211  *
19212  * Fork - LGPL
19213  * <script type="text/javascript">
19214  */
19215  
19216
19217 /**
19218  * @class Roo.tree.ColumnTree
19219  * @extends Roo.data.TreePanel
19220  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19221  * @cfg {int} borderWidth  compined right/left border allowance
19222  * @constructor
19223  * @param {String/HTMLElement/Element} el The container element
19224  * @param {Object} config
19225  */
19226 Roo.tree.ColumnTree =  function(el, config)
19227 {
19228    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19229    this.addEvents({
19230         /**
19231         * @event resize
19232         * Fire this event on a container when it resizes
19233         * @param {int} w Width
19234         * @param {int} h Height
19235         */
19236        "resize" : true
19237     });
19238     this.on('resize', this.onResize, this);
19239 };
19240
19241 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19242     //lines:false,
19243     
19244     
19245     borderWidth: Roo.isBorderBox ? 0 : 2, 
19246     headEls : false,
19247     
19248     render : function(){
19249         // add the header.....
19250        
19251         Roo.tree.ColumnTree.superclass.render.apply(this);
19252         
19253         this.el.addClass('x-column-tree');
19254         
19255         this.headers = this.el.createChild(
19256             {cls:'x-tree-headers'},this.innerCt.dom);
19257    
19258         var cols = this.columns, c;
19259         var totalWidth = 0;
19260         this.headEls = [];
19261         var  len = cols.length;
19262         for(var i = 0; i < len; i++){
19263              c = cols[i];
19264              totalWidth += c.width;
19265             this.headEls.push(this.headers.createChild({
19266                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19267                  cn: {
19268                      cls:'x-tree-hd-text',
19269                      html: c.header
19270                  },
19271                  style:'width:'+(c.width-this.borderWidth)+'px;'
19272              }));
19273         }
19274         this.headers.createChild({cls:'x-clear'});
19275         // prevent floats from wrapping when clipped
19276         this.headers.setWidth(totalWidth);
19277         //this.innerCt.setWidth(totalWidth);
19278         this.innerCt.setStyle({ overflow: 'auto' });
19279         this.onResize(this.width, this.height);
19280              
19281         
19282     },
19283     onResize : function(w,h)
19284     {
19285         this.height = h;
19286         this.width = w;
19287         // resize cols..
19288         this.innerCt.setWidth(this.width);
19289         this.innerCt.setHeight(this.height-20);
19290         
19291         // headers...
19292         var cols = this.columns, c;
19293         var totalWidth = 0;
19294         var expEl = false;
19295         var len = cols.length;
19296         for(var i = 0; i < len; i++){
19297             c = cols[i];
19298             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19299                 // it's the expander..
19300                 expEl  = this.headEls[i];
19301                 continue;
19302             }
19303             totalWidth += c.width;
19304             
19305         }
19306         if (expEl) {
19307             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19308         }
19309         this.headers.setWidth(w-20);
19310
19311         
19312         
19313         
19314     }
19315 });
19316 /*
19317  * Based on:
19318  * Ext JS Library 1.1.1
19319  * Copyright(c) 2006-2007, Ext JS, LLC.
19320  *
19321  * Originally Released Under LGPL - original licence link has changed is not relivant.
19322  *
19323  * Fork - LGPL
19324  * <script type="text/javascript">
19325  */
19326  
19327 /**
19328  * @class Roo.menu.Menu
19329  * @extends Roo.util.Observable
19330  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19331  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19332  * @constructor
19333  * Creates a new Menu
19334  * @param {Object} config Configuration options
19335  */
19336 Roo.menu.Menu = function(config){
19337     Roo.apply(this, config);
19338     this.id = this.id || Roo.id();
19339     this.addEvents({
19340         /**
19341          * @event beforeshow
19342          * Fires before this menu is displayed
19343          * @param {Roo.menu.Menu} this
19344          */
19345         beforeshow : true,
19346         /**
19347          * @event beforehide
19348          * Fires before this menu is hidden
19349          * @param {Roo.menu.Menu} this
19350          */
19351         beforehide : true,
19352         /**
19353          * @event show
19354          * Fires after this menu is displayed
19355          * @param {Roo.menu.Menu} this
19356          */
19357         show : true,
19358         /**
19359          * @event hide
19360          * Fires after this menu is hidden
19361          * @param {Roo.menu.Menu} this
19362          */
19363         hide : true,
19364         /**
19365          * @event click
19366          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19367          * @param {Roo.menu.Menu} this
19368          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19369          * @param {Roo.EventObject} e
19370          */
19371         click : true,
19372         /**
19373          * @event mouseover
19374          * Fires when the mouse is hovering over this menu
19375          * @param {Roo.menu.Menu} this
19376          * @param {Roo.EventObject} e
19377          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19378          */
19379         mouseover : true,
19380         /**
19381          * @event mouseout
19382          * Fires when the mouse exits this menu
19383          * @param {Roo.menu.Menu} this
19384          * @param {Roo.EventObject} e
19385          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19386          */
19387         mouseout : true,
19388         /**
19389          * @event itemclick
19390          * Fires when a menu item contained in this menu is clicked
19391          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19392          * @param {Roo.EventObject} e
19393          */
19394         itemclick: true
19395     });
19396     if (this.registerMenu) {
19397         Roo.menu.MenuMgr.register(this);
19398     }
19399     
19400     var mis = this.items;
19401     this.items = new Roo.util.MixedCollection();
19402     if(mis){
19403         this.add.apply(this, mis);
19404     }
19405 };
19406
19407 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19408     /**
19409      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19410      */
19411     minWidth : 120,
19412     /**
19413      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19414      * for bottom-right shadow (defaults to "sides")
19415      */
19416     shadow : "sides",
19417     /**
19418      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19419      * this menu (defaults to "tl-tr?")
19420      */
19421     subMenuAlign : "tl-tr?",
19422     /**
19423      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19424      * relative to its element of origin (defaults to "tl-bl?")
19425      */
19426     defaultAlign : "tl-bl?",
19427     /**
19428      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19429      */
19430     allowOtherMenus : false,
19431     /**
19432      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19433      */
19434     registerMenu : true,
19435
19436     hidden:true,
19437
19438     // private
19439     render : function(){
19440         if(this.el){
19441             return;
19442         }
19443         var el = this.el = new Roo.Layer({
19444             cls: "x-menu",
19445             shadow:this.shadow,
19446             constrain: false,
19447             parentEl: this.parentEl || document.body,
19448             zindex:15000
19449         });
19450
19451         this.keyNav = new Roo.menu.MenuNav(this);
19452
19453         if(this.plain){
19454             el.addClass("x-menu-plain");
19455         }
19456         if(this.cls){
19457             el.addClass(this.cls);
19458         }
19459         // generic focus element
19460         this.focusEl = el.createChild({
19461             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19462         });
19463         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19464         ul.on("click", this.onClick, this);
19465         ul.on("mouseover", this.onMouseOver, this);
19466         ul.on("mouseout", this.onMouseOut, this);
19467         this.items.each(function(item){
19468             var li = document.createElement("li");
19469             li.className = "x-menu-list-item";
19470             ul.dom.appendChild(li);
19471             item.render(li, this);
19472         }, this);
19473         this.ul = ul;
19474         this.autoWidth();
19475     },
19476
19477     // private
19478     autoWidth : function(){
19479         var el = this.el, ul = this.ul;
19480         if(!el){
19481             return;
19482         }
19483         var w = this.width;
19484         if(w){
19485             el.setWidth(w);
19486         }else if(Roo.isIE){
19487             el.setWidth(this.minWidth);
19488             var t = el.dom.offsetWidth; // force recalc
19489             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19490         }
19491     },
19492
19493     // private
19494     delayAutoWidth : function(){
19495         if(this.rendered){
19496             if(!this.awTask){
19497                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19498             }
19499             this.awTask.delay(20);
19500         }
19501     },
19502
19503     // private
19504     findTargetItem : function(e){
19505         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19506         if(t && t.menuItemId){
19507             return this.items.get(t.menuItemId);
19508         }
19509     },
19510
19511     // private
19512     onClick : function(e){
19513         var t;
19514         if(t = this.findTargetItem(e)){
19515             t.onClick(e);
19516             this.fireEvent("click", this, t, e);
19517         }
19518     },
19519
19520     // private
19521     setActiveItem : function(item, autoExpand){
19522         if(item != this.activeItem){
19523             if(this.activeItem){
19524                 this.activeItem.deactivate();
19525             }
19526             this.activeItem = item;
19527             item.activate(autoExpand);
19528         }else if(autoExpand){
19529             item.expandMenu();
19530         }
19531     },
19532
19533     // private
19534     tryActivate : function(start, step){
19535         var items = this.items;
19536         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19537             var item = items.get(i);
19538             if(!item.disabled && item.canActivate){
19539                 this.setActiveItem(item, false);
19540                 return item;
19541             }
19542         }
19543         return false;
19544     },
19545
19546     // private
19547     onMouseOver : function(e){
19548         var t;
19549         if(t = this.findTargetItem(e)){
19550             if(t.canActivate && !t.disabled){
19551                 this.setActiveItem(t, true);
19552             }
19553         }
19554         this.fireEvent("mouseover", this, e, t);
19555     },
19556
19557     // private
19558     onMouseOut : function(e){
19559         var t;
19560         if(t = this.findTargetItem(e)){
19561             if(t == this.activeItem && t.shouldDeactivate(e)){
19562                 this.activeItem.deactivate();
19563                 delete this.activeItem;
19564             }
19565         }
19566         this.fireEvent("mouseout", this, e, t);
19567     },
19568
19569     /**
19570      * Read-only.  Returns true if the menu is currently displayed, else false.
19571      * @type Boolean
19572      */
19573     isVisible : function(){
19574         return this.el && !this.hidden;
19575     },
19576
19577     /**
19578      * Displays this menu relative to another element
19579      * @param {String/HTMLElement/Roo.Element} element The element to align to
19580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19581      * the element (defaults to this.defaultAlign)
19582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19583      */
19584     show : function(el, pos, parentMenu){
19585         this.parentMenu = parentMenu;
19586         if(!this.el){
19587             this.render();
19588         }
19589         this.fireEvent("beforeshow", this);
19590         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19591     },
19592
19593     /**
19594      * Displays this menu at a specific xy position
19595      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19597      */
19598     showAt : function(xy, parentMenu, /* private: */_e){
19599         this.parentMenu = parentMenu;
19600         if(!this.el){
19601             this.render();
19602         }
19603         if(_e !== false){
19604             this.fireEvent("beforeshow", this);
19605             xy = this.el.adjustForConstraints(xy);
19606         }
19607         this.el.setXY(xy);
19608         this.el.show();
19609         this.hidden = false;
19610         this.focus();
19611         this.fireEvent("show", this);
19612     },
19613
19614     focus : function(){
19615         if(!this.hidden){
19616             this.doFocus.defer(50, this);
19617         }
19618     },
19619
19620     doFocus : function(){
19621         if(!this.hidden){
19622             this.focusEl.focus();
19623         }
19624     },
19625
19626     /**
19627      * Hides this menu and optionally all parent menus
19628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19629      */
19630     hide : function(deep){
19631         if(this.el && this.isVisible()){
19632             this.fireEvent("beforehide", this);
19633             if(this.activeItem){
19634                 this.activeItem.deactivate();
19635                 this.activeItem = null;
19636             }
19637             this.el.hide();
19638             this.hidden = true;
19639             this.fireEvent("hide", this);
19640         }
19641         if(deep === true && this.parentMenu){
19642             this.parentMenu.hide(true);
19643         }
19644     },
19645
19646     /**
19647      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19648      * Any of the following are valid:
19649      * <ul>
19650      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19651      * <li>An HTMLElement object which will be converted to a menu item</li>
19652      * <li>A menu item config object that will be created as a new menu item</li>
19653      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19654      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19655      * </ul>
19656      * Usage:
19657      * <pre><code>
19658 // Create the menu
19659 var menu = new Roo.menu.Menu();
19660
19661 // Create a menu item to add by reference
19662 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19663
19664 // Add a bunch of items at once using different methods.
19665 // Only the last item added will be returned.
19666 var item = menu.add(
19667     menuItem,                // add existing item by ref
19668     'Dynamic Item',          // new TextItem
19669     '-',                     // new separator
19670     { text: 'Config Item' }  // new item by config
19671 );
19672 </code></pre>
19673      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19674      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19675      */
19676     add : function(){
19677         var a = arguments, l = a.length, item;
19678         for(var i = 0; i < l; i++){
19679             var el = a[i];
19680             if ((typeof(el) == "object") && el.xtype && el.xns) {
19681                 el = Roo.factory(el, Roo.menu);
19682             }
19683             
19684             if(el.render){ // some kind of Item
19685                 item = this.addItem(el);
19686             }else if(typeof el == "string"){ // string
19687                 if(el == "separator" || el == "-"){
19688                     item = this.addSeparator();
19689                 }else{
19690                     item = this.addText(el);
19691                 }
19692             }else if(el.tagName || el.el){ // element
19693                 item = this.addElement(el);
19694             }else if(typeof el == "object"){ // must be menu item config?
19695                 item = this.addMenuItem(el);
19696             }
19697         }
19698         return item;
19699     },
19700
19701     /**
19702      * Returns this menu's underlying {@link Roo.Element} object
19703      * @return {Roo.Element} The element
19704      */
19705     getEl : function(){
19706         if(!this.el){
19707             this.render();
19708         }
19709         return this.el;
19710     },
19711
19712     /**
19713      * Adds a separator bar to the menu
19714      * @return {Roo.menu.Item} The menu item that was added
19715      */
19716     addSeparator : function(){
19717         return this.addItem(new Roo.menu.Separator());
19718     },
19719
19720     /**
19721      * Adds an {@link Roo.Element} object to the menu
19722      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19723      * @return {Roo.menu.Item} The menu item that was added
19724      */
19725     addElement : function(el){
19726         return this.addItem(new Roo.menu.BaseItem(el));
19727     },
19728
19729     /**
19730      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19731      * @param {Roo.menu.Item} item The menu item to add
19732      * @return {Roo.menu.Item} The menu item that was added
19733      */
19734     addItem : function(item){
19735         this.items.add(item);
19736         if(this.ul){
19737             var li = document.createElement("li");
19738             li.className = "x-menu-list-item";
19739             this.ul.dom.appendChild(li);
19740             item.render(li, this);
19741             this.delayAutoWidth();
19742         }
19743         return item;
19744     },
19745
19746     /**
19747      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19748      * @param {Object} config A MenuItem config object
19749      * @return {Roo.menu.Item} The menu item that was added
19750      */
19751     addMenuItem : function(config){
19752         if(!(config instanceof Roo.menu.Item)){
19753             if(typeof config.checked == "boolean"){ // must be check menu item config?
19754                 config = new Roo.menu.CheckItem(config);
19755             }else{
19756                 config = new Roo.menu.Item(config);
19757             }
19758         }
19759         return this.addItem(config);
19760     },
19761
19762     /**
19763      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19764      * @param {String} text The text to display in the menu item
19765      * @return {Roo.menu.Item} The menu item that was added
19766      */
19767     addText : function(text){
19768         return this.addItem(new Roo.menu.TextItem({ text : text }));
19769     },
19770
19771     /**
19772      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19773      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19774      * @param {Roo.menu.Item} item The menu item to add
19775      * @return {Roo.menu.Item} The menu item that was added
19776      */
19777     insert : function(index, item){
19778         this.items.insert(index, item);
19779         if(this.ul){
19780             var li = document.createElement("li");
19781             li.className = "x-menu-list-item";
19782             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19783             item.render(li, this);
19784             this.delayAutoWidth();
19785         }
19786         return item;
19787     },
19788
19789     /**
19790      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19791      * @param {Roo.menu.Item} item The menu item to remove
19792      */
19793     remove : function(item){
19794         this.items.removeKey(item.id);
19795         item.destroy();
19796     },
19797
19798     /**
19799      * Removes and destroys all items in the menu
19800      */
19801     removeAll : function(){
19802         var f;
19803         while(f = this.items.first()){
19804             this.remove(f);
19805         }
19806     }
19807 });
19808
19809 // MenuNav is a private utility class used internally by the Menu
19810 Roo.menu.MenuNav = function(menu){
19811     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19812     this.scope = this.menu = menu;
19813 };
19814
19815 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19816     doRelay : function(e, h){
19817         var k = e.getKey();
19818         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19819             this.menu.tryActivate(0, 1);
19820             return false;
19821         }
19822         return h.call(this.scope || this, e, this.menu);
19823     },
19824
19825     up : function(e, m){
19826         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19827             m.tryActivate(m.items.length-1, -1);
19828         }
19829     },
19830
19831     down : function(e, m){
19832         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19833             m.tryActivate(0, 1);
19834         }
19835     },
19836
19837     right : function(e, m){
19838         if(m.activeItem){
19839             m.activeItem.expandMenu(true);
19840         }
19841     },
19842
19843     left : function(e, m){
19844         m.hide();
19845         if(m.parentMenu && m.parentMenu.activeItem){
19846             m.parentMenu.activeItem.activate();
19847         }
19848     },
19849
19850     enter : function(e, m){
19851         if(m.activeItem){
19852             e.stopPropagation();
19853             m.activeItem.onClick(e);
19854             m.fireEvent("click", this, m.activeItem);
19855             return true;
19856         }
19857     }
19858 });/*
19859  * Based on:
19860  * Ext JS Library 1.1.1
19861  * Copyright(c) 2006-2007, Ext JS, LLC.
19862  *
19863  * Originally Released Under LGPL - original licence link has changed is not relivant.
19864  *
19865  * Fork - LGPL
19866  * <script type="text/javascript">
19867  */
19868  
19869 /**
19870  * @class Roo.menu.MenuMgr
19871  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19872  * @singleton
19873  */
19874 Roo.menu.MenuMgr = function(){
19875    var menus, active, groups = {}, attached = false, lastShow = new Date();
19876
19877    // private - called when first menu is created
19878    function init(){
19879        menus = {};
19880        active = new Roo.util.MixedCollection();
19881        Roo.get(document).addKeyListener(27, function(){
19882            if(active.length > 0){
19883                hideAll();
19884            }
19885        });
19886    }
19887
19888    // private
19889    function hideAll(){
19890        if(active && active.length > 0){
19891            var c = active.clone();
19892            c.each(function(m){
19893                m.hide();
19894            });
19895        }
19896    }
19897
19898    // private
19899    function onHide(m){
19900        active.remove(m);
19901        if(active.length < 1){
19902            Roo.get(document).un("mousedown", onMouseDown);
19903            attached = false;
19904        }
19905    }
19906
19907    // private
19908    function onShow(m){
19909        var last = active.last();
19910        lastShow = new Date();
19911        active.add(m);
19912        if(!attached){
19913            Roo.get(document).on("mousedown", onMouseDown);
19914            attached = true;
19915        }
19916        if(m.parentMenu){
19917           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19918           m.parentMenu.activeChild = m;
19919        }else if(last && last.isVisible()){
19920           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19921        }
19922    }
19923
19924    // private
19925    function onBeforeHide(m){
19926        if(m.activeChild){
19927            m.activeChild.hide();
19928        }
19929        if(m.autoHideTimer){
19930            clearTimeout(m.autoHideTimer);
19931            delete m.autoHideTimer;
19932        }
19933    }
19934
19935    // private
19936    function onBeforeShow(m){
19937        var pm = m.parentMenu;
19938        if(!pm && !m.allowOtherMenus){
19939            hideAll();
19940        }else if(pm && pm.activeChild && active != m){
19941            pm.activeChild.hide();
19942        }
19943    }
19944
19945    // private
19946    function onMouseDown(e){
19947        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19948            hideAll();
19949        }
19950    }
19951
19952    // private
19953    function onBeforeCheck(mi, state){
19954        if(state){
19955            var g = groups[mi.group];
19956            for(var i = 0, l = g.length; i < l; i++){
19957                if(g[i] != mi){
19958                    g[i].setChecked(false);
19959                }
19960            }
19961        }
19962    }
19963
19964    return {
19965
19966        /**
19967         * Hides all menus that are currently visible
19968         */
19969        hideAll : function(){
19970             hideAll();  
19971        },
19972
19973        // private
19974        register : function(menu){
19975            if(!menus){
19976                init();
19977            }
19978            menus[menu.id] = menu;
19979            menu.on("beforehide", onBeforeHide);
19980            menu.on("hide", onHide);
19981            menu.on("beforeshow", onBeforeShow);
19982            menu.on("show", onShow);
19983            var g = menu.group;
19984            if(g && menu.events["checkchange"]){
19985                if(!groups[g]){
19986                    groups[g] = [];
19987                }
19988                groups[g].push(menu);
19989                menu.on("checkchange", onCheck);
19990            }
19991        },
19992
19993         /**
19994          * Returns a {@link Roo.menu.Menu} object
19995          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19996          * be used to generate and return a new Menu instance.
19997          */
19998        get : function(menu){
19999            if(typeof menu == "string"){ // menu id
20000                return menus[menu];
20001            }else if(menu.events){  // menu instance
20002                return menu;
20003            }else if(typeof menu.length == 'number'){ // array of menu items?
20004                return new Roo.menu.Menu({items:menu});
20005            }else{ // otherwise, must be a config
20006                return new Roo.menu.Menu(menu);
20007            }
20008        },
20009
20010        // private
20011        unregister : function(menu){
20012            delete menus[menu.id];
20013            menu.un("beforehide", onBeforeHide);
20014            menu.un("hide", onHide);
20015            menu.un("beforeshow", onBeforeShow);
20016            menu.un("show", onShow);
20017            var g = menu.group;
20018            if(g && menu.events["checkchange"]){
20019                groups[g].remove(menu);
20020                menu.un("checkchange", onCheck);
20021            }
20022        },
20023
20024        // private
20025        registerCheckable : function(menuItem){
20026            var g = menuItem.group;
20027            if(g){
20028                if(!groups[g]){
20029                    groups[g] = [];
20030                }
20031                groups[g].push(menuItem);
20032                menuItem.on("beforecheckchange", onBeforeCheck);
20033            }
20034        },
20035
20036        // private
20037        unregisterCheckable : function(menuItem){
20038            var g = menuItem.group;
20039            if(g){
20040                groups[g].remove(menuItem);
20041                menuItem.un("beforecheckchange", onBeforeCheck);
20042            }
20043        }
20044    };
20045 }();/*
20046  * Based on:
20047  * Ext JS Library 1.1.1
20048  * Copyright(c) 2006-2007, Ext JS, LLC.
20049  *
20050  * Originally Released Under LGPL - original licence link has changed is not relivant.
20051  *
20052  * Fork - LGPL
20053  * <script type="text/javascript">
20054  */
20055  
20056
20057 /**
20058  * @class Roo.menu.BaseItem
20059  * @extends Roo.Component
20060  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20061  * management and base configuration options shared by all menu components.
20062  * @constructor
20063  * Creates a new BaseItem
20064  * @param {Object} config Configuration options
20065  */
20066 Roo.menu.BaseItem = function(config){
20067     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20068
20069     this.addEvents({
20070         /**
20071          * @event click
20072          * Fires when this item is clicked
20073          * @param {Roo.menu.BaseItem} this
20074          * @param {Roo.EventObject} e
20075          */
20076         click: true,
20077         /**
20078          * @event activate
20079          * Fires when this item is activated
20080          * @param {Roo.menu.BaseItem} this
20081          */
20082         activate : true,
20083         /**
20084          * @event deactivate
20085          * Fires when this item is deactivated
20086          * @param {Roo.menu.BaseItem} this
20087          */
20088         deactivate : true
20089     });
20090
20091     if(this.handler){
20092         this.on("click", this.handler, this.scope, true);
20093     }
20094 };
20095
20096 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20097     /**
20098      * @cfg {Function} handler
20099      * A function that will handle the click event of this menu item (defaults to undefined)
20100      */
20101     /**
20102      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20103      */
20104     canActivate : false,
20105     /**
20106      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20107      */
20108     activeClass : "x-menu-item-active",
20109     /**
20110      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20111      */
20112     hideOnClick : true,
20113     /**
20114      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20115      */
20116     hideDelay : 100,
20117
20118     // private
20119     ctype: "Roo.menu.BaseItem",
20120
20121     // private
20122     actionMode : "container",
20123
20124     // private
20125     render : function(container, parentMenu){
20126         this.parentMenu = parentMenu;
20127         Roo.menu.BaseItem.superclass.render.call(this, container);
20128         this.container.menuItemId = this.id;
20129     },
20130
20131     // private
20132     onRender : function(container, position){
20133         this.el = Roo.get(this.el);
20134         container.dom.appendChild(this.el.dom);
20135     },
20136
20137     // private
20138     onClick : function(e){
20139         if(!this.disabled && this.fireEvent("click", this, e) !== false
20140                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20141             this.handleClick(e);
20142         }else{
20143             e.stopEvent();
20144         }
20145     },
20146
20147     // private
20148     activate : function(){
20149         if(this.disabled){
20150             return false;
20151         }
20152         var li = this.container;
20153         li.addClass(this.activeClass);
20154         this.region = li.getRegion().adjust(2, 2, -2, -2);
20155         this.fireEvent("activate", this);
20156         return true;
20157     },
20158
20159     // private
20160     deactivate : function(){
20161         this.container.removeClass(this.activeClass);
20162         this.fireEvent("deactivate", this);
20163     },
20164
20165     // private
20166     shouldDeactivate : function(e){
20167         return !this.region || !this.region.contains(e.getPoint());
20168     },
20169
20170     // private
20171     handleClick : function(e){
20172         if(this.hideOnClick){
20173             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20174         }
20175     },
20176
20177     // private
20178     expandMenu : function(autoActivate){
20179         // do nothing
20180     },
20181
20182     // private
20183     hideMenu : function(){
20184         // do nothing
20185     }
20186 });/*
20187  * Based on:
20188  * Ext JS Library 1.1.1
20189  * Copyright(c) 2006-2007, Ext JS, LLC.
20190  *
20191  * Originally Released Under LGPL - original licence link has changed is not relivant.
20192  *
20193  * Fork - LGPL
20194  * <script type="text/javascript">
20195  */
20196  
20197 /**
20198  * @class Roo.menu.Adapter
20199  * @extends Roo.menu.BaseItem
20200  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20201  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20202  * @constructor
20203  * Creates a new Adapter
20204  * @param {Object} config Configuration options
20205  */
20206 Roo.menu.Adapter = function(component, config){
20207     Roo.menu.Adapter.superclass.constructor.call(this, config);
20208     this.component = component;
20209 };
20210 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20211     // private
20212     canActivate : true,
20213
20214     // private
20215     onRender : function(container, position){
20216         this.component.render(container);
20217         this.el = this.component.getEl();
20218     },
20219
20220     // private
20221     activate : function(){
20222         if(this.disabled){
20223             return false;
20224         }
20225         this.component.focus();
20226         this.fireEvent("activate", this);
20227         return true;
20228     },
20229
20230     // private
20231     deactivate : function(){
20232         this.fireEvent("deactivate", this);
20233     },
20234
20235     // private
20236     disable : function(){
20237         this.component.disable();
20238         Roo.menu.Adapter.superclass.disable.call(this);
20239     },
20240
20241     // private
20242     enable : function(){
20243         this.component.enable();
20244         Roo.menu.Adapter.superclass.enable.call(this);
20245     }
20246 });/*
20247  * Based on:
20248  * Ext JS Library 1.1.1
20249  * Copyright(c) 2006-2007, Ext JS, LLC.
20250  *
20251  * Originally Released Under LGPL - original licence link has changed is not relivant.
20252  *
20253  * Fork - LGPL
20254  * <script type="text/javascript">
20255  */
20256
20257 /**
20258  * @class Roo.menu.TextItem
20259  * @extends Roo.menu.BaseItem
20260  * Adds a static text string to a menu, usually used as either a heading or group separator.
20261  * Note: old style constructor with text is still supported.
20262  * 
20263  * @constructor
20264  * Creates a new TextItem
20265  * @param {Object} cfg Configuration
20266  */
20267 Roo.menu.TextItem = function(cfg){
20268     if (typeof(cfg) == 'string') {
20269         this.text = cfg;
20270     } else {
20271         Roo.apply(this,cfg);
20272     }
20273     
20274     Roo.menu.TextItem.superclass.constructor.call(this);
20275 };
20276
20277 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20278     /**
20279      * @cfg {Boolean} text Text to show on item.
20280      */
20281     text : '',
20282     
20283     /**
20284      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20285      */
20286     hideOnClick : false,
20287     /**
20288      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20289      */
20290     itemCls : "x-menu-text",
20291
20292     // private
20293     onRender : function(){
20294         var s = document.createElement("span");
20295         s.className = this.itemCls;
20296         s.innerHTML = this.text;
20297         this.el = s;
20298         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20299     }
20300 });/*
20301  * Based on:
20302  * Ext JS Library 1.1.1
20303  * Copyright(c) 2006-2007, Ext JS, LLC.
20304  *
20305  * Originally Released Under LGPL - original licence link has changed is not relivant.
20306  *
20307  * Fork - LGPL
20308  * <script type="text/javascript">
20309  */
20310
20311 /**
20312  * @class Roo.menu.Separator
20313  * @extends Roo.menu.BaseItem
20314  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20315  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20316  * @constructor
20317  * @param {Object} config Configuration options
20318  */
20319 Roo.menu.Separator = function(config){
20320     Roo.menu.Separator.superclass.constructor.call(this, config);
20321 };
20322
20323 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20324     /**
20325      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20326      */
20327     itemCls : "x-menu-sep",
20328     /**
20329      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20330      */
20331     hideOnClick : false,
20332
20333     // private
20334     onRender : function(li){
20335         var s = document.createElement("span");
20336         s.className = this.itemCls;
20337         s.innerHTML = "&#160;";
20338         this.el = s;
20339         li.addClass("x-menu-sep-li");
20340         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20341     }
20342 });/*
20343  * Based on:
20344  * Ext JS Library 1.1.1
20345  * Copyright(c) 2006-2007, Ext JS, LLC.
20346  *
20347  * Originally Released Under LGPL - original licence link has changed is not relivant.
20348  *
20349  * Fork - LGPL
20350  * <script type="text/javascript">
20351  */
20352 /**
20353  * @class Roo.menu.Item
20354  * @extends Roo.menu.BaseItem
20355  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20356  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20357  * activation and click handling.
20358  * @constructor
20359  * Creates a new Item
20360  * @param {Object} config Configuration options
20361  */
20362 Roo.menu.Item = function(config){
20363     Roo.menu.Item.superclass.constructor.call(this, config);
20364     if(this.menu){
20365         this.menu = Roo.menu.MenuMgr.get(this.menu);
20366     }
20367 };
20368 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20369     
20370     /**
20371      * @cfg {String} text
20372      * The text to show on the menu item.
20373      */
20374     text: '',
20375      /**
20376      * @cfg {String} HTML to render in menu
20377      * The text to show on the menu item (HTML version).
20378      */
20379     html: '',
20380     /**
20381      * @cfg {String} icon
20382      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20383      */
20384     icon: undefined,
20385     /**
20386      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20387      */
20388     itemCls : "x-menu-item",
20389     /**
20390      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20391      */
20392     canActivate : true,
20393     /**
20394      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20395      */
20396     showDelay: 200,
20397     // doc'd in BaseItem
20398     hideDelay: 200,
20399
20400     // private
20401     ctype: "Roo.menu.Item",
20402     
20403     // private
20404     onRender : function(container, position){
20405         var el = document.createElement("a");
20406         el.hideFocus = true;
20407         el.unselectable = "on";
20408         el.href = this.href || "#";
20409         if(this.hrefTarget){
20410             el.target = this.hrefTarget;
20411         }
20412         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20413         
20414         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20415         
20416         el.innerHTML = String.format(
20417                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20418                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20419         this.el = el;
20420         Roo.menu.Item.superclass.onRender.call(this, container, position);
20421     },
20422
20423     /**
20424      * Sets the text to display in this menu item
20425      * @param {String} text The text to display
20426      * @param {Boolean} isHTML true to indicate text is pure html.
20427      */
20428     setText : function(text, isHTML){
20429         if (isHTML) {
20430             this.html = text;
20431         } else {
20432             this.text = text;
20433             this.html = '';
20434         }
20435         if(this.rendered){
20436             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20437      
20438             this.el.update(String.format(
20439                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20440                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20441             this.parentMenu.autoWidth();
20442         }
20443     },
20444
20445     // private
20446     handleClick : function(e){
20447         if(!this.href){ // if no link defined, stop the event automatically
20448             e.stopEvent();
20449         }
20450         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20451     },
20452
20453     // private
20454     activate : function(autoExpand){
20455         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20456             this.focus();
20457             if(autoExpand){
20458                 this.expandMenu();
20459             }
20460         }
20461         return true;
20462     },
20463
20464     // private
20465     shouldDeactivate : function(e){
20466         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20467             if(this.menu && this.menu.isVisible()){
20468                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20469             }
20470             return true;
20471         }
20472         return false;
20473     },
20474
20475     // private
20476     deactivate : function(){
20477         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20478         this.hideMenu();
20479     },
20480
20481     // private
20482     expandMenu : function(autoActivate){
20483         if(!this.disabled && this.menu){
20484             clearTimeout(this.hideTimer);
20485             delete this.hideTimer;
20486             if(!this.menu.isVisible() && !this.showTimer){
20487                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20488             }else if (this.menu.isVisible() && autoActivate){
20489                 this.menu.tryActivate(0, 1);
20490             }
20491         }
20492     },
20493
20494     // private
20495     deferExpand : function(autoActivate){
20496         delete this.showTimer;
20497         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20498         if(autoActivate){
20499             this.menu.tryActivate(0, 1);
20500         }
20501     },
20502
20503     // private
20504     hideMenu : function(){
20505         clearTimeout(this.showTimer);
20506         delete this.showTimer;
20507         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20508             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20509         }
20510     },
20511
20512     // private
20513     deferHide : function(){
20514         delete this.hideTimer;
20515         this.menu.hide();
20516     }
20517 });/*
20518  * Based on:
20519  * Ext JS Library 1.1.1
20520  * Copyright(c) 2006-2007, Ext JS, LLC.
20521  *
20522  * Originally Released Under LGPL - original licence link has changed is not relivant.
20523  *
20524  * Fork - LGPL
20525  * <script type="text/javascript">
20526  */
20527  
20528 /**
20529  * @class Roo.menu.CheckItem
20530  * @extends Roo.menu.Item
20531  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20532  * @constructor
20533  * Creates a new CheckItem
20534  * @param {Object} config Configuration options
20535  */
20536 Roo.menu.CheckItem = function(config){
20537     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20538     this.addEvents({
20539         /**
20540          * @event beforecheckchange
20541          * Fires before the checked value is set, providing an opportunity to cancel if needed
20542          * @param {Roo.menu.CheckItem} this
20543          * @param {Boolean} checked The new checked value that will be set
20544          */
20545         "beforecheckchange" : true,
20546         /**
20547          * @event checkchange
20548          * Fires after the checked value has been set
20549          * @param {Roo.menu.CheckItem} this
20550          * @param {Boolean} checked The checked value that was set
20551          */
20552         "checkchange" : true
20553     });
20554     if(this.checkHandler){
20555         this.on('checkchange', this.checkHandler, this.scope);
20556     }
20557 };
20558 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20559     /**
20560      * @cfg {String} group
20561      * All check items with the same group name will automatically be grouped into a single-select
20562      * radio button group (defaults to '')
20563      */
20564     /**
20565      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20566      */
20567     itemCls : "x-menu-item x-menu-check-item",
20568     /**
20569      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20570      */
20571     groupClass : "x-menu-group-item",
20572
20573     /**
20574      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20575      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20576      * initialized with checked = true will be rendered as checked.
20577      */
20578     checked: false,
20579
20580     // private
20581     ctype: "Roo.menu.CheckItem",
20582
20583     // private
20584     onRender : function(c){
20585         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20586         if(this.group){
20587             this.el.addClass(this.groupClass);
20588         }
20589         Roo.menu.MenuMgr.registerCheckable(this);
20590         if(this.checked){
20591             this.checked = false;
20592             this.setChecked(true, true);
20593         }
20594     },
20595
20596     // private
20597     destroy : function(){
20598         if(this.rendered){
20599             Roo.menu.MenuMgr.unregisterCheckable(this);
20600         }
20601         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20602     },
20603
20604     /**
20605      * Set the checked state of this item
20606      * @param {Boolean} checked The new checked value
20607      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20608      */
20609     setChecked : function(state, suppressEvent){
20610         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20611             if(this.container){
20612                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20613             }
20614             this.checked = state;
20615             if(suppressEvent !== true){
20616                 this.fireEvent("checkchange", this, state);
20617             }
20618         }
20619     },
20620
20621     // private
20622     handleClick : function(e){
20623        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20624            this.setChecked(!this.checked);
20625        }
20626        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20627     }
20628 });/*
20629  * Based on:
20630  * Ext JS Library 1.1.1
20631  * Copyright(c) 2006-2007, Ext JS, LLC.
20632  *
20633  * Originally Released Under LGPL - original licence link has changed is not relivant.
20634  *
20635  * Fork - LGPL
20636  * <script type="text/javascript">
20637  */
20638  
20639 /**
20640  * @class Roo.menu.DateItem
20641  * @extends Roo.menu.Adapter
20642  * A menu item that wraps the {@link Roo.DatPicker} component.
20643  * @constructor
20644  * Creates a new DateItem
20645  * @param {Object} config Configuration options
20646  */
20647 Roo.menu.DateItem = function(config){
20648     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20649     /** The Roo.DatePicker object @type Roo.DatePicker */
20650     this.picker = this.component;
20651     this.addEvents({select: true});
20652     
20653     this.picker.on("render", function(picker){
20654         picker.getEl().swallowEvent("click");
20655         picker.container.addClass("x-menu-date-item");
20656     });
20657
20658     this.picker.on("select", this.onSelect, this);
20659 };
20660
20661 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20662     // private
20663     onSelect : function(picker, date){
20664         this.fireEvent("select", this, date, picker);
20665         Roo.menu.DateItem.superclass.handleClick.call(this);
20666     }
20667 });/*
20668  * Based on:
20669  * Ext JS Library 1.1.1
20670  * Copyright(c) 2006-2007, Ext JS, LLC.
20671  *
20672  * Originally Released Under LGPL - original licence link has changed is not relivant.
20673  *
20674  * Fork - LGPL
20675  * <script type="text/javascript">
20676  */
20677  
20678 /**
20679  * @class Roo.menu.ColorItem
20680  * @extends Roo.menu.Adapter
20681  * A menu item that wraps the {@link Roo.ColorPalette} component.
20682  * @constructor
20683  * Creates a new ColorItem
20684  * @param {Object} config Configuration options
20685  */
20686 Roo.menu.ColorItem = function(config){
20687     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20688     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20689     this.palette = this.component;
20690     this.relayEvents(this.palette, ["select"]);
20691     if(this.selectHandler){
20692         this.on('select', this.selectHandler, this.scope);
20693     }
20694 };
20695 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20696  * Based on:
20697  * Ext JS Library 1.1.1
20698  * Copyright(c) 2006-2007, Ext JS, LLC.
20699  *
20700  * Originally Released Under LGPL - original licence link has changed is not relivant.
20701  *
20702  * Fork - LGPL
20703  * <script type="text/javascript">
20704  */
20705  
20706
20707 /**
20708  * @class Roo.menu.DateMenu
20709  * @extends Roo.menu.Menu
20710  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20711  * @constructor
20712  * Creates a new DateMenu
20713  * @param {Object} config Configuration options
20714  */
20715 Roo.menu.DateMenu = function(config){
20716     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20717     this.plain = true;
20718     var di = new Roo.menu.DateItem(config);
20719     this.add(di);
20720     /**
20721      * The {@link Roo.DatePicker} instance for this DateMenu
20722      * @type DatePicker
20723      */
20724     this.picker = di.picker;
20725     /**
20726      * @event select
20727      * @param {DatePicker} picker
20728      * @param {Date} date
20729      */
20730     this.relayEvents(di, ["select"]);
20731
20732     this.on('beforeshow', function(){
20733         if(this.picker){
20734             this.picker.hideMonthPicker(true);
20735         }
20736     }, this);
20737 };
20738 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20739     cls:'x-date-menu'
20740 });/*
20741  * Based on:
20742  * Ext JS Library 1.1.1
20743  * Copyright(c) 2006-2007, Ext JS, LLC.
20744  *
20745  * Originally Released Under LGPL - original licence link has changed is not relivant.
20746  *
20747  * Fork - LGPL
20748  * <script type="text/javascript">
20749  */
20750  
20751
20752 /**
20753  * @class Roo.menu.ColorMenu
20754  * @extends Roo.menu.Menu
20755  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20756  * @constructor
20757  * Creates a new ColorMenu
20758  * @param {Object} config Configuration options
20759  */
20760 Roo.menu.ColorMenu = function(config){
20761     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20762     this.plain = true;
20763     var ci = new Roo.menu.ColorItem(config);
20764     this.add(ci);
20765     /**
20766      * The {@link Roo.ColorPalette} instance for this ColorMenu
20767      * @type ColorPalette
20768      */
20769     this.palette = ci.palette;
20770     /**
20771      * @event select
20772      * @param {ColorPalette} palette
20773      * @param {String} color
20774      */
20775     this.relayEvents(ci, ["select"]);
20776 };
20777 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20778  * Based on:
20779  * Ext JS Library 1.1.1
20780  * Copyright(c) 2006-2007, Ext JS, LLC.
20781  *
20782  * Originally Released Under LGPL - original licence link has changed is not relivant.
20783  *
20784  * Fork - LGPL
20785  * <script type="text/javascript">
20786  */
20787  
20788 /**
20789  * @class Roo.form.Field
20790  * @extends Roo.BoxComponent
20791  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20792  * @constructor
20793  * Creates a new Field
20794  * @param {Object} config Configuration options
20795  */
20796 Roo.form.Field = function(config){
20797     Roo.form.Field.superclass.constructor.call(this, config);
20798 };
20799
20800 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20801     /**
20802      * @cfg {String} fieldLabel Label to use when rendering a form.
20803      */
20804        /**
20805      * @cfg {String} qtip Mouse over tip
20806      */
20807      
20808     /**
20809      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20810      */
20811     invalidClass : "x-form-invalid",
20812     /**
20813      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20814      */
20815     invalidText : "The value in this field is invalid",
20816     /**
20817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20818      */
20819     focusClass : "x-form-focus",
20820     /**
20821      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20822       automatic validation (defaults to "keyup").
20823      */
20824     validationEvent : "keyup",
20825     /**
20826      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20827      */
20828     validateOnBlur : true,
20829     /**
20830      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20831      */
20832     validationDelay : 250,
20833     /**
20834      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20835      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20836      */
20837     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20838     /**
20839      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20840      */
20841     fieldClass : "x-form-field",
20842     /**
20843      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20844      *<pre>
20845 Value         Description
20846 -----------   ----------------------------------------------------------------------
20847 qtip          Display a quick tip when the user hovers over the field
20848 title         Display a default browser title attribute popup
20849 under         Add a block div beneath the field containing the error text
20850 side          Add an error icon to the right of the field with a popup on hover
20851 [element id]  Add the error text directly to the innerHTML of the specified element
20852 </pre>
20853      */
20854     msgTarget : 'qtip',
20855     /**
20856      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20857      */
20858     msgFx : 'normal',
20859
20860     /**
20861      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20862      */
20863     readOnly : false,
20864
20865     /**
20866      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20867      */
20868     disabled : false,
20869
20870     /**
20871      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20872      */
20873     inputType : undefined,
20874     
20875     /**
20876      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20877          */
20878         tabIndex : undefined,
20879         
20880     // private
20881     isFormField : true,
20882
20883     // private
20884     hasFocus : false,
20885     /**
20886      * @property {Roo.Element} fieldEl
20887      * Element Containing the rendered Field (with label etc.)
20888      */
20889     /**
20890      * @cfg {Mixed} value A value to initialize this field with.
20891      */
20892     value : undefined,
20893
20894     /**
20895      * @cfg {String} name The field's HTML name attribute.
20896      */
20897     /**
20898      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20899      */
20900
20901         // private ??
20902         initComponent : function(){
20903         Roo.form.Field.superclass.initComponent.call(this);
20904         this.addEvents({
20905             /**
20906              * @event focus
20907              * Fires when this field receives input focus.
20908              * @param {Roo.form.Field} this
20909              */
20910             focus : true,
20911             /**
20912              * @event blur
20913              * Fires when this field loses input focus.
20914              * @param {Roo.form.Field} this
20915              */
20916             blur : true,
20917             /**
20918              * @event specialkey
20919              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20920              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20921              * @param {Roo.form.Field} this
20922              * @param {Roo.EventObject} e The event object
20923              */
20924             specialkey : true,
20925             /**
20926              * @event change
20927              * Fires just before the field blurs if the field value has changed.
20928              * @param {Roo.form.Field} this
20929              * @param {Mixed} newValue The new value
20930              * @param {Mixed} oldValue The original value
20931              */
20932             change : true,
20933             /**
20934              * @event invalid
20935              * Fires after the field has been marked as invalid.
20936              * @param {Roo.form.Field} this
20937              * @param {String} msg The validation message
20938              */
20939             invalid : true,
20940             /**
20941              * @event valid
20942              * Fires after the field has been validated with no errors.
20943              * @param {Roo.form.Field} this
20944              */
20945             valid : true,
20946              /**
20947              * @event keyup
20948              * Fires after the key up
20949              * @param {Roo.form.Field} this
20950              * @param {Roo.EventObject}  e The event Object
20951              */
20952             keyup : true
20953         });
20954     },
20955
20956     /**
20957      * Returns the name attribute of the field if available
20958      * @return {String} name The field name
20959      */
20960     getName: function(){
20961          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20962     },
20963
20964     // private
20965     onRender : function(ct, position){
20966         Roo.form.Field.superclass.onRender.call(this, ct, position);
20967         if(!this.el){
20968             var cfg = this.getAutoCreate();
20969             if(!cfg.name){
20970                 cfg.name = this.name || this.id;
20971             }
20972             if(this.inputType){
20973                 cfg.type = this.inputType;
20974             }
20975             this.el = ct.createChild(cfg, position);
20976         }
20977         var type = this.el.dom.type;
20978         if(type){
20979             if(type == 'password'){
20980                 type = 'text';
20981             }
20982             this.el.addClass('x-form-'+type);
20983         }
20984         if(this.readOnly){
20985             this.el.dom.readOnly = true;
20986         }
20987         if(this.tabIndex !== undefined){
20988             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20989         }
20990
20991         this.el.addClass([this.fieldClass, this.cls]);
20992         this.initValue();
20993     },
20994
20995     /**
20996      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20997      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20998      * @return {Roo.form.Field} this
20999      */
21000     applyTo : function(target){
21001         this.allowDomMove = false;
21002         this.el = Roo.get(target);
21003         this.render(this.el.dom.parentNode);
21004         return this;
21005     },
21006
21007     // private
21008     initValue : function(){
21009         if(this.value !== undefined){
21010             this.setValue(this.value);
21011         }else if(this.el.dom.value.length > 0){
21012             this.setValue(this.el.dom.value);
21013         }
21014     },
21015
21016     /**
21017      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21018      */
21019     isDirty : function() {
21020         if(this.disabled) {
21021             return false;
21022         }
21023         return String(this.getValue()) !== String(this.originalValue);
21024     },
21025
21026     // private
21027     afterRender : function(){
21028         Roo.form.Field.superclass.afterRender.call(this);
21029         this.initEvents();
21030     },
21031
21032     // private
21033     fireKey : function(e){
21034         //Roo.log('field ' + e.getKey());
21035         if(e.isNavKeyPress()){
21036             this.fireEvent("specialkey", this, e);
21037         }
21038     },
21039
21040     /**
21041      * Resets the current field value to the originally loaded value and clears any validation messages
21042      */
21043     reset : function(){
21044         this.setValue(this.originalValue);
21045         this.clearInvalid();
21046     },
21047
21048     // private
21049     initEvents : function(){
21050         // safari killled keypress - so keydown is now used..
21051         this.el.on("keydown" , this.fireKey,  this);
21052         this.el.on("focus", this.onFocus,  this);
21053         this.el.on("blur", this.onBlur,  this);
21054         this.el.relayEvent('keyup', this);
21055
21056         // reference to original value for reset
21057         this.originalValue = this.getValue();
21058     },
21059
21060     // private
21061     onFocus : function(){
21062         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21063             this.el.addClass(this.focusClass);
21064         }
21065         if(!this.hasFocus){
21066             this.hasFocus = true;
21067             this.startValue = this.getValue();
21068             this.fireEvent("focus", this);
21069         }
21070     },
21071
21072     beforeBlur : Roo.emptyFn,
21073
21074     // private
21075     onBlur : function(){
21076         this.beforeBlur();
21077         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21078             this.el.removeClass(this.focusClass);
21079         }
21080         this.hasFocus = false;
21081         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21082             this.validate();
21083         }
21084         var v = this.getValue();
21085         if(String(v) !== String(this.startValue)){
21086             this.fireEvent('change', this, v, this.startValue);
21087         }
21088         this.fireEvent("blur", this);
21089     },
21090
21091     /**
21092      * Returns whether or not the field value is currently valid
21093      * @param {Boolean} preventMark True to disable marking the field invalid
21094      * @return {Boolean} True if the value is valid, else false
21095      */
21096     isValid : function(preventMark){
21097         if(this.disabled){
21098             return true;
21099         }
21100         var restore = this.preventMark;
21101         this.preventMark = preventMark === true;
21102         var v = this.validateValue(this.processValue(this.getRawValue()));
21103         this.preventMark = restore;
21104         return v;
21105     },
21106
21107     /**
21108      * Validates the field value
21109      * @return {Boolean} True if the value is valid, else false
21110      */
21111     validate : function(){
21112         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21113             this.clearInvalid();
21114             return true;
21115         }
21116         return false;
21117     },
21118
21119     processValue : function(value){
21120         return value;
21121     },
21122
21123     // private
21124     // Subclasses should provide the validation implementation by overriding this
21125     validateValue : function(value){
21126         return true;
21127     },
21128
21129     /**
21130      * Mark this field as invalid
21131      * @param {String} msg The validation message
21132      */
21133     markInvalid : function(msg){
21134         if(!this.rendered || this.preventMark){ // not rendered
21135             return;
21136         }
21137         this.el.addClass(this.invalidClass);
21138         msg = msg || this.invalidText;
21139         switch(this.msgTarget){
21140             case 'qtip':
21141                 this.el.dom.qtip = msg;
21142                 this.el.dom.qclass = 'x-form-invalid-tip';
21143                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21144                     Roo.QuickTips.enable();
21145                 }
21146                 break;
21147             case 'title':
21148                 this.el.dom.title = msg;
21149                 break;
21150             case 'under':
21151                 if(!this.errorEl){
21152                     var elp = this.el.findParent('.x-form-element', 5, true);
21153                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21154                     this.errorEl.setWidth(elp.getWidth(true)-20);
21155                 }
21156                 this.errorEl.update(msg);
21157                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21158                 break;
21159             case 'side':
21160                 if(!this.errorIcon){
21161                     var elp = this.el.findParent('.x-form-element', 5, true);
21162                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21163                 }
21164                 this.alignErrorIcon();
21165                 this.errorIcon.dom.qtip = msg;
21166                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21167                 this.errorIcon.show();
21168                 this.on('resize', this.alignErrorIcon, this);
21169                 break;
21170             default:
21171                 var t = Roo.getDom(this.msgTarget);
21172                 t.innerHTML = msg;
21173                 t.style.display = this.msgDisplay;
21174                 break;
21175         }
21176         this.fireEvent('invalid', this, msg);
21177     },
21178
21179     // private
21180     alignErrorIcon : function(){
21181         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21182     },
21183
21184     /**
21185      * Clear any invalid styles/messages for this field
21186      */
21187     clearInvalid : function(){
21188         if(!this.rendered || this.preventMark){ // not rendered
21189             return;
21190         }
21191         this.el.removeClass(this.invalidClass);
21192         switch(this.msgTarget){
21193             case 'qtip':
21194                 this.el.dom.qtip = '';
21195                 break;
21196             case 'title':
21197                 this.el.dom.title = '';
21198                 break;
21199             case 'under':
21200                 if(this.errorEl){
21201                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21202                 }
21203                 break;
21204             case 'side':
21205                 if(this.errorIcon){
21206                     this.errorIcon.dom.qtip = '';
21207                     this.errorIcon.hide();
21208                     this.un('resize', this.alignErrorIcon, this);
21209                 }
21210                 break;
21211             default:
21212                 var t = Roo.getDom(this.msgTarget);
21213                 t.innerHTML = '';
21214                 t.style.display = 'none';
21215                 break;
21216         }
21217         this.fireEvent('valid', this);
21218     },
21219
21220     /**
21221      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21222      * @return {Mixed} value The field value
21223      */
21224     getRawValue : function(){
21225         var v = this.el.getValue();
21226         if(v === this.emptyText){
21227             v = '';
21228         }
21229         return v;
21230     },
21231
21232     /**
21233      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21234      * @return {Mixed} value The field value
21235      */
21236     getValue : function(){
21237         var v = this.el.getValue();
21238         if(v === this.emptyText || v === undefined){
21239             v = '';
21240         }
21241         return v;
21242     },
21243
21244     /**
21245      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21246      * @param {Mixed} value The value to set
21247      */
21248     setRawValue : function(v){
21249         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21250     },
21251
21252     /**
21253      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21254      * @param {Mixed} value The value to set
21255      */
21256     setValue : function(v){
21257         this.value = v;
21258         if(this.rendered){
21259             this.el.dom.value = (v === null || v === undefined ? '' : v);
21260             this.validate();
21261         }
21262     },
21263
21264     adjustSize : function(w, h){
21265         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21266         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21267         return s;
21268     },
21269
21270     adjustWidth : function(tag, w){
21271         tag = tag.toLowerCase();
21272         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21273             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21274                 if(tag == 'input'){
21275                     return w + 2;
21276                 }
21277                 if(tag = 'textarea'){
21278                     return w-2;
21279                 }
21280             }else if(Roo.isOpera){
21281                 if(tag == 'input'){
21282                     return w + 2;
21283                 }
21284                 if(tag = 'textarea'){
21285                     return w-2;
21286                 }
21287             }
21288         }
21289         return w;
21290     }
21291 });
21292
21293
21294 // anything other than normal should be considered experimental
21295 Roo.form.Field.msgFx = {
21296     normal : {
21297         show: function(msgEl, f){
21298             msgEl.setDisplayed('block');
21299         },
21300
21301         hide : function(msgEl, f){
21302             msgEl.setDisplayed(false).update('');
21303         }
21304     },
21305
21306     slide : {
21307         show: function(msgEl, f){
21308             msgEl.slideIn('t', {stopFx:true});
21309         },
21310
21311         hide : function(msgEl, f){
21312             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21313         }
21314     },
21315
21316     slideRight : {
21317         show: function(msgEl, f){
21318             msgEl.fixDisplay();
21319             msgEl.alignTo(f.el, 'tl-tr');
21320             msgEl.slideIn('l', {stopFx:true});
21321         },
21322
21323         hide : function(msgEl, f){
21324             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21325         }
21326     }
21327 };/*
21328  * Based on:
21329  * Ext JS Library 1.1.1
21330  * Copyright(c) 2006-2007, Ext JS, LLC.
21331  *
21332  * Originally Released Under LGPL - original licence link has changed is not relivant.
21333  *
21334  * Fork - LGPL
21335  * <script type="text/javascript">
21336  */
21337  
21338
21339 /**
21340  * @class Roo.form.TextField
21341  * @extends Roo.form.Field
21342  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21343  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21344  * @constructor
21345  * Creates a new TextField
21346  * @param {Object} config Configuration options
21347  */
21348 Roo.form.TextField = function(config){
21349     Roo.form.TextField.superclass.constructor.call(this, config);
21350     this.addEvents({
21351         /**
21352          * @event autosize
21353          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21354          * according to the default logic, but this event provides a hook for the developer to apply additional
21355          * logic at runtime to resize the field if needed.
21356              * @param {Roo.form.Field} this This text field
21357              * @param {Number} width The new field width
21358              */
21359         autosize : true
21360     });
21361 };
21362
21363 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21364     /**
21365      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21366      */
21367     grow : false,
21368     /**
21369      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21370      */
21371     growMin : 30,
21372     /**
21373      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21374      */
21375     growMax : 800,
21376     /**
21377      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21378      */
21379     vtype : null,
21380     /**
21381      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21382      */
21383     maskRe : null,
21384     /**
21385      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21386      */
21387     disableKeyFilter : false,
21388     /**
21389      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21390      */
21391     allowBlank : true,
21392     /**
21393      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21394      */
21395     minLength : 0,
21396     /**
21397      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21398      */
21399     maxLength : Number.MAX_VALUE,
21400     /**
21401      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21402      */
21403     minLengthText : "The minimum length for this field is {0}",
21404     /**
21405      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21406      */
21407     maxLengthText : "The maximum length for this field is {0}",
21408     /**
21409      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21410      */
21411     selectOnFocus : false,
21412     /**
21413      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21414      */
21415     blankText : "This field is required",
21416     /**
21417      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21418      * If available, this function will be called only after the basic validators all return true, and will be passed the
21419      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21420      */
21421     validator : null,
21422     /**
21423      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21424      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21425      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21426      */
21427     regex : null,
21428     /**
21429      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21430      */
21431     regexText : "",
21432     /**
21433      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21434      */
21435     emptyText : null,
21436     /**
21437      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21438      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21439      */
21440     emptyClass : 'x-form-empty-field',
21441
21442     // private
21443     initEvents : function(){
21444         Roo.form.TextField.superclass.initEvents.call(this);
21445         if(this.validationEvent == 'keyup'){
21446             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21447             this.el.on('keyup', this.filterValidation, this);
21448         }
21449         else if(this.validationEvent !== false){
21450             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21451         }
21452         if(this.selectOnFocus || this.emptyText){
21453             this.on("focus", this.preFocus, this);
21454             if(this.emptyText){
21455                 this.on('blur', this.postBlur, this);
21456                 this.applyEmptyText();
21457             }
21458         }
21459         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21460             this.el.on("keypress", this.filterKeys, this);
21461         }
21462         if(this.grow){
21463             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21464             this.el.on("click", this.autoSize,  this);
21465         }
21466     },
21467
21468     processValue : function(value){
21469         if(this.stripCharsRe){
21470             var newValue = value.replace(this.stripCharsRe, '');
21471             if(newValue !== value){
21472                 this.setRawValue(newValue);
21473                 return newValue;
21474             }
21475         }
21476         return value;
21477     },
21478
21479     filterValidation : function(e){
21480         if(!e.isNavKeyPress()){
21481             this.validationTask.delay(this.validationDelay);
21482         }
21483     },
21484
21485     // private
21486     onKeyUp : function(e){
21487         if(!e.isNavKeyPress()){
21488             this.autoSize();
21489         }
21490     },
21491
21492     /**
21493      * Resets the current field value to the originally-loaded value and clears any validation messages.
21494      * Also adds emptyText and emptyClass if the original value was blank.
21495      */
21496     reset : function(){
21497         Roo.form.TextField.superclass.reset.call(this);
21498         this.applyEmptyText();
21499     },
21500
21501     applyEmptyText : function(){
21502         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21503             this.setRawValue(this.emptyText);
21504             this.el.addClass(this.emptyClass);
21505         }
21506     },
21507
21508     // private
21509     preFocus : function(){
21510         if(this.emptyText){
21511             if(this.el.dom.value == this.emptyText){
21512                 this.setRawValue('');
21513             }
21514             this.el.removeClass(this.emptyClass);
21515         }
21516         if(this.selectOnFocus){
21517             this.el.dom.select();
21518         }
21519     },
21520
21521     // private
21522     postBlur : function(){
21523         this.applyEmptyText();
21524     },
21525
21526     // private
21527     filterKeys : function(e){
21528         var k = e.getKey();
21529         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21530             return;
21531         }
21532         var c = e.getCharCode(), cc = String.fromCharCode(c);
21533         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21534             return;
21535         }
21536         if(!this.maskRe.test(cc)){
21537             e.stopEvent();
21538         }
21539     },
21540
21541     setValue : function(v){
21542         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21543             this.el.removeClass(this.emptyClass);
21544         }
21545         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21546         this.applyEmptyText();
21547         this.autoSize();
21548     },
21549
21550     /**
21551      * Validates a value according to the field's validation rules and marks the field as invalid
21552      * if the validation fails
21553      * @param {Mixed} value The value to validate
21554      * @return {Boolean} True if the value is valid, else false
21555      */
21556     validateValue : function(value){
21557         if(value.length < 1 || value === this.emptyText){ // if it's blank
21558              if(this.allowBlank){
21559                 this.clearInvalid();
21560                 return true;
21561              }else{
21562                 this.markInvalid(this.blankText);
21563                 return false;
21564              }
21565         }
21566         if(value.length < this.minLength){
21567             this.markInvalid(String.format(this.minLengthText, this.minLength));
21568             return false;
21569         }
21570         if(value.length > this.maxLength){
21571             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21572             return false;
21573         }
21574         if(this.vtype){
21575             var vt = Roo.form.VTypes;
21576             if(!vt[this.vtype](value, this)){
21577                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21578                 return false;
21579             }
21580         }
21581         if(typeof this.validator == "function"){
21582             var msg = this.validator(value);
21583             if(msg !== true){
21584                 this.markInvalid(msg);
21585                 return false;
21586             }
21587         }
21588         if(this.regex && !this.regex.test(value)){
21589             this.markInvalid(this.regexText);
21590             return false;
21591         }
21592         return true;
21593     },
21594
21595     /**
21596      * Selects text in this field
21597      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21598      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21599      */
21600     selectText : function(start, end){
21601         var v = this.getRawValue();
21602         if(v.length > 0){
21603             start = start === undefined ? 0 : start;
21604             end = end === undefined ? v.length : end;
21605             var d = this.el.dom;
21606             if(d.setSelectionRange){
21607                 d.setSelectionRange(start, end);
21608             }else if(d.createTextRange){
21609                 var range = d.createTextRange();
21610                 range.moveStart("character", start);
21611                 range.moveEnd("character", v.length-end);
21612                 range.select();
21613             }
21614         }
21615     },
21616
21617     /**
21618      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21619      * This only takes effect if grow = true, and fires the autosize event.
21620      */
21621     autoSize : function(){
21622         if(!this.grow || !this.rendered){
21623             return;
21624         }
21625         if(!this.metrics){
21626             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21627         }
21628         var el = this.el;
21629         var v = el.dom.value;
21630         var d = document.createElement('div');
21631         d.appendChild(document.createTextNode(v));
21632         v = d.innerHTML;
21633         d = null;
21634         v += "&#160;";
21635         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21636         this.el.setWidth(w);
21637         this.fireEvent("autosize", this, w);
21638     }
21639 });/*
21640  * Based on:
21641  * Ext JS Library 1.1.1
21642  * Copyright(c) 2006-2007, Ext JS, LLC.
21643  *
21644  * Originally Released Under LGPL - original licence link has changed is not relivant.
21645  *
21646  * Fork - LGPL
21647  * <script type="text/javascript">
21648  */
21649  
21650 /**
21651  * @class Roo.form.Hidden
21652  * @extends Roo.form.TextField
21653  * Simple Hidden element used on forms 
21654  * 
21655  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21656  * 
21657  * @constructor
21658  * Creates a new Hidden form element.
21659  * @param {Object} config Configuration options
21660  */
21661
21662
21663
21664 // easy hidden field...
21665 Roo.form.Hidden = function(config){
21666     Roo.form.Hidden.superclass.constructor.call(this, config);
21667 };
21668   
21669 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21670     fieldLabel:      '',
21671     inputType:      'hidden',
21672     width:          50,
21673     allowBlank:     true,
21674     labelSeparator: '',
21675     hidden:         true,
21676     itemCls :       'x-form-item-display-none'
21677
21678
21679 });
21680
21681
21682 /*
21683  * Based on:
21684  * Ext JS Library 1.1.1
21685  * Copyright(c) 2006-2007, Ext JS, LLC.
21686  *
21687  * Originally Released Under LGPL - original licence link has changed is not relivant.
21688  *
21689  * Fork - LGPL
21690  * <script type="text/javascript">
21691  */
21692  
21693 /**
21694  * @class Roo.form.TriggerField
21695  * @extends Roo.form.TextField
21696  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21697  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21698  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21699  * for which you can provide a custom implementation.  For example:
21700  * <pre><code>
21701 var trigger = new Roo.form.TriggerField();
21702 trigger.onTriggerClick = myTriggerFn;
21703 trigger.applyTo('my-field');
21704 </code></pre>
21705  *
21706  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21707  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21708  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21709  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21710  * @constructor
21711  * Create a new TriggerField.
21712  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21713  * to the base TextField)
21714  */
21715 Roo.form.TriggerField = function(config){
21716     this.mimicing = false;
21717     Roo.form.TriggerField.superclass.constructor.call(this, config);
21718 };
21719
21720 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21721     /**
21722      * @cfg {String} triggerClass A CSS class to apply to the trigger
21723      */
21724     /**
21725      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21726      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21727      */
21728     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21729     /**
21730      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21731      */
21732     hideTrigger:false,
21733
21734     /** @cfg {Boolean} grow @hide */
21735     /** @cfg {Number} growMin @hide */
21736     /** @cfg {Number} growMax @hide */
21737
21738     /**
21739      * @hide 
21740      * @method
21741      */
21742     autoSize: Roo.emptyFn,
21743     // private
21744     monitorTab : true,
21745     // private
21746     deferHeight : true,
21747
21748     
21749     actionMode : 'wrap',
21750     // private
21751     onResize : function(w, h){
21752         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21753         if(typeof w == 'number'){
21754             var x = w - this.trigger.getWidth();
21755             this.el.setWidth(this.adjustWidth('input', x));
21756             this.trigger.setStyle('left', x+'px');
21757         }
21758     },
21759
21760     // private
21761     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21762
21763     // private
21764     getResizeEl : function(){
21765         return this.wrap;
21766     },
21767
21768     // private
21769     getPositionEl : function(){
21770         return this.wrap;
21771     },
21772
21773     // private
21774     alignErrorIcon : function(){
21775         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21776     },
21777
21778     // private
21779     onRender : function(ct, position){
21780         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21781         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21782         this.trigger = this.wrap.createChild(this.triggerConfig ||
21783                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21784         if(this.hideTrigger){
21785             this.trigger.setDisplayed(false);
21786         }
21787         this.initTrigger();
21788         if(!this.width){
21789             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21790         }
21791     },
21792
21793     // private
21794     initTrigger : function(){
21795         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21796         this.trigger.addClassOnOver('x-form-trigger-over');
21797         this.trigger.addClassOnClick('x-form-trigger-click');
21798     },
21799
21800     // private
21801     onDestroy : function(){
21802         if(this.trigger){
21803             this.trigger.removeAllListeners();
21804             this.trigger.remove();
21805         }
21806         if(this.wrap){
21807             this.wrap.remove();
21808         }
21809         Roo.form.TriggerField.superclass.onDestroy.call(this);
21810     },
21811
21812     // private
21813     onFocus : function(){
21814         Roo.form.TriggerField.superclass.onFocus.call(this);
21815         if(!this.mimicing){
21816             this.wrap.addClass('x-trigger-wrap-focus');
21817             this.mimicing = true;
21818             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21819             if(this.monitorTab){
21820                 this.el.on("keydown", this.checkTab, this);
21821             }
21822         }
21823     },
21824
21825     // private
21826     checkTab : function(e){
21827         if(e.getKey() == e.TAB){
21828             this.triggerBlur();
21829         }
21830     },
21831
21832     // private
21833     onBlur : function(){
21834         // do nothing
21835     },
21836
21837     // private
21838     mimicBlur : function(e, t){
21839         if(!this.wrap.contains(t) && this.validateBlur()){
21840             this.triggerBlur();
21841         }
21842     },
21843
21844     // private
21845     triggerBlur : function(){
21846         this.mimicing = false;
21847         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21848         if(this.monitorTab){
21849             this.el.un("keydown", this.checkTab, this);
21850         }
21851         this.wrap.removeClass('x-trigger-wrap-focus');
21852         Roo.form.TriggerField.superclass.onBlur.call(this);
21853     },
21854
21855     // private
21856     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21857     validateBlur : function(e, t){
21858         return true;
21859     },
21860
21861     // private
21862     onDisable : function(){
21863         Roo.form.TriggerField.superclass.onDisable.call(this);
21864         if(this.wrap){
21865             this.wrap.addClass('x-item-disabled');
21866         }
21867     },
21868
21869     // private
21870     onEnable : function(){
21871         Roo.form.TriggerField.superclass.onEnable.call(this);
21872         if(this.wrap){
21873             this.wrap.removeClass('x-item-disabled');
21874         }
21875     },
21876
21877     // private
21878     onShow : function(){
21879         var ae = this.getActionEl();
21880         
21881         if(ae){
21882             ae.dom.style.display = '';
21883             ae.dom.style.visibility = 'visible';
21884         }
21885     },
21886
21887     // private
21888     
21889     onHide : function(){
21890         var ae = this.getActionEl();
21891         ae.dom.style.display = 'none';
21892     },
21893
21894     /**
21895      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21896      * by an implementing function.
21897      * @method
21898      * @param {EventObject} e
21899      */
21900     onTriggerClick : Roo.emptyFn
21901 });
21902
21903 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21904 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21905 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21906 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21907     initComponent : function(){
21908         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21909
21910         this.triggerConfig = {
21911             tag:'span', cls:'x-form-twin-triggers', cn:[
21912             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21913             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21914         ]};
21915     },
21916
21917     getTrigger : function(index){
21918         return this.triggers[index];
21919     },
21920
21921     initTrigger : function(){
21922         var ts = this.trigger.select('.x-form-trigger', true);
21923         this.wrap.setStyle('overflow', 'hidden');
21924         var triggerField = this;
21925         ts.each(function(t, all, index){
21926             t.hide = function(){
21927                 var w = triggerField.wrap.getWidth();
21928                 this.dom.style.display = 'none';
21929                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21930             };
21931             t.show = function(){
21932                 var w = triggerField.wrap.getWidth();
21933                 this.dom.style.display = '';
21934                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21935             };
21936             var triggerIndex = 'Trigger'+(index+1);
21937
21938             if(this['hide'+triggerIndex]){
21939                 t.dom.style.display = 'none';
21940             }
21941             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21942             t.addClassOnOver('x-form-trigger-over');
21943             t.addClassOnClick('x-form-trigger-click');
21944         }, this);
21945         this.triggers = ts.elements;
21946     },
21947
21948     onTrigger1Click : Roo.emptyFn,
21949     onTrigger2Click : Roo.emptyFn
21950 });/*
21951  * Based on:
21952  * Ext JS Library 1.1.1
21953  * Copyright(c) 2006-2007, Ext JS, LLC.
21954  *
21955  * Originally Released Under LGPL - original licence link has changed is not relivant.
21956  *
21957  * Fork - LGPL
21958  * <script type="text/javascript">
21959  */
21960  
21961 /**
21962  * @class Roo.form.TextArea
21963  * @extends Roo.form.TextField
21964  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21965  * support for auto-sizing.
21966  * @constructor
21967  * Creates a new TextArea
21968  * @param {Object} config Configuration options
21969  */
21970 Roo.form.TextArea = function(config){
21971     Roo.form.TextArea.superclass.constructor.call(this, config);
21972     // these are provided exchanges for backwards compat
21973     // minHeight/maxHeight were replaced by growMin/growMax to be
21974     // compatible with TextField growing config values
21975     if(this.minHeight !== undefined){
21976         this.growMin = this.minHeight;
21977     }
21978     if(this.maxHeight !== undefined){
21979         this.growMax = this.maxHeight;
21980     }
21981 };
21982
21983 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21984     /**
21985      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21986      */
21987     growMin : 60,
21988     /**
21989      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21990      */
21991     growMax: 1000,
21992     /**
21993      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21994      * in the field (equivalent to setting overflow: hidden, defaults to false)
21995      */
21996     preventScrollbars: false,
21997     /**
21998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21999      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22000      */
22001
22002     // private
22003     onRender : function(ct, position){
22004         if(!this.el){
22005             this.defaultAutoCreate = {
22006                 tag: "textarea",
22007                 style:"width:300px;height:60px;",
22008                 autocomplete: "off"
22009             };
22010         }
22011         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22012         if(this.grow){
22013             this.textSizeEl = Roo.DomHelper.append(document.body, {
22014                 tag: "pre", cls: "x-form-grow-sizer"
22015             });
22016             if(this.preventScrollbars){
22017                 this.el.setStyle("overflow", "hidden");
22018             }
22019             this.el.setHeight(this.growMin);
22020         }
22021     },
22022
22023     onDestroy : function(){
22024         if(this.textSizeEl){
22025             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22026         }
22027         Roo.form.TextArea.superclass.onDestroy.call(this);
22028     },
22029
22030     // private
22031     onKeyUp : function(e){
22032         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22033             this.autoSize();
22034         }
22035     },
22036
22037     /**
22038      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22039      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22040      */
22041     autoSize : function(){
22042         if(!this.grow || !this.textSizeEl){
22043             return;
22044         }
22045         var el = this.el;
22046         var v = el.dom.value;
22047         var ts = this.textSizeEl;
22048
22049         ts.innerHTML = '';
22050         ts.appendChild(document.createTextNode(v));
22051         v = ts.innerHTML;
22052
22053         Roo.fly(ts).setWidth(this.el.getWidth());
22054         if(v.length < 1){
22055             v = "&#160;&#160;";
22056         }else{
22057             if(Roo.isIE){
22058                 v = v.replace(/\n/g, '<p>&#160;</p>');
22059             }
22060             v += "&#160;\n&#160;";
22061         }
22062         ts.innerHTML = v;
22063         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22064         if(h != this.lastHeight){
22065             this.lastHeight = h;
22066             this.el.setHeight(h);
22067             this.fireEvent("autosize", this, h);
22068         }
22069     }
22070 });/*
22071  * Based on:
22072  * Ext JS Library 1.1.1
22073  * Copyright(c) 2006-2007, Ext JS, LLC.
22074  *
22075  * Originally Released Under LGPL - original licence link has changed is not relivant.
22076  *
22077  * Fork - LGPL
22078  * <script type="text/javascript">
22079  */
22080  
22081
22082 /**
22083  * @class Roo.form.NumberField
22084  * @extends Roo.form.TextField
22085  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22086  * @constructor
22087  * Creates a new NumberField
22088  * @param {Object} config Configuration options
22089  */
22090 Roo.form.NumberField = function(config){
22091     Roo.form.NumberField.superclass.constructor.call(this, config);
22092 };
22093
22094 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22095     /**
22096      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22097      */
22098     fieldClass: "x-form-field x-form-num-field",
22099     /**
22100      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22101      */
22102     allowDecimals : true,
22103     /**
22104      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22105      */
22106     decimalSeparator : ".",
22107     /**
22108      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22109      */
22110     decimalPrecision : 2,
22111     /**
22112      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22113      */
22114     allowNegative : true,
22115     /**
22116      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22117      */
22118     minValue : Number.NEGATIVE_INFINITY,
22119     /**
22120      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22121      */
22122     maxValue : Number.MAX_VALUE,
22123     /**
22124      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22125      */
22126     minText : "The minimum value for this field is {0}",
22127     /**
22128      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22129      */
22130     maxText : "The maximum value for this field is {0}",
22131     /**
22132      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22133      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22134      */
22135     nanText : "{0} is not a valid number",
22136
22137     // private
22138     initEvents : function(){
22139         Roo.form.NumberField.superclass.initEvents.call(this);
22140         var allowed = "0123456789";
22141         if(this.allowDecimals){
22142             allowed += this.decimalSeparator;
22143         }
22144         if(this.allowNegative){
22145             allowed += "-";
22146         }
22147         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22148         var keyPress = function(e){
22149             var k = e.getKey();
22150             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22151                 return;
22152             }
22153             var c = e.getCharCode();
22154             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22155                 e.stopEvent();
22156             }
22157         };
22158         this.el.on("keypress", keyPress, this);
22159     },
22160
22161     // private
22162     validateValue : function(value){
22163         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22164             return false;
22165         }
22166         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22167              return true;
22168         }
22169         var num = this.parseValue(value);
22170         if(isNaN(num)){
22171             this.markInvalid(String.format(this.nanText, value));
22172             return false;
22173         }
22174         if(num < this.minValue){
22175             this.markInvalid(String.format(this.minText, this.minValue));
22176             return false;
22177         }
22178         if(num > this.maxValue){
22179             this.markInvalid(String.format(this.maxText, this.maxValue));
22180             return false;
22181         }
22182         return true;
22183     },
22184
22185     getValue : function(){
22186         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22187     },
22188
22189     // private
22190     parseValue : function(value){
22191         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22192         return isNaN(value) ? '' : value;
22193     },
22194
22195     // private
22196     fixPrecision : function(value){
22197         var nan = isNaN(value);
22198         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22199             return nan ? '' : value;
22200         }
22201         return parseFloat(value).toFixed(this.decimalPrecision);
22202     },
22203
22204     setValue : function(v){
22205         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22206     },
22207
22208     // private
22209     decimalPrecisionFcn : function(v){
22210         return Math.floor(v);
22211     },
22212
22213     beforeBlur : function(){
22214         var v = this.parseValue(this.getRawValue());
22215         if(v){
22216             this.setValue(this.fixPrecision(v));
22217         }
22218     }
22219 });/*
22220  * Based on:
22221  * Ext JS Library 1.1.1
22222  * Copyright(c) 2006-2007, Ext JS, LLC.
22223  *
22224  * Originally Released Under LGPL - original licence link has changed is not relivant.
22225  *
22226  * Fork - LGPL
22227  * <script type="text/javascript">
22228  */
22229  
22230 /**
22231  * @class Roo.form.DateField
22232  * @extends Roo.form.TriggerField
22233  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22234 * @constructor
22235 * Create a new DateField
22236 * @param {Object} config
22237  */
22238 Roo.form.DateField = function(config){
22239     Roo.form.DateField.superclass.constructor.call(this, config);
22240     
22241       this.addEvents({
22242          
22243         /**
22244          * @event select
22245          * Fires when a date is selected
22246              * @param {Roo.form.DateField} combo This combo box
22247              * @param {Date} date The date selected
22248              */
22249         'select' : true
22250          
22251     });
22252     
22253     
22254     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22255     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22256     this.ddMatch = null;
22257     if(this.disabledDates){
22258         var dd = this.disabledDates;
22259         var re = "(?:";
22260         for(var i = 0; i < dd.length; i++){
22261             re += dd[i];
22262             if(i != dd.length-1) re += "|";
22263         }
22264         this.ddMatch = new RegExp(re + ")");
22265     }
22266 };
22267
22268 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22269     /**
22270      * @cfg {String} format
22271      * The default date format string which can be overriden for localization support.  The format must be
22272      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22273      */
22274     format : "m/d/y",
22275     /**
22276      * @cfg {String} altFormats
22277      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22278      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22279      */
22280     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22281     /**
22282      * @cfg {Array} disabledDays
22283      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22284      */
22285     disabledDays : null,
22286     /**
22287      * @cfg {String} disabledDaysText
22288      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22289      */
22290     disabledDaysText : "Disabled",
22291     /**
22292      * @cfg {Array} disabledDates
22293      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22294      * expression so they are very powerful. Some examples:
22295      * <ul>
22296      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22297      * <li>["03/08", "09/16"] would disable those days for every year</li>
22298      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22299      * <li>["03/../2006"] would disable every day in March 2006</li>
22300      * <li>["^03"] would disable every day in every March</li>
22301      * </ul>
22302      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22303      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22304      */
22305     disabledDates : null,
22306     /**
22307      * @cfg {String} disabledDatesText
22308      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22309      */
22310     disabledDatesText : "Disabled",
22311     /**
22312      * @cfg {Date/String} minValue
22313      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22314      * valid format (defaults to null).
22315      */
22316     minValue : null,
22317     /**
22318      * @cfg {Date/String} maxValue
22319      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22320      * valid format (defaults to null).
22321      */
22322     maxValue : null,
22323     /**
22324      * @cfg {String} minText
22325      * The error text to display when the date in the cell is before minValue (defaults to
22326      * 'The date in this field must be after {minValue}').
22327      */
22328     minText : "The date in this field must be equal to or after {0}",
22329     /**
22330      * @cfg {String} maxText
22331      * The error text to display when the date in the cell is after maxValue (defaults to
22332      * 'The date in this field must be before {maxValue}').
22333      */
22334     maxText : "The date in this field must be equal to or before {0}",
22335     /**
22336      * @cfg {String} invalidText
22337      * The error text to display when the date in the field is invalid (defaults to
22338      * '{value} is not a valid date - it must be in the format {format}').
22339      */
22340     invalidText : "{0} is not a valid date - it must be in the format {1}",
22341     /**
22342      * @cfg {String} triggerClass
22343      * An additional CSS class used to style the trigger button.  The trigger will always get the
22344      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22345      * which displays a calendar icon).
22346      */
22347     triggerClass : 'x-form-date-trigger',
22348     
22349
22350     /**
22351      * @cfg {bool} useIso
22352      * if enabled, then the date field will use a hidden field to store the 
22353      * real value as iso formated date. default (false)
22354      */ 
22355     useIso : false,
22356     /**
22357      * @cfg {String/Object} autoCreate
22358      * A DomHelper element spec, or true for a default element spec (defaults to
22359      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22360      */ 
22361     // private
22362     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22363     
22364     // private
22365     hiddenField: false,
22366     
22367     onRender : function(ct, position)
22368     {
22369         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22370         if (this.useIso) {
22371             this.el.dom.removeAttribute('name'); 
22372             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22373                     'before', true);
22374             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22375             // prevent input submission
22376             this.hiddenName = this.name;
22377         }
22378             
22379             
22380     },
22381     
22382     // private
22383     validateValue : function(value)
22384     {
22385         value = this.formatDate(value);
22386         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22387             return false;
22388         }
22389         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22390              return true;
22391         }
22392         var svalue = value;
22393         value = this.parseDate(value);
22394         if(!value){
22395             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22396             return false;
22397         }
22398         var time = value.getTime();
22399         if(this.minValue && time < this.minValue.getTime()){
22400             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22401             return false;
22402         }
22403         if(this.maxValue && time > this.maxValue.getTime()){
22404             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22405             return false;
22406         }
22407         if(this.disabledDays){
22408             var day = value.getDay();
22409             for(var i = 0; i < this.disabledDays.length; i++) {
22410                 if(day === this.disabledDays[i]){
22411                     this.markInvalid(this.disabledDaysText);
22412                     return false;
22413                 }
22414             }
22415         }
22416         var fvalue = this.formatDate(value);
22417         if(this.ddMatch && this.ddMatch.test(fvalue)){
22418             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22419             return false;
22420         }
22421         return true;
22422     },
22423
22424     // private
22425     // Provides logic to override the default TriggerField.validateBlur which just returns true
22426     validateBlur : function(){
22427         return !this.menu || !this.menu.isVisible();
22428     },
22429
22430     /**
22431      * Returns the current date value of the date field.
22432      * @return {Date} The date value
22433      */
22434     getValue : function(){
22435         
22436         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22437     },
22438
22439     /**
22440      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22441      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22442      * (the default format used is "m/d/y").
22443      * <br />Usage:
22444      * <pre><code>
22445 //All of these calls set the same date value (May 4, 2006)
22446
22447 //Pass a date object:
22448 var dt = new Date('5/4/06');
22449 dateField.setValue(dt);
22450
22451 //Pass a date string (default format):
22452 dateField.setValue('5/4/06');
22453
22454 //Pass a date string (custom format):
22455 dateField.format = 'Y-m-d';
22456 dateField.setValue('2006-5-4');
22457 </code></pre>
22458      * @param {String/Date} date The date or valid date string
22459      */
22460     setValue : function(date){
22461         if (this.hiddenField) {
22462             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22463         }
22464         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22465     },
22466
22467     // private
22468     parseDate : function(value){
22469         if(!value || value instanceof Date){
22470             return value;
22471         }
22472         var v = Date.parseDate(value, this.format);
22473         if(!v && this.altFormats){
22474             if(!this.altFormatsArray){
22475                 this.altFormatsArray = this.altFormats.split("|");
22476             }
22477             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22478                 v = Date.parseDate(value, this.altFormatsArray[i]);
22479             }
22480         }
22481         return v;
22482     },
22483
22484     // private
22485     formatDate : function(date, fmt){
22486         return (!date || !(date instanceof Date)) ?
22487                date : date.dateFormat(fmt || this.format);
22488     },
22489
22490     // private
22491     menuListeners : {
22492         select: function(m, d){
22493             this.setValue(d);
22494             this.fireEvent('select', this, d);
22495         },
22496         show : function(){ // retain focus styling
22497             this.onFocus();
22498         },
22499         hide : function(){
22500             this.focus.defer(10, this);
22501             var ml = this.menuListeners;
22502             this.menu.un("select", ml.select,  this);
22503             this.menu.un("show", ml.show,  this);
22504             this.menu.un("hide", ml.hide,  this);
22505         }
22506     },
22507
22508     // private
22509     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22510     onTriggerClick : function(){
22511         if(this.disabled){
22512             return;
22513         }
22514         if(this.menu == null){
22515             this.menu = new Roo.menu.DateMenu();
22516         }
22517         Roo.apply(this.menu.picker,  {
22518             showClear: this.allowBlank,
22519             minDate : this.minValue,
22520             maxDate : this.maxValue,
22521             disabledDatesRE : this.ddMatch,
22522             disabledDatesText : this.disabledDatesText,
22523             disabledDays : this.disabledDays,
22524             disabledDaysText : this.disabledDaysText,
22525             format : this.format,
22526             minText : String.format(this.minText, this.formatDate(this.minValue)),
22527             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22528         });
22529         this.menu.on(Roo.apply({}, this.menuListeners, {
22530             scope:this
22531         }));
22532         this.menu.picker.setValue(this.getValue() || new Date());
22533         this.menu.show(this.el, "tl-bl?");
22534     },
22535
22536     beforeBlur : function(){
22537         var v = this.parseDate(this.getRawValue());
22538         if(v){
22539             this.setValue(v);
22540         }
22541     }
22542
22543     /** @cfg {Boolean} grow @hide */
22544     /** @cfg {Number} growMin @hide */
22545     /** @cfg {Number} growMax @hide */
22546     /**
22547      * @hide
22548      * @method autoSize
22549      */
22550 });/*
22551  * Based on:
22552  * Ext JS Library 1.1.1
22553  * Copyright(c) 2006-2007, Ext JS, LLC.
22554  *
22555  * Originally Released Under LGPL - original licence link has changed is not relivant.
22556  *
22557  * Fork - LGPL
22558  * <script type="text/javascript">
22559  */
22560  
22561
22562 /**
22563  * @class Roo.form.ComboBox
22564  * @extends Roo.form.TriggerField
22565  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22566  * @constructor
22567  * Create a new ComboBox.
22568  * @param {Object} config Configuration options
22569  */
22570 Roo.form.ComboBox = function(config){
22571     Roo.form.ComboBox.superclass.constructor.call(this, config);
22572     this.addEvents({
22573         /**
22574          * @event expand
22575          * Fires when the dropdown list is expanded
22576              * @param {Roo.form.ComboBox} combo This combo box
22577              */
22578         'expand' : true,
22579         /**
22580          * @event collapse
22581          * Fires when the dropdown list is collapsed
22582              * @param {Roo.form.ComboBox} combo This combo box
22583              */
22584         'collapse' : true,
22585         /**
22586          * @event beforeselect
22587          * Fires before a list item is selected. Return false to cancel the selection.
22588              * @param {Roo.form.ComboBox} combo This combo box
22589              * @param {Roo.data.Record} record The data record returned from the underlying store
22590              * @param {Number} index The index of the selected item in the dropdown list
22591              */
22592         'beforeselect' : true,
22593         /**
22594          * @event select
22595          * Fires when a list item is selected
22596              * @param {Roo.form.ComboBox} combo This combo box
22597              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22598              * @param {Number} index The index of the selected item in the dropdown list
22599              */
22600         'select' : true,
22601         /**
22602          * @event beforequery
22603          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22604          * The event object passed has these properties:
22605              * @param {Roo.form.ComboBox} combo This combo box
22606              * @param {String} query The query
22607              * @param {Boolean} forceAll true to force "all" query
22608              * @param {Boolean} cancel true to cancel the query
22609              * @param {Object} e The query event object
22610              */
22611         'beforequery': true,
22612          /**
22613          * @event add
22614          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22615              * @param {Roo.form.ComboBox} combo This combo box
22616              */
22617         'add' : true,
22618         /**
22619          * @event edit
22620          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22621              * @param {Roo.form.ComboBox} combo This combo box
22622              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22623              */
22624         'edit' : true
22625         
22626         
22627     });
22628     if(this.transform){
22629         this.allowDomMove = false;
22630         var s = Roo.getDom(this.transform);
22631         if(!this.hiddenName){
22632             this.hiddenName = s.name;
22633         }
22634         if(!this.store){
22635             this.mode = 'local';
22636             var d = [], opts = s.options;
22637             for(var i = 0, len = opts.length;i < len; i++){
22638                 var o = opts[i];
22639                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22640                 if(o.selected) {
22641                     this.value = value;
22642                 }
22643                 d.push([value, o.text]);
22644             }
22645             this.store = new Roo.data.SimpleStore({
22646                 'id': 0,
22647                 fields: ['value', 'text'],
22648                 data : d
22649             });
22650             this.valueField = 'value';
22651             this.displayField = 'text';
22652         }
22653         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22654         if(!this.lazyRender){
22655             this.target = true;
22656             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22657             s.parentNode.removeChild(s); // remove it
22658             this.render(this.el.parentNode);
22659         }else{
22660             s.parentNode.removeChild(s); // remove it
22661         }
22662
22663     }
22664     if (this.store) {
22665         this.store = Roo.factory(this.store, Roo.data);
22666     }
22667     
22668     this.selectedIndex = -1;
22669     if(this.mode == 'local'){
22670         if(config.queryDelay === undefined){
22671             this.queryDelay = 10;
22672         }
22673         if(config.minChars === undefined){
22674             this.minChars = 0;
22675         }
22676     }
22677 };
22678
22679 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22680     /**
22681      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22682      */
22683     /**
22684      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22685      * rendering into an Roo.Editor, defaults to false)
22686      */
22687     /**
22688      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22689      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22690      */
22691     /**
22692      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22693      */
22694     /**
22695      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22696      * the dropdown list (defaults to undefined, with no header element)
22697      */
22698
22699      /**
22700      * @cfg {String/Roo.Template} tpl The template to use to render the output
22701      */
22702      
22703     // private
22704     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22705     /**
22706      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22707      */
22708     listWidth: undefined,
22709     /**
22710      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22711      * mode = 'remote' or 'text' if mode = 'local')
22712      */
22713     displayField: undefined,
22714     /**
22715      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22716      * mode = 'remote' or 'value' if mode = 'local'). 
22717      * Note: use of a valueField requires the user make a selection
22718      * in order for a value to be mapped.
22719      */
22720     valueField: undefined,
22721     /**
22722      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22723      * field's data value (defaults to the underlying DOM element's name)
22724      */
22725     hiddenName: undefined,
22726     /**
22727      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22728      */
22729     listClass: '',
22730     /**
22731      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22732      */
22733     selectedClass: 'x-combo-selected',
22734     /**
22735      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22736      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22737      * which displays a downward arrow icon).
22738      */
22739     triggerClass : 'x-form-arrow-trigger',
22740     /**
22741      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22742      */
22743     shadow:'sides',
22744     /**
22745      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22746      * anchor positions (defaults to 'tl-bl')
22747      */
22748     listAlign: 'tl-bl?',
22749     /**
22750      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22751      */
22752     maxHeight: 300,
22753     /**
22754      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22755      * query specified by the allQuery config option (defaults to 'query')
22756      */
22757     triggerAction: 'query',
22758     /**
22759      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22760      * (defaults to 4, does not apply if editable = false)
22761      */
22762     minChars : 4,
22763     /**
22764      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22765      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22766      */
22767     typeAhead: false,
22768     /**
22769      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22770      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22771      */
22772     queryDelay: 500,
22773     /**
22774      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22775      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22776      */
22777     pageSize: 0,
22778     /**
22779      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22780      * when editable = true (defaults to false)
22781      */
22782     selectOnFocus:false,
22783     /**
22784      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22785      */
22786     queryParam: 'query',
22787     /**
22788      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22789      * when mode = 'remote' (defaults to 'Loading...')
22790      */
22791     loadingText: 'Loading...',
22792     /**
22793      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22794      */
22795     resizable: false,
22796     /**
22797      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22798      */
22799     handleHeight : 8,
22800     /**
22801      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22802      * traditional select (defaults to true)
22803      */
22804     editable: true,
22805     /**
22806      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22807      */
22808     allQuery: '',
22809     /**
22810      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22811      */
22812     mode: 'remote',
22813     /**
22814      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22815      * listWidth has a higher value)
22816      */
22817     minListWidth : 70,
22818     /**
22819      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22820      * allow the user to set arbitrary text into the field (defaults to false)
22821      */
22822     forceSelection:false,
22823     /**
22824      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22825      * if typeAhead = true (defaults to 250)
22826      */
22827     typeAheadDelay : 250,
22828     /**
22829      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22830      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22831      */
22832     valueNotFoundText : undefined,
22833     /**
22834      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22835      */
22836     blockFocus : false,
22837     
22838     /**
22839      * @cfg {Boolean} disableClear Disable showing of clear button.
22840      */
22841     disableClear : false,
22842     /**
22843      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22844      */
22845     alwaysQuery : false,
22846     
22847     //private
22848     addicon : false,
22849     editicon: false,
22850     
22851     
22852     // private
22853     onRender : function(ct, position){
22854         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22855         if(this.hiddenName){
22856             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22857                     'before', true);
22858             this.hiddenField.value =
22859                 this.hiddenValue !== undefined ? this.hiddenValue :
22860                 this.value !== undefined ? this.value : '';
22861
22862             // prevent input submission
22863             this.el.dom.removeAttribute('name');
22864         }
22865         if(Roo.isGecko){
22866             this.el.dom.setAttribute('autocomplete', 'off');
22867         }
22868
22869         var cls = 'x-combo-list';
22870
22871         this.list = new Roo.Layer({
22872             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22873         });
22874
22875         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22876         this.list.setWidth(lw);
22877         this.list.swallowEvent('mousewheel');
22878         this.assetHeight = 0;
22879
22880         if(this.title){
22881             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22882             this.assetHeight += this.header.getHeight();
22883         }
22884
22885         this.innerList = this.list.createChild({cls:cls+'-inner'});
22886         this.innerList.on('mouseover', this.onViewOver, this);
22887         this.innerList.on('mousemove', this.onViewMove, this);
22888         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22889         
22890         if(this.allowBlank && !this.pageSize && !this.disableClear){
22891             this.footer = this.list.createChild({cls:cls+'-ft'});
22892             this.pageTb = new Roo.Toolbar(this.footer);
22893            
22894         }
22895         if(this.pageSize){
22896             this.footer = this.list.createChild({cls:cls+'-ft'});
22897             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22898                     {pageSize: this.pageSize});
22899             
22900         }
22901         
22902         if (this.pageTb && this.allowBlank && !this.disableClear) {
22903             var _this = this;
22904             this.pageTb.add(new Roo.Toolbar.Fill(), {
22905                 cls: 'x-btn-icon x-btn-clear',
22906                 text: '&#160;',
22907                 handler: function()
22908                 {
22909                     _this.collapse();
22910                     _this.clearValue();
22911                     _this.onSelect(false, -1);
22912                 }
22913             });
22914         }
22915         if (this.footer) {
22916             this.assetHeight += this.footer.getHeight();
22917         }
22918         
22919
22920         if(!this.tpl){
22921             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22922         }
22923
22924         this.view = new Roo.View(this.innerList, this.tpl, {
22925             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22926         });
22927
22928         this.view.on('click', this.onViewClick, this);
22929
22930         this.store.on('beforeload', this.onBeforeLoad, this);
22931         this.store.on('load', this.onLoad, this);
22932         this.store.on('loadexception', this.collapse, this);
22933
22934         if(this.resizable){
22935             this.resizer = new Roo.Resizable(this.list,  {
22936                pinned:true, handles:'se'
22937             });
22938             this.resizer.on('resize', function(r, w, h){
22939                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22940                 this.listWidth = w;
22941                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22942                 this.restrictHeight();
22943             }, this);
22944             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22945         }
22946         if(!this.editable){
22947             this.editable = true;
22948             this.setEditable(false);
22949         }  
22950         
22951         
22952         if (typeof(this.events.add.listeners) != 'undefined') {
22953             
22954             this.addicon = this.wrap.createChild(
22955                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22956        
22957             this.addicon.on('click', function(e) {
22958                 this.fireEvent('add', this);
22959             }, this);
22960         }
22961         if (typeof(this.events.edit.listeners) != 'undefined') {
22962             
22963             this.editicon = this.wrap.createChild(
22964                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22965             if (this.addicon) {
22966                 this.editicon.setStyle('margin-left', '40px');
22967             }
22968             this.editicon.on('click', function(e) {
22969                 
22970                 // we fire even  if inothing is selected..
22971                 this.fireEvent('edit', this, this.lastData );
22972                 
22973             }, this);
22974         }
22975         
22976         
22977         
22978     },
22979
22980     // private
22981     initEvents : function(){
22982         Roo.form.ComboBox.superclass.initEvents.call(this);
22983
22984         this.keyNav = new Roo.KeyNav(this.el, {
22985             "up" : function(e){
22986                 this.inKeyMode = true;
22987                 this.selectPrev();
22988             },
22989
22990             "down" : function(e){
22991                 if(!this.isExpanded()){
22992                     this.onTriggerClick();
22993                 }else{
22994                     this.inKeyMode = true;
22995                     this.selectNext();
22996                 }
22997             },
22998
22999             "enter" : function(e){
23000                 this.onViewClick();
23001                 //return true;
23002             },
23003
23004             "esc" : function(e){
23005                 this.collapse();
23006             },
23007
23008             "tab" : function(e){
23009                 this.onViewClick(false);
23010                 return true;
23011             },
23012
23013             scope : this,
23014
23015             doRelay : function(foo, bar, hname){
23016                 if(hname == 'down' || this.scope.isExpanded()){
23017                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23018                 }
23019                 return true;
23020             },
23021
23022             forceKeyDown: true
23023         });
23024         this.queryDelay = Math.max(this.queryDelay || 10,
23025                 this.mode == 'local' ? 10 : 250);
23026         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23027         if(this.typeAhead){
23028             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23029         }
23030         if(this.editable !== false){
23031             this.el.on("keyup", this.onKeyUp, this);
23032         }
23033         if(this.forceSelection){
23034             this.on('blur', this.doForce, this);
23035         }
23036     },
23037
23038     onDestroy : function(){
23039         if(this.view){
23040             this.view.setStore(null);
23041             this.view.el.removeAllListeners();
23042             this.view.el.remove();
23043             this.view.purgeListeners();
23044         }
23045         if(this.list){
23046             this.list.destroy();
23047         }
23048         if(this.store){
23049             this.store.un('beforeload', this.onBeforeLoad, this);
23050             this.store.un('load', this.onLoad, this);
23051             this.store.un('loadexception', this.collapse, this);
23052         }
23053         Roo.form.ComboBox.superclass.onDestroy.call(this);
23054     },
23055
23056     // private
23057     fireKey : function(e){
23058         if(e.isNavKeyPress() && !this.list.isVisible()){
23059             this.fireEvent("specialkey", this, e);
23060         }
23061     },
23062
23063     // private
23064     onResize: function(w, h){
23065         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23066         
23067         if(typeof w != 'number'){
23068             // we do not handle it!?!?
23069             return;
23070         }
23071         var tw = this.trigger.getWidth();
23072         tw += this.addicon ? this.addicon.getWidth() : 0;
23073         tw += this.editicon ? this.editicon.getWidth() : 0;
23074         var x = w - tw;
23075         this.el.setWidth( this.adjustWidth('input', x));
23076             
23077         this.trigger.setStyle('left', x+'px');
23078         
23079         if(this.list && this.listWidth === undefined){
23080             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23081             this.list.setWidth(lw);
23082             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23083         }
23084         
23085     
23086         
23087     },
23088
23089     /**
23090      * Allow or prevent the user from directly editing the field text.  If false is passed,
23091      * the user will only be able to select from the items defined in the dropdown list.  This method
23092      * is the runtime equivalent of setting the 'editable' config option at config time.
23093      * @param {Boolean} value True to allow the user to directly edit the field text
23094      */
23095     setEditable : function(value){
23096         if(value == this.editable){
23097             return;
23098         }
23099         this.editable = value;
23100         if(!value){
23101             this.el.dom.setAttribute('readOnly', true);
23102             this.el.on('mousedown', this.onTriggerClick,  this);
23103             this.el.addClass('x-combo-noedit');
23104         }else{
23105             this.el.dom.setAttribute('readOnly', false);
23106             this.el.un('mousedown', this.onTriggerClick,  this);
23107             this.el.removeClass('x-combo-noedit');
23108         }
23109     },
23110
23111     // private
23112     onBeforeLoad : function(){
23113         if(!this.hasFocus){
23114             return;
23115         }
23116         this.innerList.update(this.loadingText ?
23117                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23118         this.restrictHeight();
23119         this.selectedIndex = -1;
23120     },
23121
23122     // private
23123     onLoad : function(){
23124         if(!this.hasFocus){
23125             return;
23126         }
23127         if(this.store.getCount() > 0){
23128             this.expand();
23129             this.restrictHeight();
23130             if(this.lastQuery == this.allQuery){
23131                 if(this.editable){
23132                     this.el.dom.select();
23133                 }
23134                 if(!this.selectByValue(this.value, true)){
23135                     this.select(0, true);
23136                 }
23137             }else{
23138                 this.selectNext();
23139                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23140                     this.taTask.delay(this.typeAheadDelay);
23141                 }
23142             }
23143         }else{
23144             this.onEmptyResults();
23145         }
23146         //this.el.focus();
23147     },
23148
23149     // private
23150     onTypeAhead : function(){
23151         if(this.store.getCount() > 0){
23152             var r = this.store.getAt(0);
23153             var newValue = r.data[this.displayField];
23154             var len = newValue.length;
23155             var selStart = this.getRawValue().length;
23156             if(selStart != len){
23157                 this.setRawValue(newValue);
23158                 this.selectText(selStart, newValue.length);
23159             }
23160         }
23161     },
23162
23163     // private
23164     onSelect : function(record, index){
23165         if(this.fireEvent('beforeselect', this, record, index) !== false){
23166             this.setFromData(index > -1 ? record.data : false);
23167             this.collapse();
23168             this.fireEvent('select', this, record, index);
23169         }
23170     },
23171
23172     /**
23173      * Returns the currently selected field value or empty string if no value is set.
23174      * @return {String} value The selected value
23175      */
23176     getValue : function(){
23177         if(this.valueField){
23178             return typeof this.value != 'undefined' ? this.value : '';
23179         }else{
23180             return Roo.form.ComboBox.superclass.getValue.call(this);
23181         }
23182     },
23183
23184     /**
23185      * Clears any text/value currently set in the field
23186      */
23187     clearValue : function(){
23188         if(this.hiddenField){
23189             this.hiddenField.value = '';
23190         }
23191         this.value = '';
23192         this.setRawValue('');
23193         this.lastSelectionText = '';
23194         this.applyEmptyText();
23195     },
23196
23197     /**
23198      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23199      * will be displayed in the field.  If the value does not match the data value of an existing item,
23200      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23201      * Otherwise the field will be blank (although the value will still be set).
23202      * @param {String} value The value to match
23203      */
23204     setValue : function(v){
23205         var text = v;
23206         if(this.valueField){
23207             var r = this.findRecord(this.valueField, v);
23208             if(r){
23209                 text = r.data[this.displayField];
23210             }else if(this.valueNotFoundText !== undefined){
23211                 text = this.valueNotFoundText;
23212             }
23213         }
23214         this.lastSelectionText = text;
23215         if(this.hiddenField){
23216             this.hiddenField.value = v;
23217         }
23218         Roo.form.ComboBox.superclass.setValue.call(this, text);
23219         this.value = v;
23220     },
23221     /**
23222      * @property {Object} the last set data for the element
23223      */
23224     
23225     lastData : false,
23226     /**
23227      * Sets the value of the field based on a object which is related to the record format for the store.
23228      * @param {Object} value the value to set as. or false on reset?
23229      */
23230     setFromData : function(o){
23231         var dv = ''; // display value
23232         var vv = ''; // value value..
23233         this.lastData = o;
23234         if (this.displayField) {
23235             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23236         } else {
23237             // this is an error condition!!!
23238             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23239         }
23240         
23241         if(this.valueField){
23242             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23243         }
23244         if(this.hiddenField){
23245             this.hiddenField.value = vv;
23246             
23247             this.lastSelectionText = dv;
23248             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23249             this.value = vv;
23250             return;
23251         }
23252         // no hidden field.. - we store the value in 'value', but still display
23253         // display field!!!!
23254         this.lastSelectionText = dv;
23255         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23256         this.value = vv;
23257         
23258         
23259     },
23260     // private
23261     reset : function(){
23262         // overridden so that last data is reset..
23263         this.setValue(this.originalValue);
23264         this.clearInvalid();
23265         this.lastData = false;
23266     },
23267     // private
23268     findRecord : function(prop, value){
23269         var record;
23270         if(this.store.getCount() > 0){
23271             this.store.each(function(r){
23272                 if(r.data[prop] == value){
23273                     record = r;
23274                     return false;
23275                 }
23276             });
23277         }
23278         return record;
23279     },
23280
23281     // private
23282     onViewMove : function(e, t){
23283         this.inKeyMode = false;
23284     },
23285
23286     // private
23287     onViewOver : function(e, t){
23288         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23289             return;
23290         }
23291         var item = this.view.findItemFromChild(t);
23292         if(item){
23293             var index = this.view.indexOf(item);
23294             this.select(index, false);
23295         }
23296     },
23297
23298     // private
23299     onViewClick : function(doFocus){
23300         var index = this.view.getSelectedIndexes()[0];
23301         var r = this.store.getAt(index);
23302         if(r){
23303             this.onSelect(r, index);
23304         }
23305         if(doFocus !== false && !this.blockFocus){
23306             this.el.focus();
23307         }
23308     },
23309
23310     // private
23311     restrictHeight : function(){
23312         this.innerList.dom.style.height = '';
23313         var inner = this.innerList.dom;
23314         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23315         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23316         this.list.beginUpdate();
23317         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23318         this.list.alignTo(this.el, this.listAlign);
23319         this.list.endUpdate();
23320     },
23321
23322     // private
23323     onEmptyResults : function(){
23324         this.collapse();
23325     },
23326
23327     /**
23328      * Returns true if the dropdown list is expanded, else false.
23329      */
23330     isExpanded : function(){
23331         return this.list.isVisible();
23332     },
23333
23334     /**
23335      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23336      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23337      * @param {String} value The data value of the item to select
23338      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23339      * selected item if it is not currently in view (defaults to true)
23340      * @return {Boolean} True if the value matched an item in the list, else false
23341      */
23342     selectByValue : function(v, scrollIntoView){
23343         if(v !== undefined && v !== null){
23344             var r = this.findRecord(this.valueField || this.displayField, v);
23345             if(r){
23346                 this.select(this.store.indexOf(r), scrollIntoView);
23347                 return true;
23348             }
23349         }
23350         return false;
23351     },
23352
23353     /**
23354      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23355      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23356      * @param {Number} index The zero-based index of the list item to select
23357      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23358      * selected item if it is not currently in view (defaults to true)
23359      */
23360     select : function(index, scrollIntoView){
23361         this.selectedIndex = index;
23362         this.view.select(index);
23363         if(scrollIntoView !== false){
23364             var el = this.view.getNode(index);
23365             if(el){
23366                 this.innerList.scrollChildIntoView(el, false);
23367             }
23368         }
23369     },
23370
23371     // private
23372     selectNext : function(){
23373         var ct = this.store.getCount();
23374         if(ct > 0){
23375             if(this.selectedIndex == -1){
23376                 this.select(0);
23377             }else if(this.selectedIndex < ct-1){
23378                 this.select(this.selectedIndex+1);
23379             }
23380         }
23381     },
23382
23383     // private
23384     selectPrev : function(){
23385         var ct = this.store.getCount();
23386         if(ct > 0){
23387             if(this.selectedIndex == -1){
23388                 this.select(0);
23389             }else if(this.selectedIndex != 0){
23390                 this.select(this.selectedIndex-1);
23391             }
23392         }
23393     },
23394
23395     // private
23396     onKeyUp : function(e){
23397         if(this.editable !== false && !e.isSpecialKey()){
23398             this.lastKey = e.getKey();
23399             this.dqTask.delay(this.queryDelay);
23400         }
23401     },
23402
23403     // private
23404     validateBlur : function(){
23405         return !this.list || !this.list.isVisible();   
23406     },
23407
23408     // private
23409     initQuery : function(){
23410         this.doQuery(this.getRawValue());
23411     },
23412
23413     // private
23414     doForce : function(){
23415         if(this.el.dom.value.length > 0){
23416             this.el.dom.value =
23417                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23418             this.applyEmptyText();
23419         }
23420     },
23421
23422     /**
23423      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23424      * query allowing the query action to be canceled if needed.
23425      * @param {String} query The SQL query to execute
23426      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23427      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23428      * saved in the current store (defaults to false)
23429      */
23430     doQuery : function(q, forceAll){
23431         if(q === undefined || q === null){
23432             q = '';
23433         }
23434         var qe = {
23435             query: q,
23436             forceAll: forceAll,
23437             combo: this,
23438             cancel:false
23439         };
23440         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23441             return false;
23442         }
23443         q = qe.query;
23444         forceAll = qe.forceAll;
23445         if(forceAll === true || (q.length >= this.minChars)){
23446             if(this.lastQuery != q || this.alwaysQuery){
23447                 this.lastQuery = q;
23448                 if(this.mode == 'local'){
23449                     this.selectedIndex = -1;
23450                     if(forceAll){
23451                         this.store.clearFilter();
23452                     }else{
23453                         this.store.filter(this.displayField, q);
23454                     }
23455                     this.onLoad();
23456                 }else{
23457                     this.store.baseParams[this.queryParam] = q;
23458                     this.store.load({
23459                         params: this.getParams(q)
23460                     });
23461                     this.expand();
23462                 }
23463             }else{
23464                 this.selectedIndex = -1;
23465                 this.onLoad();   
23466             }
23467         }
23468     },
23469
23470     // private
23471     getParams : function(q){
23472         var p = {};
23473         //p[this.queryParam] = q;
23474         if(this.pageSize){
23475             p.start = 0;
23476             p.limit = this.pageSize;
23477         }
23478         return p;
23479     },
23480
23481     /**
23482      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23483      */
23484     collapse : function(){
23485         if(!this.isExpanded()){
23486             return;
23487         }
23488         this.list.hide();
23489         Roo.get(document).un('mousedown', this.collapseIf, this);
23490         Roo.get(document).un('mousewheel', this.collapseIf, this);
23491         if (!this.editable) {
23492             Roo.get(document).un('keydown', this.listKeyPress, this);
23493         }
23494         this.fireEvent('collapse', this);
23495     },
23496
23497     // private
23498     collapseIf : function(e){
23499         if(!e.within(this.wrap) && !e.within(this.list)){
23500             this.collapse();
23501         }
23502     },
23503
23504     /**
23505      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23506      */
23507     expand : function(){
23508         if(this.isExpanded() || !this.hasFocus){
23509             return;
23510         }
23511         this.list.alignTo(this.el, this.listAlign);
23512         this.list.show();
23513         Roo.get(document).on('mousedown', this.collapseIf, this);
23514         Roo.get(document).on('mousewheel', this.collapseIf, this);
23515         if (!this.editable) {
23516             Roo.get(document).on('keydown', this.listKeyPress, this);
23517         }
23518         
23519         this.fireEvent('expand', this);
23520     },
23521
23522     // private
23523     // Implements the default empty TriggerField.onTriggerClick function
23524     onTriggerClick : function(){
23525         if(this.disabled){
23526             return;
23527         }
23528         if(this.isExpanded()){
23529             this.collapse();
23530             if (!this.blockFocus) {
23531                 this.el.focus();
23532             }
23533             
23534         }else {
23535             this.hasFocus = true;
23536             if(this.triggerAction == 'all') {
23537                 this.doQuery(this.allQuery, true);
23538             } else {
23539                 this.doQuery(this.getRawValue());
23540             }
23541             if (!this.blockFocus) {
23542                 this.el.focus();
23543             }
23544         }
23545     },
23546     listKeyPress : function(e)
23547     {
23548         //Roo.log('listkeypress');
23549         // scroll to first matching element based on key pres..
23550         if (e.isSpecialKey()) {
23551             return false;
23552         }
23553         var k = String.fromCharCode(e.getKey()).toUpperCase();
23554         //Roo.log(k);
23555         var match  = false;
23556         var csel = this.view.getSelectedNodes();
23557         var cselitem = false;
23558         if (csel.length) {
23559             var ix = this.view.indexOf(csel[0]);
23560             cselitem  = this.store.getAt(ix);
23561             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23562                 cselitem = false;
23563             }
23564             
23565         }
23566         
23567         this.store.each(function(v) { 
23568             if (cselitem) {
23569                 // start at existing selection.
23570                 if (cselitem.id == v.id) {
23571                     cselitem = false;
23572                 }
23573                 return;
23574             }
23575                 
23576             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23577                 match = this.store.indexOf(v);
23578                 return false;
23579             }
23580         }, this);
23581         
23582         if (match === false) {
23583             return true; // no more action?
23584         }
23585         // scroll to?
23586         this.view.select(match);
23587         var sn = Roo.get(this.view.getSelectedNodes()[0])
23588         sn.scrollIntoView(sn.dom.parentNode, false);
23589     }
23590
23591     /** 
23592     * @cfg {Boolean} grow 
23593     * @hide 
23594     */
23595     /** 
23596     * @cfg {Number} growMin 
23597     * @hide 
23598     */
23599     /** 
23600     * @cfg {Number} growMax 
23601     * @hide 
23602     */
23603     /**
23604      * @hide
23605      * @method autoSize
23606      */
23607 });/*
23608  * Based on:
23609  * Ext JS Library 1.1.1
23610  * Copyright(c) 2006-2007, Ext JS, LLC.
23611  *
23612  * Originally Released Under LGPL - original licence link has changed is not relivant.
23613  *
23614  * Fork - LGPL
23615  * <script type="text/javascript">
23616  */
23617 /**
23618  * @class Roo.form.Checkbox
23619  * @extends Roo.form.Field
23620  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23621  * @constructor
23622  * Creates a new Checkbox
23623  * @param {Object} config Configuration options
23624  */
23625 Roo.form.Checkbox = function(config){
23626     Roo.form.Checkbox.superclass.constructor.call(this, config);
23627     this.addEvents({
23628         /**
23629          * @event check
23630          * Fires when the checkbox is checked or unchecked.
23631              * @param {Roo.form.Checkbox} this This checkbox
23632              * @param {Boolean} checked The new checked value
23633              */
23634         check : true
23635     });
23636 };
23637
23638 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23639     /**
23640      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23641      */
23642     focusClass : undefined,
23643     /**
23644      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23645      */
23646     fieldClass: "x-form-field",
23647     /**
23648      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23649      */
23650     checked: false,
23651     /**
23652      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23653      * {tag: "input", type: "checkbox", autocomplete: "off"})
23654      */
23655     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23656     /**
23657      * @cfg {String} boxLabel The text that appears beside the checkbox
23658      */
23659     boxLabel : "",
23660     /**
23661      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23662      */  
23663     inputValue : '1',
23664     /**
23665      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23666      */
23667      valueOff: '0', // value when not checked..
23668
23669     actionMode : 'viewEl', 
23670     //
23671     // private
23672     itemCls : 'x-menu-check-item x-form-item',
23673     groupClass : 'x-menu-group-item',
23674     inputType : 'hidden',
23675     
23676     
23677     inSetChecked: false, // check that we are not calling self...
23678     
23679     inputElement: false, // real input element?
23680     basedOn: false, // ????
23681     
23682     isFormField: true, // not sure where this is needed!!!!
23683
23684     onResize : function(){
23685         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23686         if(!this.boxLabel){
23687             this.el.alignTo(this.wrap, 'c-c');
23688         }
23689     },
23690
23691     initEvents : function(){
23692         Roo.form.Checkbox.superclass.initEvents.call(this);
23693         this.el.on("click", this.onClick,  this);
23694         this.el.on("change", this.onClick,  this);
23695     },
23696
23697
23698     getResizeEl : function(){
23699         return this.wrap;
23700     },
23701
23702     getPositionEl : function(){
23703         return this.wrap;
23704     },
23705
23706     // private
23707     onRender : function(ct, position){
23708         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23709         /*
23710         if(this.inputValue !== undefined){
23711             this.el.dom.value = this.inputValue;
23712         }
23713         */
23714         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23715         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23716         var viewEl = this.wrap.createChild({ 
23717             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23718         this.viewEl = viewEl;   
23719         this.wrap.on('click', this.onClick,  this); 
23720         
23721         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23722         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23723         
23724         
23725         
23726         if(this.boxLabel){
23727             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23728         //    viewEl.on('click', this.onClick,  this); 
23729         }
23730         //if(this.checked){
23731             this.setChecked(this.checked);
23732         //}else{
23733             //this.checked = this.el.dom;
23734         //}
23735
23736     },
23737
23738     // private
23739     initValue : Roo.emptyFn,
23740
23741     /**
23742      * Returns the checked state of the checkbox.
23743      * @return {Boolean} True if checked, else false
23744      */
23745     getValue : function(){
23746         if(this.el){
23747             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23748         }
23749         return this.valueOff;
23750         
23751     },
23752
23753         // private
23754     onClick : function(){ 
23755         this.setChecked(!this.checked);
23756
23757         //if(this.el.dom.checked != this.checked){
23758         //    this.setValue(this.el.dom.checked);
23759        // }
23760     },
23761
23762     /**
23763      * Sets the checked state of the checkbox.
23764      * On is always based on a string comparison between inputValue and the param.
23765      * @param {Boolean/String} value - the value to set 
23766      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23767      */
23768     setValue : function(v,suppressEvent){
23769         
23770         
23771         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23772         //if(this.el && this.el.dom){
23773         //    this.el.dom.checked = this.checked;
23774         //    this.el.dom.defaultChecked = this.checked;
23775         //}
23776         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23777         //this.fireEvent("check", this, this.checked);
23778     },
23779     // private..
23780     setChecked : function(state,suppressEvent)
23781     {
23782         if (this.inSetChecked) {
23783             this.checked = state;
23784             return;
23785         }
23786         
23787     
23788         if(this.wrap){
23789             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23790         }
23791         this.checked = state;
23792         if(suppressEvent !== true){
23793             this.fireEvent('check', this, state);
23794         }
23795         this.inSetChecked = true;
23796         this.el.dom.value = state ? this.inputValue : this.valueOff;
23797         this.inSetChecked = false;
23798         
23799     },
23800     // handle setting of hidden value by some other method!!?!?
23801     setFromHidden: function()
23802     {
23803         if(!this.el){
23804             return;
23805         }
23806         //console.log("SET FROM HIDDEN");
23807         //alert('setFrom hidden');
23808         this.setValue(this.el.dom.value);
23809     },
23810     
23811     onDestroy : function()
23812     {
23813         if(this.viewEl){
23814             Roo.get(this.viewEl).remove();
23815         }
23816          
23817         Roo.form.Checkbox.superclass.onDestroy.call(this);
23818     }
23819
23820 });/*
23821  * Based on:
23822  * Ext JS Library 1.1.1
23823  * Copyright(c) 2006-2007, Ext JS, LLC.
23824  *
23825  * Originally Released Under LGPL - original licence link has changed is not relivant.
23826  *
23827  * Fork - LGPL
23828  * <script type="text/javascript">
23829  */
23830  
23831 /**
23832  * @class Roo.form.Radio
23833  * @extends Roo.form.Checkbox
23834  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23835  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23836  * @constructor
23837  * Creates a new Radio
23838  * @param {Object} config Configuration options
23839  */
23840 Roo.form.Radio = function(){
23841     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23842 };
23843 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23844     inputType: 'radio',
23845
23846     /**
23847      * If this radio is part of a group, it will return the selected value
23848      * @return {String}
23849      */
23850     getGroupValue : function(){
23851         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23852     }
23853 });//<script type="text/javascript">
23854
23855 /*
23856  * Ext JS Library 1.1.1
23857  * Copyright(c) 2006-2007, Ext JS, LLC.
23858  * licensing@extjs.com
23859  * 
23860  * http://www.extjs.com/license
23861  */
23862  
23863  /*
23864   * 
23865   * Known bugs:
23866   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23867   * - IE ? - no idea how much works there.
23868   * 
23869   * 
23870   * 
23871   */
23872  
23873
23874 /**
23875  * @class Ext.form.HtmlEditor
23876  * @extends Ext.form.Field
23877  * Provides a lightweight HTML Editor component.
23878  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23879  * 
23880  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23881  * supported by this editor.</b><br/><br/>
23882  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23883  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23884  */
23885 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23886       /**
23887      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23888      */
23889     toolbars : false,
23890     /**
23891      * @cfg {String} createLinkText The default text for the create link prompt
23892      */
23893     createLinkText : 'Please enter the URL for the link:',
23894     /**
23895      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23896      */
23897     defaultLinkValue : 'http:/'+'/',
23898    
23899     
23900     // id of frame..
23901     frameId: false,
23902     
23903     // private properties
23904     validationEvent : false,
23905     deferHeight: true,
23906     initialized : false,
23907     activated : false,
23908     sourceEditMode : false,
23909     onFocus : Roo.emptyFn,
23910     iframePad:3,
23911     hideMode:'offsets',
23912     defaultAutoCreate : {
23913         tag: "textarea",
23914         style:"width:500px;height:300px;",
23915         autocomplete: "off"
23916     },
23917
23918     // private
23919     initComponent : function(){
23920         this.addEvents({
23921             /**
23922              * @event initialize
23923              * Fires when the editor is fully initialized (including the iframe)
23924              * @param {HtmlEditor} this
23925              */
23926             initialize: true,
23927             /**
23928              * @event activate
23929              * Fires when the editor is first receives the focus. Any insertion must wait
23930              * until after this event.
23931              * @param {HtmlEditor} this
23932              */
23933             activate: true,
23934              /**
23935              * @event beforesync
23936              * Fires before the textarea is updated with content from the editor iframe. Return false
23937              * to cancel the sync.
23938              * @param {HtmlEditor} this
23939              * @param {String} html
23940              */
23941             beforesync: true,
23942              /**
23943              * @event beforepush
23944              * Fires before the iframe editor is updated with content from the textarea. Return false
23945              * to cancel the push.
23946              * @param {HtmlEditor} this
23947              * @param {String} html
23948              */
23949             beforepush: true,
23950              /**
23951              * @event sync
23952              * Fires when the textarea is updated with content from the editor iframe.
23953              * @param {HtmlEditor} this
23954              * @param {String} html
23955              */
23956             sync: true,
23957              /**
23958              * @event push
23959              * Fires when the iframe editor is updated with content from the textarea.
23960              * @param {HtmlEditor} this
23961              * @param {String} html
23962              */
23963             push: true,
23964              /**
23965              * @event editmodechange
23966              * Fires when the editor switches edit modes
23967              * @param {HtmlEditor} this
23968              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23969              */
23970             editmodechange: true,
23971             /**
23972              * @event editorevent
23973              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23974              * @param {HtmlEditor} this
23975              */
23976             editorevent: true
23977         })
23978     },
23979
23980     /**
23981      * Protected method that will not generally be called directly. It
23982      * is called when the editor creates its toolbar. Override this method if you need to
23983      * add custom toolbar buttons.
23984      * @param {HtmlEditor} editor
23985      */
23986     createToolbar : function(editor){
23987         if (!editor.toolbars || !editor.toolbars.length) {
23988             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23989         }
23990         
23991         for (var i =0 ; i < editor.toolbars.length;i++) {
23992             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23993             editor.toolbars[i].init(editor);
23994         }
23995          
23996         
23997     },
23998
23999     /**
24000      * Protected method that will not generally be called directly. It
24001      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24002      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24003      */
24004     getDocMarkup : function(){
24005         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24006     },
24007
24008     // private
24009     onRender : function(ct, position){
24010         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24011         this.el.dom.style.border = '0 none';
24012         this.el.dom.setAttribute('tabIndex', -1);
24013         this.el.addClass('x-hidden');
24014         if(Roo.isIE){ // fix IE 1px bogus margin
24015             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24016         }
24017         this.wrap = this.el.wrap({
24018             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24019         });
24020
24021         this.frameId = Roo.id();
24022         this.createToolbar(this);
24023         
24024         
24025         
24026         
24027       
24028         
24029         var iframe = this.wrap.createChild({
24030             tag: 'iframe',
24031             id: this.frameId,
24032             name: this.frameId,
24033             frameBorder : 'no',
24034             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24035         });
24036         
24037        // console.log(iframe);
24038         //this.wrap.dom.appendChild(iframe);
24039
24040         this.iframe = iframe.dom;
24041
24042          this.assignDocWin();
24043         
24044         this.doc.designMode = 'on';
24045        
24046         this.doc.open();
24047         this.doc.write(this.getDocMarkup());
24048         this.doc.close();
24049
24050         
24051         var task = { // must defer to wait for browser to be ready
24052             run : function(){
24053                 //console.log("run task?" + this.doc.readyState);
24054                 this.assignDocWin();
24055                 if(this.doc.body || this.doc.readyState == 'complete'){
24056                     try {
24057                         this.doc.designMode="on";
24058                     } catch (e) {
24059                         return;
24060                     }
24061                     Roo.TaskMgr.stop(task);
24062                     this.initEditor.defer(10, this);
24063                 }
24064             },
24065             interval : 10,
24066             duration:10000,
24067             scope: this
24068         };
24069         Roo.TaskMgr.start(task);
24070
24071         if(!this.width){
24072             this.setSize(this.el.getSize());
24073         }
24074     },
24075
24076     // private
24077     onResize : function(w, h){
24078         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24079         if(this.el && this.iframe){
24080             if(typeof w == 'number'){
24081                 var aw = w - this.wrap.getFrameWidth('lr');
24082                 this.el.setWidth(this.adjustWidth('textarea', aw));
24083                 this.iframe.style.width = aw + 'px';
24084             }
24085             if(typeof h == 'number'){
24086                 var tbh = 0;
24087                 for (var i =0; i < this.toolbars.length;i++) {
24088                     // fixme - ask toolbars for heights?
24089                     tbh += this.toolbars[i].tb.el.getHeight();
24090                 }
24091                 
24092                 
24093                 
24094                 
24095                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24096                 this.el.setHeight(this.adjustWidth('textarea', ah));
24097                 this.iframe.style.height = ah + 'px';
24098                 if(this.doc){
24099                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24100                 }
24101             }
24102         }
24103     },
24104
24105     /**
24106      * Toggles the editor between standard and source edit mode.
24107      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24108      */
24109     toggleSourceEdit : function(sourceEditMode){
24110         
24111         this.sourceEditMode = sourceEditMode === true;
24112         
24113         if(this.sourceEditMode){
24114           
24115             this.syncValue();
24116             this.iframe.className = 'x-hidden';
24117             this.el.removeClass('x-hidden');
24118             this.el.dom.removeAttribute('tabIndex');
24119             this.el.focus();
24120         }else{
24121              
24122             this.pushValue();
24123             this.iframe.className = '';
24124             this.el.addClass('x-hidden');
24125             this.el.dom.setAttribute('tabIndex', -1);
24126             this.deferFocus();
24127         }
24128         this.setSize(this.wrap.getSize());
24129         this.fireEvent('editmodechange', this, this.sourceEditMode);
24130     },
24131
24132     // private used internally
24133     createLink : function(){
24134         var url = prompt(this.createLinkText, this.defaultLinkValue);
24135         if(url && url != 'http:/'+'/'){
24136             this.relayCmd('createlink', url);
24137         }
24138     },
24139
24140     // private (for BoxComponent)
24141     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24142
24143     // private (for BoxComponent)
24144     getResizeEl : function(){
24145         return this.wrap;
24146     },
24147
24148     // private (for BoxComponent)
24149     getPositionEl : function(){
24150         return this.wrap;
24151     },
24152
24153     // private
24154     initEvents : function(){
24155         this.originalValue = this.getValue();
24156     },
24157
24158     /**
24159      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24160      * @method
24161      */
24162     markInvalid : Roo.emptyFn,
24163     /**
24164      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24165      * @method
24166      */
24167     clearInvalid : Roo.emptyFn,
24168
24169     setValue : function(v){
24170         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24171         this.pushValue();
24172     },
24173
24174     /**
24175      * Protected method that will not generally be called directly. If you need/want
24176      * custom HTML cleanup, this is the method you should override.
24177      * @param {String} html The HTML to be cleaned
24178      * return {String} The cleaned HTML
24179      */
24180     cleanHtml : function(html){
24181         html = String(html);
24182         if(html.length > 5){
24183             if(Roo.isSafari){ // strip safari nonsense
24184                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24185             }
24186         }
24187         if(html == '&nbsp;'){
24188             html = '';
24189         }
24190         return html;
24191     },
24192
24193     /**
24194      * Protected method that will not generally be called directly. Syncs the contents
24195      * of the editor iframe with the textarea.
24196      */
24197     syncValue : function(){
24198         if(this.initialized){
24199             var bd = (this.doc.body || this.doc.documentElement);
24200             var html = bd.innerHTML;
24201             if(Roo.isSafari){
24202                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24203                 var m = bs.match(/text-align:(.*?);/i);
24204                 if(m && m[1]){
24205                     html = '<div style="'+m[0]+'">' + html + '</div>';
24206                 }
24207             }
24208             html = this.cleanHtml(html);
24209             if(this.fireEvent('beforesync', this, html) !== false){
24210                 this.el.dom.value = html;
24211                 this.fireEvent('sync', this, html);
24212             }
24213         }
24214     },
24215
24216     /**
24217      * Protected method that will not generally be called directly. Pushes the value of the textarea
24218      * into the iframe editor.
24219      */
24220     pushValue : function(){
24221         if(this.initialized){
24222             var v = this.el.dom.value;
24223             if(v.length < 1){
24224                 v = '&#160;';
24225             }
24226             if(this.fireEvent('beforepush', this, v) !== false){
24227                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24228                 this.fireEvent('push', this, v);
24229             }
24230         }
24231     },
24232
24233     // private
24234     deferFocus : function(){
24235         this.focus.defer(10, this);
24236     },
24237
24238     // doc'ed in Field
24239     focus : function(){
24240         if(this.win && !this.sourceEditMode){
24241             this.win.focus();
24242         }else{
24243             this.el.focus();
24244         }
24245     },
24246     
24247     assignDocWin: function()
24248     {
24249         var iframe = this.iframe;
24250         
24251          if(Roo.isIE){
24252             this.doc = iframe.contentWindow.document;
24253             this.win = iframe.contentWindow;
24254         } else {
24255             if (!Roo.get(this.frameId)) {
24256                 return;
24257             }
24258             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24259             this.win = Roo.get(this.frameId).dom.contentWindow;
24260         }
24261     },
24262     
24263     // private
24264     initEditor : function(){
24265         //console.log("INIT EDITOR");
24266         this.assignDocWin();
24267         
24268         
24269         
24270         this.doc.designMode="on";
24271         this.doc.open();
24272         this.doc.write(this.getDocMarkup());
24273         this.doc.close();
24274         
24275         var dbody = (this.doc.body || this.doc.documentElement);
24276         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24277         // this copies styles from the containing element into thsi one..
24278         // not sure why we need all of this..
24279         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24280         ss['background-attachment'] = 'fixed'; // w3c
24281         dbody.bgProperties = 'fixed'; // ie
24282         Roo.DomHelper.applyStyles(dbody, ss);
24283         Roo.EventManager.on(this.doc, {
24284             'mousedown': this.onEditorEvent,
24285             'dblclick': this.onEditorEvent,
24286             'click': this.onEditorEvent,
24287             'keyup': this.onEditorEvent,
24288             buffer:100,
24289             scope: this
24290         });
24291         if(Roo.isGecko){
24292             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24293         }
24294         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24295             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24296         }
24297         this.initialized = true;
24298
24299         this.fireEvent('initialize', this);
24300         this.pushValue();
24301     },
24302
24303     // private
24304     onDestroy : function(){
24305         
24306         
24307         
24308         if(this.rendered){
24309             
24310             for (var i =0; i < this.toolbars.length;i++) {
24311                 // fixme - ask toolbars for heights?
24312                 this.toolbars[i].onDestroy();
24313             }
24314             
24315             this.wrap.dom.innerHTML = '';
24316             this.wrap.remove();
24317         }
24318     },
24319
24320     // private
24321     onFirstFocus : function(){
24322         
24323         this.assignDocWin();
24324         
24325         
24326         this.activated = true;
24327         for (var i =0; i < this.toolbars.length;i++) {
24328             this.toolbars[i].onFirstFocus();
24329         }
24330        
24331         if(Roo.isGecko){ // prevent silly gecko errors
24332             this.win.focus();
24333             var s = this.win.getSelection();
24334             if(!s.focusNode || s.focusNode.nodeType != 3){
24335                 var r = s.getRangeAt(0);
24336                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24337                 r.collapse(true);
24338                 this.deferFocus();
24339             }
24340             try{
24341                 this.execCmd('useCSS', true);
24342                 this.execCmd('styleWithCSS', false);
24343             }catch(e){}
24344         }
24345         this.fireEvent('activate', this);
24346     },
24347
24348     // private
24349     adjustFont: function(btn){
24350         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24351         //if(Roo.isSafari){ // safari
24352         //    adjust *= 2;
24353        // }
24354         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24355         if(Roo.isSafari){ // safari
24356             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24357             v =  (v < 10) ? 10 : v;
24358             v =  (v > 48) ? 48 : v;
24359             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24360             
24361         }
24362         
24363         
24364         v = Math.max(1, v+adjust);
24365         
24366         this.execCmd('FontSize', v  );
24367     },
24368
24369     onEditorEvent : function(e){
24370         this.fireEvent('editorevent', this, e);
24371       //  this.updateToolbar();
24372         this.syncValue();
24373     },
24374
24375     insertTag : function(tg)
24376     {
24377         // could be a bit smarter... -> wrap the current selected tRoo..
24378         
24379         this.execCmd("formatblock",   tg);
24380         
24381     },
24382     
24383     insertText : function(txt)
24384     {
24385         
24386         
24387         range = this.createRange();
24388         range.deleteContents();
24389                //alert(Sender.getAttribute('label'));
24390                
24391         range.insertNode(this.doc.createTextNode(txt));
24392     } ,
24393     
24394     // private
24395     relayBtnCmd : function(btn){
24396         this.relayCmd(btn.cmd);
24397     },
24398
24399     /**
24400      * Executes a Midas editor command on the editor document and performs necessary focus and
24401      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24402      * @param {String} cmd The Midas command
24403      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24404      */
24405     relayCmd : function(cmd, value){
24406         this.win.focus();
24407         this.execCmd(cmd, value);
24408         this.fireEvent('editorevent', this);
24409         //this.updateToolbar();
24410         this.deferFocus();
24411     },
24412
24413     /**
24414      * Executes a Midas editor command directly on the editor document.
24415      * For visual commands, you should use {@link #relayCmd} instead.
24416      * <b>This should only be called after the editor is initialized.</b>
24417      * @param {String} cmd The Midas command
24418      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24419      */
24420     execCmd : function(cmd, value){
24421         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24422         this.syncValue();
24423     },
24424
24425    
24426     /**
24427      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24428      * to insert tRoo.
24429      * @param {String} text
24430      */
24431     insertAtCursor : function(text){
24432         if(!this.activated){
24433             return;
24434         }
24435         if(Roo.isIE){
24436             this.win.focus();
24437             var r = this.doc.selection.createRange();
24438             if(r){
24439                 r.collapse(true);
24440                 r.pasteHTML(text);
24441                 this.syncValue();
24442                 this.deferFocus();
24443             }
24444         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24445             this.win.focus();
24446             this.execCmd('InsertHTML', text);
24447             this.deferFocus();
24448         }
24449     },
24450  // private
24451     mozKeyPress : function(e){
24452         if(e.ctrlKey){
24453             var c = e.getCharCode(), cmd;
24454           
24455             if(c > 0){
24456                 c = String.fromCharCode(c).toLowerCase();
24457                 switch(c){
24458                     case 'b':
24459                         cmd = 'bold';
24460                     break;
24461                     case 'i':
24462                         cmd = 'italic';
24463                     break;
24464                     case 'u':
24465                         cmd = 'underline';
24466                     case 'v':
24467                         this.cleanUpPaste.defer(100, this);
24468                         return;
24469                     break;
24470                 }
24471                 if(cmd){
24472                     this.win.focus();
24473                     this.execCmd(cmd);
24474                     this.deferFocus();
24475                     e.preventDefault();
24476                 }
24477                 
24478             }
24479         }
24480     },
24481
24482     // private
24483     fixKeys : function(){ // load time branching for fastest keydown performance
24484         if(Roo.isIE){
24485             return function(e){
24486                 var k = e.getKey(), r;
24487                 if(k == e.TAB){
24488                     e.stopEvent();
24489                     r = this.doc.selection.createRange();
24490                     if(r){
24491                         r.collapse(true);
24492                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24493                         this.deferFocus();
24494                     }
24495                     return;
24496                 }
24497                 
24498                 if(k == e.ENTER){
24499                     r = this.doc.selection.createRange();
24500                     if(r){
24501                         var target = r.parentElement();
24502                         if(!target || target.tagName.toLowerCase() != 'li'){
24503                             e.stopEvent();
24504                             r.pasteHTML('<br />');
24505                             r.collapse(false);
24506                             r.select();
24507                         }
24508                     }
24509                 }
24510                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24511                     this.cleanUpPaste.defer(100, this);
24512                     return;
24513                 }
24514                 
24515                 
24516             };
24517         }else if(Roo.isOpera){
24518             return function(e){
24519                 var k = e.getKey();
24520                 if(k == e.TAB){
24521                     e.stopEvent();
24522                     this.win.focus();
24523                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24524                     this.deferFocus();
24525                 }
24526                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24527                     this.cleanUpPaste.defer(100, this);
24528                     return;
24529                 }
24530                 
24531             };
24532         }else if(Roo.isSafari){
24533             return function(e){
24534                 var k = e.getKey();
24535                 
24536                 if(k == e.TAB){
24537                     e.stopEvent();
24538                     this.execCmd('InsertText','\t');
24539                     this.deferFocus();
24540                     return;
24541                 }
24542                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24543                     this.cleanUpPaste.defer(100, this);
24544                     return;
24545                 }
24546                 
24547              };
24548         }
24549     }(),
24550     
24551     getAllAncestors: function()
24552     {
24553         var p = this.getSelectedNode();
24554         var a = [];
24555         if (!p) {
24556             a.push(p); // push blank onto stack..
24557             p = this.getParentElement();
24558         }
24559         
24560         
24561         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24562             a.push(p);
24563             p = p.parentNode;
24564         }
24565         a.push(this.doc.body);
24566         return a;
24567     },
24568     lastSel : false,
24569     lastSelNode : false,
24570     
24571     
24572     getSelection : function() 
24573     {
24574         this.assignDocWin();
24575         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24576     },
24577     
24578     getSelectedNode: function() 
24579     {
24580         // this may only work on Gecko!!!
24581         
24582         // should we cache this!!!!
24583         
24584         
24585         
24586          
24587         var range = this.createRange(this.getSelection());
24588         
24589         if (Roo.isIE) {
24590             var parent = range.parentElement();
24591             while (true) {
24592                 var testRange = range.duplicate();
24593                 testRange.moveToElementText(parent);
24594                 if (testRange.inRange(range)) {
24595                     break;
24596                 }
24597                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24598                     break;
24599                 }
24600                 parent = parent.parentElement;
24601             }
24602             return parent;
24603         }
24604         
24605         
24606         var ar = range.endContainer.childNodes;
24607         if (!ar.length) {
24608             ar = range.commonAncestorContainer.childNodes;
24609             //alert(ar.length);
24610         }
24611         var nodes = [];
24612         var other_nodes = [];
24613         var has_other_nodes = false;
24614         for (var i=0;i<ar.length;i++) {
24615             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24616                 continue;
24617             }
24618             // fullly contained node.
24619             
24620             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24621                 nodes.push(ar[i]);
24622                 continue;
24623             }
24624             
24625             // probably selected..
24626             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24627                 other_nodes.push(ar[i]);
24628                 continue;
24629             }
24630             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24631                 continue;
24632             }
24633             
24634             
24635             has_other_nodes = true;
24636         }
24637         if (!nodes.length && other_nodes.length) {
24638             nodes= other_nodes;
24639         }
24640         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24641             return false;
24642         }
24643         
24644         return nodes[0];
24645     },
24646     createRange: function(sel)
24647     {
24648         // this has strange effects when using with 
24649         // top toolbar - not sure if it's a great idea.
24650         //this.editor.contentWindow.focus();
24651         if (typeof sel != "undefined") {
24652             try {
24653                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24654             } catch(e) {
24655                 return this.doc.createRange();
24656             }
24657         } else {
24658             return this.doc.createRange();
24659         }
24660     },
24661     getParentElement: function()
24662     {
24663         
24664         this.assignDocWin();
24665         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24666         
24667         var range = this.createRange(sel);
24668          
24669         try {
24670             var p = range.commonAncestorContainer;
24671             while (p.nodeType == 3) { // text node
24672                 p = p.parentNode;
24673             }
24674             return p;
24675         } catch (e) {
24676             return null;
24677         }
24678     
24679     },
24680     
24681     
24682     
24683     // BC Hacks - cause I cant work out what i was trying to do..
24684     rangeIntersectsNode : function(range, node)
24685     {
24686         var nodeRange = node.ownerDocument.createRange();
24687         try {
24688             nodeRange.selectNode(node);
24689         }
24690         catch (e) {
24691             nodeRange.selectNodeContents(node);
24692         }
24693
24694         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24695                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24696     },
24697     rangeCompareNode : function(range, node) {
24698         var nodeRange = node.ownerDocument.createRange();
24699         try {
24700             nodeRange.selectNode(node);
24701         } catch (e) {
24702             nodeRange.selectNodeContents(node);
24703         }
24704         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24705         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24706
24707         if (nodeIsBefore && !nodeIsAfter)
24708             return 0;
24709         if (!nodeIsBefore && nodeIsAfter)
24710             return 1;
24711         if (nodeIsBefore && nodeIsAfter)
24712             return 2;
24713
24714         return 3;
24715     },
24716
24717     // private? - in a new class?
24718     cleanUpPaste :  function()
24719     {
24720         // cleans up the whole document..
24721       //  console.log('cleanuppaste');
24722         this.cleanUpChildren(this.doc.body)
24723         
24724         
24725     },
24726     cleanUpChildren : function (n)
24727     {
24728         if (!n.childNodes.length) {
24729             return;
24730         }
24731         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24732            this.cleanUpChild(n.childNodes[i]);
24733         }
24734     },
24735     
24736     
24737         
24738     
24739     cleanUpChild : function (node)
24740     {
24741         //console.log(node);
24742         if (node.nodeName == "#text") {
24743             // clean up silly Windows -- stuff?
24744             return; 
24745         }
24746         if (node.nodeName == "#comment") {
24747             node.parentNode.removeChild(node);
24748             // clean up silly Windows -- stuff?
24749             return; 
24750         }
24751         
24752         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24753             // remove node.
24754             node.parentNode.removeChild(node);
24755             return;
24756             
24757         }
24758         if (!node.attributes || !node.attributes.length) {
24759             this.cleanUpChildren(node);
24760             return;
24761         }
24762         
24763         function cleanAttr(n,v)
24764         {
24765             
24766             if (v.match(/^\./) || v.match(/^\//)) {
24767                 return;
24768             }
24769             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24770                 return;
24771             }
24772             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24773             node.removeAttribute(n);
24774             
24775         }
24776         
24777         function cleanStyle(n,v)
24778         {
24779             if (v.match(/expression/)) { //XSS?? should we even bother..
24780                 node.removeAttribute(n);
24781                 return;
24782             }
24783             
24784             
24785             var parts = v.split(/;/);
24786             Roo.each(parts, function(p) {
24787                 p = p.replace(/\s+/g,'');
24788                 if (!p.length) {
24789                     return;
24790                 }
24791                 var l = p.split(':').shift().replace(/\s+/g,'');
24792                 
24793                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24794                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24795                     node.removeAttribute(n);
24796                     return false;
24797                 }
24798             });
24799             
24800             
24801         }
24802         
24803         
24804         for (var i = node.attributes.length-1; i > -1 ; i--) {
24805             var a = node.attributes[i];
24806             //console.log(a);
24807             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24808                 node.removeAttribute(a.name);
24809                 return;
24810             }
24811             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24812                 cleanAttr(a.name,a.value); // fixme..
24813                 return;
24814             }
24815             if (a.name == 'style') {
24816                 cleanStyle(a.name,a.value);
24817             }
24818             /// clean up MS crap..
24819             if (a.name == 'class') {
24820                 if (a.value.match(/^Mso/)) {
24821                     node.className = '';
24822                 }
24823             }
24824             
24825             // style cleanup!?
24826             // class cleanup?
24827             
24828         }
24829         
24830         
24831         this.cleanUpChildren(node);
24832         
24833         
24834     }
24835     
24836     
24837     // hide stuff that is not compatible
24838     /**
24839      * @event blur
24840      * @hide
24841      */
24842     /**
24843      * @event change
24844      * @hide
24845      */
24846     /**
24847      * @event focus
24848      * @hide
24849      */
24850     /**
24851      * @event specialkey
24852      * @hide
24853      */
24854     /**
24855      * @cfg {String} fieldClass @hide
24856      */
24857     /**
24858      * @cfg {String} focusClass @hide
24859      */
24860     /**
24861      * @cfg {String} autoCreate @hide
24862      */
24863     /**
24864      * @cfg {String} inputType @hide
24865      */
24866     /**
24867      * @cfg {String} invalidClass @hide
24868      */
24869     /**
24870      * @cfg {String} invalidText @hide
24871      */
24872     /**
24873      * @cfg {String} msgFx @hide
24874      */
24875     /**
24876      * @cfg {String} validateOnBlur @hide
24877      */
24878 });
24879
24880 Roo.form.HtmlEditor.white = [
24881         'area', 'br', 'img', 'input', 'hr', 'wbr',
24882         
24883        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24884        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24885        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24886        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24887        'table',   'ul',         'xmp', 
24888        
24889        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24890       'thead',   'tr', 
24891      
24892       'dir', 'menu', 'ol', 'ul', 'dl',
24893        
24894       'embed',  'object'
24895 ];
24896
24897
24898 Roo.form.HtmlEditor.black = [
24899     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24900         'applet', // 
24901         'base',   'basefont', 'bgsound', 'blink',  'body', 
24902         'frame',  'frameset', 'head',    'html',   'ilayer', 
24903         'iframe', 'layer',  'link',     'meta',    'object',   
24904         'script', 'style' ,'title',  'xml' // clean later..
24905 ];
24906 Roo.form.HtmlEditor.clean = [
24907     'script', 'style', 'title', 'xml'
24908 ];
24909
24910 // attributes..
24911
24912 Roo.form.HtmlEditor.ablack = [
24913     'on'
24914 ];
24915     
24916 Roo.form.HtmlEditor.aclean = [ 
24917     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24918 ];
24919
24920 // protocols..
24921 Roo.form.HtmlEditor.pwhite= [
24922         'http',  'https',  'mailto'
24923 ];
24924
24925 Roo.form.HtmlEditor.cwhite= [
24926         'text-align',
24927         'font-size'
24928 ];
24929
24930 // <script type="text/javascript">
24931 /*
24932  * Based on
24933  * Ext JS Library 1.1.1
24934  * Copyright(c) 2006-2007, Ext JS, LLC.
24935  *  
24936  
24937  */
24938
24939 /**
24940  * @class Roo.form.HtmlEditorToolbar1
24941  * Basic Toolbar
24942  * 
24943  * Usage:
24944  *
24945  new Roo.form.HtmlEditor({
24946     ....
24947     toolbars : [
24948         new Roo.form.HtmlEditorToolbar1({
24949             disable : { fonts: 1 , format: 1, ..., ... , ...],
24950             btns : [ .... ]
24951         })
24952     }
24953      
24954  * 
24955  * @cfg {Object} disable List of elements to disable..
24956  * @cfg {Array} btns List of additional buttons.
24957  * 
24958  * 
24959  * NEEDS Extra CSS? 
24960  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24961  */
24962  
24963 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24964 {
24965     
24966     Roo.apply(this, config);
24967     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24968     // dont call parent... till later.
24969 }
24970
24971 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24972     
24973     tb: false,
24974     
24975     rendered: false,
24976     
24977     editor : false,
24978     /**
24979      * @cfg {Object} disable  List of toolbar elements to disable
24980          
24981      */
24982     disable : false,
24983       /**
24984      * @cfg {Array} fontFamilies An array of available font families
24985      */
24986     fontFamilies : [
24987         'Arial',
24988         'Courier New',
24989         'Tahoma',
24990         'Times New Roman',
24991         'Verdana'
24992     ],
24993     
24994     specialChars : [
24995            "&#169;",
24996           "&#174;",     
24997           "&#8482;",    
24998           "&#163;" ,    
24999          // "&#8212;",    
25000           "&#8230;",    
25001           "&#247;" ,    
25002         //  "&#225;" ,     ?? a acute?
25003            "&#8364;"    , //Euro
25004        //   "&#8220;"    ,
25005         //  "&#8221;"    ,
25006         //  "&#8226;"    ,
25007           "&#176;"  //   , // degrees
25008
25009          // "&#233;"     , // e ecute
25010          // "&#250;"     , // u ecute?
25011     ],
25012     inputElements : [ 
25013             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25014             "input:submit", "input:button", "select", "textarea", "label" ],
25015     formats : [
25016         ["p"] ,  
25017         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25018         ["pre"],[ "code"], 
25019         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25020     ],
25021      /**
25022      * @cfg {String} defaultFont default font to use.
25023      */
25024     defaultFont: 'tahoma',
25025    
25026     fontSelect : false,
25027     
25028     
25029     formatCombo : false,
25030     
25031     init : function(editor)
25032     {
25033         this.editor = editor;
25034         
25035         
25036         var fid = editor.frameId;
25037         var etb = this;
25038         function btn(id, toggle, handler){
25039             var xid = fid + '-'+ id ;
25040             return {
25041                 id : xid,
25042                 cmd : id,
25043                 cls : 'x-btn-icon x-edit-'+id,
25044                 enableToggle:toggle !== false,
25045                 scope: editor, // was editor...
25046                 handler:handler||editor.relayBtnCmd,
25047                 clickEvent:'mousedown',
25048                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25049                 tabIndex:-1
25050             };
25051         }
25052         
25053         
25054         
25055         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25056         this.tb = tb;
25057          // stop form submits
25058         tb.el.on('click', function(e){
25059             e.preventDefault(); // what does this do?
25060         });
25061
25062         if(!this.disable.font && !Roo.isSafari){
25063             /* why no safari for fonts
25064             editor.fontSelect = tb.el.createChild({
25065                 tag:'select',
25066                 tabIndex: -1,
25067                 cls:'x-font-select',
25068                 html: editor.createFontOptions()
25069             });
25070             editor.fontSelect.on('change', function(){
25071                 var font = editor.fontSelect.dom.value;
25072                 editor.relayCmd('fontname', font);
25073                 editor.deferFocus();
25074             }, editor);
25075             tb.add(
25076                 editor.fontSelect.dom,
25077                 '-'
25078             );
25079             */
25080         };
25081         if(!this.disable.formats){
25082             this.formatCombo = new Roo.form.ComboBox({
25083                 store: new Roo.data.SimpleStore({
25084                     id : 'tag',
25085                     fields: ['tag'],
25086                     data : this.formats // from states.js
25087                 }),
25088                 blockFocus : true,
25089                 //autoCreate : {tag: "div",  size: "20"},
25090                 displayField:'tag',
25091                 typeAhead: false,
25092                 mode: 'local',
25093                 editable : false,
25094                 triggerAction: 'all',
25095                 emptyText:'Add tag',
25096                 selectOnFocus:true,
25097                 width:135,
25098                 listeners : {
25099                     'select': function(c, r, i) {
25100                         editor.insertTag(r.get('tag'));
25101                         editor.focus();
25102                     }
25103                 }
25104
25105             });
25106             tb.addField(this.formatCombo);
25107             
25108         }
25109         
25110         if(!this.disable.format){
25111             tb.add(
25112                 btn('bold'),
25113                 btn('italic'),
25114                 btn('underline')
25115             );
25116         };
25117         if(!this.disable.fontSize){
25118             tb.add(
25119                 '-',
25120                 
25121                 
25122                 btn('increasefontsize', false, editor.adjustFont),
25123                 btn('decreasefontsize', false, editor.adjustFont)
25124             );
25125         };
25126         
25127         
25128         if(this.disable.colors){
25129             tb.add(
25130                 '-', {
25131                     id:editor.frameId +'-forecolor',
25132                     cls:'x-btn-icon x-edit-forecolor',
25133                     clickEvent:'mousedown',
25134                     tooltip: this.buttonTips['forecolor'] || undefined,
25135                     tabIndex:-1,
25136                     menu : new Roo.menu.ColorMenu({
25137                         allowReselect: true,
25138                         focus: Roo.emptyFn,
25139                         value:'000000',
25140                         plain:true,
25141                         selectHandler: function(cp, color){
25142                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25143                             editor.deferFocus();
25144                         },
25145                         scope: editor,
25146                         clickEvent:'mousedown'
25147                     })
25148                 }, {
25149                     id:editor.frameId +'backcolor',
25150                     cls:'x-btn-icon x-edit-backcolor',
25151                     clickEvent:'mousedown',
25152                     tooltip: this.buttonTips['backcolor'] || undefined,
25153                     tabIndex:-1,
25154                     menu : new Roo.menu.ColorMenu({
25155                         focus: Roo.emptyFn,
25156                         value:'FFFFFF',
25157                         plain:true,
25158                         allowReselect: true,
25159                         selectHandler: function(cp, color){
25160                             if(Roo.isGecko){
25161                                 editor.execCmd('useCSS', false);
25162                                 editor.execCmd('hilitecolor', color);
25163                                 editor.execCmd('useCSS', true);
25164                                 editor.deferFocus();
25165                             }else{
25166                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25167                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25168                                 editor.deferFocus();
25169                             }
25170                         },
25171                         scope:editor,
25172                         clickEvent:'mousedown'
25173                     })
25174                 }
25175             );
25176         };
25177         // now add all the items...
25178         
25179
25180         if(!this.disable.alignments){
25181             tb.add(
25182                 '-',
25183                 btn('justifyleft'),
25184                 btn('justifycenter'),
25185                 btn('justifyright')
25186             );
25187         };
25188
25189         //if(!Roo.isSafari){
25190             if(!this.disable.links){
25191                 tb.add(
25192                     '-',
25193                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25194                 );
25195             };
25196
25197             if(!this.disable.lists){
25198                 tb.add(
25199                     '-',
25200                     btn('insertorderedlist'),
25201                     btn('insertunorderedlist')
25202                 );
25203             }
25204             if(!this.disable.sourceEdit){
25205                 tb.add(
25206                     '-',
25207                     btn('sourceedit', true, function(btn){
25208                         this.toggleSourceEdit(btn.pressed);
25209                     })
25210                 );
25211             }
25212         //}
25213         
25214         var smenu = { };
25215         // special menu.. - needs to be tidied up..
25216         if (!this.disable.special) {
25217             smenu = {
25218                 text: "&#169;",
25219                 cls: 'x-edit-none',
25220                 menu : {
25221                     items : []
25222                    }
25223             };
25224             for (var i =0; i < this.specialChars.length; i++) {
25225                 smenu.menu.items.push({
25226                     
25227                     html: this.specialChars[i],
25228                     handler: function(a,b) {
25229                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25230                         
25231                     },
25232                     tabIndex:-1
25233                 });
25234             }
25235             
25236             
25237             tb.add(smenu);
25238             
25239             
25240         }
25241         if (this.btns) {
25242             for(var i =0; i< this.btns.length;i++) {
25243                 var b = this.btns[i];
25244                 b.cls =  'x-edit-none';
25245                 b.scope = editor;
25246                 tb.add(b);
25247             }
25248         
25249         }
25250         
25251         
25252         
25253         // disable everything...
25254         
25255         this.tb.items.each(function(item){
25256            if(item.id != editor.frameId+ '-sourceedit'){
25257                 item.disable();
25258             }
25259         });
25260         this.rendered = true;
25261         
25262         // the all the btns;
25263         editor.on('editorevent', this.updateToolbar, this);
25264         // other toolbars need to implement this..
25265         //editor.on('editmodechange', this.updateToolbar, this);
25266     },
25267     
25268     
25269     
25270     /**
25271      * Protected method that will not generally be called directly. It triggers
25272      * a toolbar update by reading the markup state of the current selection in the editor.
25273      */
25274     updateToolbar: function(){
25275
25276         if(!this.editor.activated){
25277             this.editor.onFirstFocus();
25278             return;
25279         }
25280
25281         var btns = this.tb.items.map, 
25282             doc = this.editor.doc,
25283             frameId = this.editor.frameId;
25284
25285         if(!this.disable.font && !Roo.isSafari){
25286             /*
25287             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25288             if(name != this.fontSelect.dom.value){
25289                 this.fontSelect.dom.value = name;
25290             }
25291             */
25292         }
25293         if(!this.disable.format){
25294             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25295             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25296             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25297         }
25298         if(!this.disable.alignments){
25299             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25300             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25301             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25302         }
25303         if(!Roo.isSafari && !this.disable.lists){
25304             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25305             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25306         }
25307         
25308         var ans = this.editor.getAllAncestors();
25309         if (this.formatCombo) {
25310             
25311             
25312             var store = this.formatCombo.store;
25313             this.formatCombo.setValue("");
25314             for (var i =0; i < ans.length;i++) {
25315                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25316                     // select it..
25317                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25318                     break;
25319                 }
25320             }
25321         }
25322         
25323         
25324         
25325         // hides menus... - so this cant be on a menu...
25326         Roo.menu.MenuMgr.hideAll();
25327
25328         //this.editorsyncValue();
25329     },
25330    
25331     
25332     createFontOptions : function(){
25333         var buf = [], fs = this.fontFamilies, ff, lc;
25334         for(var i = 0, len = fs.length; i< len; i++){
25335             ff = fs[i];
25336             lc = ff.toLowerCase();
25337             buf.push(
25338                 '<option value="',lc,'" style="font-family:',ff,';"',
25339                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25340                     ff,
25341                 '</option>'
25342             );
25343         }
25344         return buf.join('');
25345     },
25346     
25347     toggleSourceEdit : function(sourceEditMode){
25348         if(sourceEditMode === undefined){
25349             sourceEditMode = !this.sourceEditMode;
25350         }
25351         this.sourceEditMode = sourceEditMode === true;
25352         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25353         // just toggle the button?
25354         if(btn.pressed !== this.editor.sourceEditMode){
25355             btn.toggle(this.editor.sourceEditMode);
25356             return;
25357         }
25358         
25359         if(this.sourceEditMode){
25360             this.tb.items.each(function(item){
25361                 if(item.cmd != 'sourceedit'){
25362                     item.disable();
25363                 }
25364             });
25365           
25366         }else{
25367             if(this.initialized){
25368                 this.tb.items.each(function(item){
25369                     item.enable();
25370                 });
25371             }
25372             
25373         }
25374         // tell the editor that it's been pressed..
25375         this.editor.toggleSourceEdit(sourceEditMode);
25376        
25377     },
25378      /**
25379      * Object collection of toolbar tooltips for the buttons in the editor. The key
25380      * is the command id associated with that button and the value is a valid QuickTips object.
25381      * For example:
25382 <pre><code>
25383 {
25384     bold : {
25385         title: 'Bold (Ctrl+B)',
25386         text: 'Make the selected text bold.',
25387         cls: 'x-html-editor-tip'
25388     },
25389     italic : {
25390         title: 'Italic (Ctrl+I)',
25391         text: 'Make the selected text italic.',
25392         cls: 'x-html-editor-tip'
25393     },
25394     ...
25395 </code></pre>
25396     * @type Object
25397      */
25398     buttonTips : {
25399         bold : {
25400             title: 'Bold (Ctrl+B)',
25401             text: 'Make the selected text bold.',
25402             cls: 'x-html-editor-tip'
25403         },
25404         italic : {
25405             title: 'Italic (Ctrl+I)',
25406             text: 'Make the selected text italic.',
25407             cls: 'x-html-editor-tip'
25408         },
25409         underline : {
25410             title: 'Underline (Ctrl+U)',
25411             text: 'Underline the selected text.',
25412             cls: 'x-html-editor-tip'
25413         },
25414         increasefontsize : {
25415             title: 'Grow Text',
25416             text: 'Increase the font size.',
25417             cls: 'x-html-editor-tip'
25418         },
25419         decreasefontsize : {
25420             title: 'Shrink Text',
25421             text: 'Decrease the font size.',
25422             cls: 'x-html-editor-tip'
25423         },
25424         backcolor : {
25425             title: 'Text Highlight Color',
25426             text: 'Change the background color of the selected text.',
25427             cls: 'x-html-editor-tip'
25428         },
25429         forecolor : {
25430             title: 'Font Color',
25431             text: 'Change the color of the selected text.',
25432             cls: 'x-html-editor-tip'
25433         },
25434         justifyleft : {
25435             title: 'Align Text Left',
25436             text: 'Align text to the left.',
25437             cls: 'x-html-editor-tip'
25438         },
25439         justifycenter : {
25440             title: 'Center Text',
25441             text: 'Center text in the editor.',
25442             cls: 'x-html-editor-tip'
25443         },
25444         justifyright : {
25445             title: 'Align Text Right',
25446             text: 'Align text to the right.',
25447             cls: 'x-html-editor-tip'
25448         },
25449         insertunorderedlist : {
25450             title: 'Bullet List',
25451             text: 'Start a bulleted list.',
25452             cls: 'x-html-editor-tip'
25453         },
25454         insertorderedlist : {
25455             title: 'Numbered List',
25456             text: 'Start a numbered list.',
25457             cls: 'x-html-editor-tip'
25458         },
25459         createlink : {
25460             title: 'Hyperlink',
25461             text: 'Make the selected text a hyperlink.',
25462             cls: 'x-html-editor-tip'
25463         },
25464         sourceedit : {
25465             title: 'Source Edit',
25466             text: 'Switch to source editing mode.',
25467             cls: 'x-html-editor-tip'
25468         }
25469     },
25470     // private
25471     onDestroy : function(){
25472         if(this.rendered){
25473             
25474             this.tb.items.each(function(item){
25475                 if(item.menu){
25476                     item.menu.removeAll();
25477                     if(item.menu.el){
25478                         item.menu.el.destroy();
25479                     }
25480                 }
25481                 item.destroy();
25482             });
25483              
25484         }
25485     },
25486     onFirstFocus: function() {
25487         this.tb.items.each(function(item){
25488            item.enable();
25489         });
25490     }
25491 });
25492
25493
25494
25495
25496 // <script type="text/javascript">
25497 /*
25498  * Based on
25499  * Ext JS Library 1.1.1
25500  * Copyright(c) 2006-2007, Ext JS, LLC.
25501  *  
25502  
25503  */
25504
25505  
25506 /**
25507  * @class Roo.form.HtmlEditor.ToolbarContext
25508  * Context Toolbar
25509  * 
25510  * Usage:
25511  *
25512  new Roo.form.HtmlEditor({
25513     ....
25514     toolbars : [
25515         new Roo.form.HtmlEditor.ToolbarStandard(),
25516         new Roo.form.HtmlEditor.ToolbarContext()
25517         })
25518     }
25519      
25520  * 
25521  * @config : {Object} disable List of elements to disable.. (not done yet.)
25522  * 
25523  * 
25524  */
25525
25526 Roo.form.HtmlEditor.ToolbarContext = function(config)
25527 {
25528     
25529     Roo.apply(this, config);
25530     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25531     // dont call parent... till later.
25532 }
25533 Roo.form.HtmlEditor.ToolbarContext.types = {
25534     'IMG' : {
25535         width : {
25536             title: "Width",
25537             width: 40
25538         },
25539         height:  {
25540             title: "Height",
25541             width: 40
25542         },
25543         align: {
25544             title: "Align",
25545             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25546             width : 80
25547             
25548         },
25549         border: {
25550             title: "Border",
25551             width: 40
25552         },
25553         alt: {
25554             title: "Alt",
25555             width: 120
25556         },
25557         src : {
25558             title: "Src",
25559             width: 220
25560         }
25561         
25562     },
25563     'A' : {
25564         name : {
25565             title: "Name",
25566             width: 50
25567         },
25568         href:  {
25569             title: "Href",
25570             width: 220
25571         } // border?
25572         
25573     },
25574     'TABLE' : {
25575         rows : {
25576             title: "Rows",
25577             width: 20
25578         },
25579         cols : {
25580             title: "Cols",
25581             width: 20
25582         },
25583         width : {
25584             title: "Width",
25585             width: 40
25586         },
25587         height : {
25588             title: "Height",
25589             width: 40
25590         },
25591         border : {
25592             title: "Border",
25593             width: 20
25594         }
25595     },
25596     'TD' : {
25597         width : {
25598             title: "Width",
25599             width: 40
25600         },
25601         height : {
25602             title: "Height",
25603             width: 40
25604         },   
25605         align: {
25606             title: "Align",
25607             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25608             width: 40
25609         },
25610         valign: {
25611             title: "Valign",
25612             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25613             width: 40
25614         },
25615         colspan: {
25616             title: "Colspan",
25617             width: 20
25618             
25619         }
25620     },
25621     'INPUT' : {
25622         name : {
25623             title: "name",
25624             width: 120
25625         },
25626         value : {
25627             title: "Value",
25628             width: 120
25629         },
25630         width : {
25631             title: "Width",
25632             width: 40
25633         }
25634     },
25635     'LABEL' : {
25636         'for' : {
25637             title: "For",
25638             width: 120
25639         }
25640     },
25641     'TEXTAREA' : {
25642           name : {
25643             title: "name",
25644             width: 120
25645         },
25646         rows : {
25647             title: "Rows",
25648             width: 20
25649         },
25650         cols : {
25651             title: "Cols",
25652             width: 20
25653         }
25654     },
25655     'SELECT' : {
25656         name : {
25657             title: "name",
25658             width: 120
25659         },
25660         selectoptions : {
25661             title: "Options",
25662             width: 200
25663         }
25664     },
25665     'BODY' : {
25666         title : {
25667             title: "title",
25668             width: 120,
25669             disabled : true
25670         }
25671     }
25672 };
25673
25674
25675
25676 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25677     
25678     tb: false,
25679     
25680     rendered: false,
25681     
25682     editor : false,
25683     /**
25684      * @cfg {Object} disable  List of toolbar elements to disable
25685          
25686      */
25687     disable : false,
25688     
25689     
25690     
25691     toolbars : false,
25692     
25693     init : function(editor)
25694     {
25695         this.editor = editor;
25696         
25697         
25698         var fid = editor.frameId;
25699         var etb = this;
25700         function btn(id, toggle, handler){
25701             var xid = fid + '-'+ id ;
25702             return {
25703                 id : xid,
25704                 cmd : id,
25705                 cls : 'x-btn-icon x-edit-'+id,
25706                 enableToggle:toggle !== false,
25707                 scope: editor, // was editor...
25708                 handler:handler||editor.relayBtnCmd,
25709                 clickEvent:'mousedown',
25710                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25711                 tabIndex:-1
25712             };
25713         }
25714         // create a new element.
25715         var wdiv = editor.wrap.createChild({
25716                 tag: 'div'
25717             }, editor.wrap.dom.firstChild.nextSibling, true);
25718         
25719         // can we do this more than once??
25720         
25721          // stop form submits
25722       
25723  
25724         // disable everything...
25725         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25726         this.toolbars = {};
25727            
25728         for (var i in  ty) {
25729           
25730             this.toolbars[i] = this.buildToolbar(ty[i],i);
25731         }
25732         this.tb = this.toolbars.BODY;
25733         this.tb.el.show();
25734         
25735          
25736         this.rendered = true;
25737         
25738         // the all the btns;
25739         editor.on('editorevent', this.updateToolbar, this);
25740         // other toolbars need to implement this..
25741         //editor.on('editmodechange', this.updateToolbar, this);
25742     },
25743     
25744     
25745     
25746     /**
25747      * Protected method that will not generally be called directly. It triggers
25748      * a toolbar update by reading the markup state of the current selection in the editor.
25749      */
25750     updateToolbar: function(){
25751
25752         if(!this.editor.activated){
25753             this.editor.onFirstFocus();
25754             return;
25755         }
25756
25757         
25758         var ans = this.editor.getAllAncestors();
25759         
25760         // pick
25761         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25762         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25763         sel = sel ? sel : this.editor.doc.body;
25764         sel = sel.tagName.length ? sel : this.editor.doc.body;
25765         var tn = sel.tagName.toUpperCase();
25766         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25767         tn = sel.tagName.toUpperCase();
25768         if (this.tb.name  == tn) {
25769             return; // no change
25770         }
25771         this.tb.el.hide();
25772         ///console.log("show: " + tn);
25773         this.tb =  this.toolbars[tn];
25774         this.tb.el.show();
25775         this.tb.fields.each(function(e) {
25776             e.setValue(sel.getAttribute(e.name));
25777         });
25778         this.tb.selectedNode = sel;
25779         
25780         
25781         Roo.menu.MenuMgr.hideAll();
25782
25783         //this.editorsyncValue();
25784     },
25785    
25786        
25787     // private
25788     onDestroy : function(){
25789         if(this.rendered){
25790             
25791             this.tb.items.each(function(item){
25792                 if(item.menu){
25793                     item.menu.removeAll();
25794                     if(item.menu.el){
25795                         item.menu.el.destroy();
25796                     }
25797                 }
25798                 item.destroy();
25799             });
25800              
25801         }
25802     },
25803     onFirstFocus: function() {
25804         // need to do this for all the toolbars..
25805         this.tb.items.each(function(item){
25806            item.enable();
25807         });
25808     },
25809     buildToolbar: function(tlist, nm)
25810     {
25811         var editor = this.editor;
25812          // create a new element.
25813         var wdiv = editor.wrap.createChild({
25814                 tag: 'div'
25815             }, editor.wrap.dom.firstChild.nextSibling, true);
25816         
25817        
25818         var tb = new Roo.Toolbar(wdiv);
25819         tb.add(nm+ ":&nbsp;");
25820         for (var i in tlist) {
25821             var item = tlist[i];
25822             tb.add(item.title + ":&nbsp;");
25823             if (item.opts) {
25824                 // fixme
25825                 
25826               
25827                 tb.addField( new Roo.form.ComboBox({
25828                     store: new Roo.data.SimpleStore({
25829                         id : 'val',
25830                         fields: ['val'],
25831                         data : item.opts // from states.js
25832                     }),
25833                     name : i,
25834                     displayField:'val',
25835                     typeAhead: false,
25836                     mode: 'local',
25837                     editable : false,
25838                     triggerAction: 'all',
25839                     emptyText:'Select',
25840                     selectOnFocus:true,
25841                     width: item.width ? item.width  : 130,
25842                     listeners : {
25843                         'select': function(c, r, i) {
25844                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25845                         }
25846                     }
25847
25848                 }));
25849                 continue;
25850                     
25851                 
25852                 
25853                 
25854                 
25855                 tb.addField( new Roo.form.TextField({
25856                     name: i,
25857                     width: 100,
25858                     //allowBlank:false,
25859                     value: ''
25860                 }));
25861                 continue;
25862             }
25863             tb.addField( new Roo.form.TextField({
25864                 name: i,
25865                 width: item.width,
25866                 //allowBlank:true,
25867                 value: '',
25868                 listeners: {
25869                     'change' : function(f, nv, ov) {
25870                         tb.selectedNode.setAttribute(f.name, nv);
25871                     }
25872                 }
25873             }));
25874              
25875         }
25876         tb.el.on('click', function(e){
25877             e.preventDefault(); // what does this do?
25878         });
25879         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25880         tb.el.hide();
25881         tb.name = nm;
25882         // dont need to disable them... as they will get hidden
25883         return tb;
25884          
25885         
25886     }
25887     
25888     
25889     
25890     
25891 });
25892
25893
25894
25895
25896
25897 /*
25898  * Based on:
25899  * Ext JS Library 1.1.1
25900  * Copyright(c) 2006-2007, Ext JS, LLC.
25901  *
25902  * Originally Released Under LGPL - original licence link has changed is not relivant.
25903  *
25904  * Fork - LGPL
25905  * <script type="text/javascript">
25906  */
25907  
25908 /**
25909  * @class Roo.form.BasicForm
25910  * @extends Roo.util.Observable
25911  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25912  * @constructor
25913  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25914  * @param {Object} config Configuration options
25915  */
25916 Roo.form.BasicForm = function(el, config){
25917     this.allItems = [];
25918     this.childForms = [];
25919     Roo.apply(this, config);
25920     /*
25921      * The Roo.form.Field items in this form.
25922      * @type MixedCollection
25923      */
25924      
25925      
25926     this.items = new Roo.util.MixedCollection(false, function(o){
25927         return o.id || (o.id = Roo.id());
25928     });
25929     this.addEvents({
25930         /**
25931          * @event beforeaction
25932          * Fires before any action is performed. Return false to cancel the action.
25933          * @param {Form} this
25934          * @param {Action} action The action to be performed
25935          */
25936         beforeaction: true,
25937         /**
25938          * @event actionfailed
25939          * Fires when an action fails.
25940          * @param {Form} this
25941          * @param {Action} action The action that failed
25942          */
25943         actionfailed : true,
25944         /**
25945          * @event actioncomplete
25946          * Fires when an action is completed.
25947          * @param {Form} this
25948          * @param {Action} action The action that completed
25949          */
25950         actioncomplete : true
25951     });
25952     if(el){
25953         this.initEl(el);
25954     }
25955     Roo.form.BasicForm.superclass.constructor.call(this);
25956 };
25957
25958 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25959     /**
25960      * @cfg {String} method
25961      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25962      */
25963     /**
25964      * @cfg {DataReader} reader
25965      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25966      * This is optional as there is built-in support for processing JSON.
25967      */
25968     /**
25969      * @cfg {DataReader} errorReader
25970      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25971      * This is completely optional as there is built-in support for processing JSON.
25972      */
25973     /**
25974      * @cfg {String} url
25975      * The URL to use for form actions if one isn't supplied in the action options.
25976      */
25977     /**
25978      * @cfg {Boolean} fileUpload
25979      * Set to true if this form is a file upload.
25980      */
25981     /**
25982      * @cfg {Object} baseParams
25983      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25984      */
25985     /**
25986      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25987      */
25988     timeout: 30,
25989
25990     // private
25991     activeAction : null,
25992
25993     /**
25994      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25995      * or setValues() data instead of when the form was first created.
25996      */
25997     trackResetOnLoad : false,
25998     
25999     
26000     /**
26001      * childForms - used for multi-tab forms
26002      * @type {Array}
26003      */
26004     childForms : false,
26005     
26006     /**
26007      * allItems - full list of fields.
26008      * @type {Array}
26009      */
26010     allItems : false,
26011     
26012     /**
26013      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26014      * element by passing it or its id or mask the form itself by passing in true.
26015      * @type Mixed
26016      */
26017     waitMsgTarget : undefined,
26018
26019     // private
26020     initEl : function(el){
26021         this.el = Roo.get(el);
26022         this.id = this.el.id || Roo.id();
26023         this.el.on('submit', this.onSubmit, this);
26024         this.el.addClass('x-form');
26025     },
26026
26027     // private
26028     onSubmit : function(e){
26029         e.stopEvent();
26030     },
26031
26032     /**
26033      * Returns true if client-side validation on the form is successful.
26034      * @return Boolean
26035      */
26036     isValid : function(){
26037         var valid = true;
26038         this.items.each(function(f){
26039            if(!f.validate()){
26040                valid = false;
26041            }
26042         });
26043         return valid;
26044     },
26045
26046     /**
26047      * Returns true if any fields in this form have changed since their original load.
26048      * @return Boolean
26049      */
26050     isDirty : function(){
26051         var dirty = false;
26052         this.items.each(function(f){
26053            if(f.isDirty()){
26054                dirty = true;
26055                return false;
26056            }
26057         });
26058         return dirty;
26059     },
26060
26061     /**
26062      * Performs a predefined action (submit or load) or custom actions you define on this form.
26063      * @param {String} actionName The name of the action type
26064      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26065      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26066      * accept other config options):
26067      * <pre>
26068 Property          Type             Description
26069 ----------------  ---------------  ----------------------------------------------------------------------------------
26070 url               String           The url for the action (defaults to the form's url)
26071 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26072 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26073 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26074                                    validate the form on the client (defaults to false)
26075      * </pre>
26076      * @return {BasicForm} this
26077      */
26078     doAction : function(action, options){
26079         if(typeof action == 'string'){
26080             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26081         }
26082         if(this.fireEvent('beforeaction', this, action) !== false){
26083             this.beforeAction(action);
26084             action.run.defer(100, action);
26085         }
26086         return this;
26087     },
26088
26089     /**
26090      * Shortcut to do a submit action.
26091      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26092      * @return {BasicForm} this
26093      */
26094     submit : function(options){
26095         this.doAction('submit', options);
26096         return this;
26097     },
26098
26099     /**
26100      * Shortcut to do a load action.
26101      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26102      * @return {BasicForm} this
26103      */
26104     load : function(options){
26105         this.doAction('load', options);
26106         return this;
26107     },
26108
26109     /**
26110      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26111      * @param {Record} record The record to edit
26112      * @return {BasicForm} this
26113      */
26114     updateRecord : function(record){
26115         record.beginEdit();
26116         var fs = record.fields;
26117         fs.each(function(f){
26118             var field = this.findField(f.name);
26119             if(field){
26120                 record.set(f.name, field.getValue());
26121             }
26122         }, this);
26123         record.endEdit();
26124         return this;
26125     },
26126
26127     /**
26128      * Loads an Roo.data.Record into this form.
26129      * @param {Record} record The record to load
26130      * @return {BasicForm} this
26131      */
26132     loadRecord : function(record){
26133         this.setValues(record.data);
26134         return this;
26135     },
26136
26137     // private
26138     beforeAction : function(action){
26139         var o = action.options;
26140         if(o.waitMsg){
26141             if(this.waitMsgTarget === true){
26142                 this.el.mask(o.waitMsg, 'x-mask-loading');
26143             }else if(this.waitMsgTarget){
26144                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26145                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26146             }else{
26147                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26148             }
26149         }
26150     },
26151
26152     // private
26153     afterAction : function(action, success){
26154         this.activeAction = null;
26155         var o = action.options;
26156         if(o.waitMsg){
26157             if(this.waitMsgTarget === true){
26158                 this.el.unmask();
26159             }else if(this.waitMsgTarget){
26160                 this.waitMsgTarget.unmask();
26161             }else{
26162                 Roo.MessageBox.updateProgress(1);
26163                 Roo.MessageBox.hide();
26164             }
26165         }
26166         if(success){
26167             if(o.reset){
26168                 this.reset();
26169             }
26170             Roo.callback(o.success, o.scope, [this, action]);
26171             this.fireEvent('actioncomplete', this, action);
26172         }else{
26173             Roo.callback(o.failure, o.scope, [this, action]);
26174             this.fireEvent('actionfailed', this, action);
26175         }
26176     },
26177
26178     /**
26179      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26180      * @param {String} id The value to search for
26181      * @return Field
26182      */
26183     findField : function(id){
26184         var field = this.items.get(id);
26185         if(!field){
26186             this.items.each(function(f){
26187                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26188                     field = f;
26189                     return false;
26190                 }
26191             });
26192         }
26193         return field || null;
26194     },
26195
26196     /**
26197      * Add a secondary form to this one, 
26198      * Used to provide tabbed forms. One form is primary, with hidden values 
26199      * which mirror the elements from the other forms.
26200      * 
26201      * @param {Roo.form.Form} form to add.
26202      * 
26203      */
26204     addForm : function(form)
26205     {
26206        
26207         if (this.childForms.indexOf(form) > -1) {
26208             // already added..
26209             return;
26210         }
26211         this.childForms.push(form);
26212         var n = '';
26213         Roo.each(form.allItems, function (fe) {
26214             
26215             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26216             if (this.findField(n)) { // already added..
26217                 return;
26218             }
26219             var add = new Roo.form.Hidden({
26220                 name : n
26221             });
26222             add.render(this.el);
26223             
26224             this.add( add );
26225         }, this);
26226         
26227     },
26228     /**
26229      * Mark fields in this form invalid in bulk.
26230      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26231      * @return {BasicForm} this
26232      */
26233     markInvalid : function(errors){
26234         if(errors instanceof Array){
26235             for(var i = 0, len = errors.length; i < len; i++){
26236                 var fieldError = errors[i];
26237                 var f = this.findField(fieldError.id);
26238                 if(f){
26239                     f.markInvalid(fieldError.msg);
26240                 }
26241             }
26242         }else{
26243             var field, id;
26244             for(id in errors){
26245                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26246                     field.markInvalid(errors[id]);
26247                 }
26248             }
26249         }
26250         Roo.each(this.childForms || [], function (f) {
26251             f.markInvalid(errors);
26252         });
26253         
26254         return this;
26255     },
26256
26257     /**
26258      * Set values for fields in this form in bulk.
26259      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26260      * @return {BasicForm} this
26261      */
26262     setValues : function(values){
26263         if(values instanceof Array){ // array of objects
26264             for(var i = 0, len = values.length; i < len; i++){
26265                 var v = values[i];
26266                 var f = this.findField(v.id);
26267                 if(f){
26268                     f.setValue(v.value);
26269                     if(this.trackResetOnLoad){
26270                         f.originalValue = f.getValue();
26271                     }
26272                 }
26273             }
26274         }else{ // object hash
26275             var field, id;
26276             for(id in values){
26277                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26278                     
26279                     if (field.setFromData && 
26280                         field.valueField && 
26281                         field.displayField &&
26282                         // combos' with local stores can 
26283                         // be queried via setValue()
26284                         // to set their value..
26285                         (field.store && !field.store.isLocal)
26286                         ) {
26287                         // it's a combo
26288                         var sd = { };
26289                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26290                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26291                         field.setFromData(sd);
26292                         
26293                     } else {
26294                         field.setValue(values[id]);
26295                     }
26296                     
26297                     
26298                     if(this.trackResetOnLoad){
26299                         field.originalValue = field.getValue();
26300                     }
26301                 }
26302             }
26303         }
26304          
26305         Roo.each(this.childForms || [], function (f) {
26306             f.setValues(values);
26307         });
26308                 
26309         return this;
26310     },
26311
26312     /**
26313      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26314      * they are returned as an array.
26315      * @param {Boolean} asString
26316      * @return {Object}
26317      */
26318     getValues : function(asString){
26319         if (this.childForms) {
26320             // copy values from the child forms
26321             Roo.each(this.childForms, function (f) {
26322                 this.setValues(f.getValues());
26323             }, this);
26324         }
26325         
26326         
26327         
26328         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26329         if(asString === true){
26330             return fs;
26331         }
26332         return Roo.urlDecode(fs);
26333     },
26334     
26335     /**
26336      * Returns the fields in this form as an object with key/value pairs. 
26337      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26338      * @return {Object}
26339      */
26340     getFieldValues : function()
26341     {
26342         if (this.childForms) {
26343             // copy values from the child forms
26344             Roo.each(this.childForms, function (f) {
26345                 this.setValues(f.getValues());
26346             }, this);
26347         }
26348         
26349         var ret = {};
26350         this.items.each(function(f){
26351             if (!f.getName()) {
26352                 return;
26353             }
26354             var v = f.getValue();
26355             if ((typeof(v) == 'object') && f.getRawValue) {
26356                 v = f.getRawValue() ; // dates..
26357             }
26358             ret[f.getName()] = v;
26359         });
26360         
26361         return ret;
26362     },
26363
26364     /**
26365      * Clears all invalid messages in this form.
26366      * @return {BasicForm} this
26367      */
26368     clearInvalid : function(){
26369         this.items.each(function(f){
26370            f.clearInvalid();
26371         });
26372         
26373         Roo.each(this.childForms || [], function (f) {
26374             f.clearInvalid();
26375         });
26376         
26377         
26378         return this;
26379     },
26380
26381     /**
26382      * Resets this form.
26383      * @return {BasicForm} this
26384      */
26385     reset : function(){
26386         this.items.each(function(f){
26387             f.reset();
26388         });
26389         
26390         Roo.each(this.childForms || [], function (f) {
26391             f.reset();
26392         });
26393        
26394         
26395         return this;
26396     },
26397
26398     /**
26399      * Add Roo.form components to this form.
26400      * @param {Field} field1
26401      * @param {Field} field2 (optional)
26402      * @param {Field} etc (optional)
26403      * @return {BasicForm} this
26404      */
26405     add : function(){
26406         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26407         return this;
26408     },
26409
26410
26411     /**
26412      * Removes a field from the items collection (does NOT remove its markup).
26413      * @param {Field} field
26414      * @return {BasicForm} this
26415      */
26416     remove : function(field){
26417         this.items.remove(field);
26418         return this;
26419     },
26420
26421     /**
26422      * Looks at the fields in this form, checks them for an id attribute,
26423      * and calls applyTo on the existing dom element with that id.
26424      * @return {BasicForm} this
26425      */
26426     render : function(){
26427         this.items.each(function(f){
26428             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26429                 f.applyTo(f.id);
26430             }
26431         });
26432         return this;
26433     },
26434
26435     /**
26436      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26437      * @param {Object} values
26438      * @return {BasicForm} this
26439      */
26440     applyToFields : function(o){
26441         this.items.each(function(f){
26442            Roo.apply(f, o);
26443         });
26444         return this;
26445     },
26446
26447     /**
26448      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26449      * @param {Object} values
26450      * @return {BasicForm} this
26451      */
26452     applyIfToFields : function(o){
26453         this.items.each(function(f){
26454            Roo.applyIf(f, o);
26455         });
26456         return this;
26457     }
26458 });
26459
26460 // back compat
26461 Roo.BasicForm = Roo.form.BasicForm;/*
26462  * Based on:
26463  * Ext JS Library 1.1.1
26464  * Copyright(c) 2006-2007, Ext JS, LLC.
26465  *
26466  * Originally Released Under LGPL - original licence link has changed is not relivant.
26467  *
26468  * Fork - LGPL
26469  * <script type="text/javascript">
26470  */
26471
26472 /**
26473  * @class Roo.form.Form
26474  * @extends Roo.form.BasicForm
26475  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26476  * @constructor
26477  * @param {Object} config Configuration options
26478  */
26479 Roo.form.Form = function(config){
26480     var xitems =  [];
26481     if (config.items) {
26482         xitems = config.items;
26483         delete config.items;
26484     }
26485    
26486     
26487     Roo.form.Form.superclass.constructor.call(this, null, config);
26488     this.url = this.url || this.action;
26489     if(!this.root){
26490         this.root = new Roo.form.Layout(Roo.applyIf({
26491             id: Roo.id()
26492         }, config));
26493     }
26494     this.active = this.root;
26495     /**
26496      * Array of all the buttons that have been added to this form via {@link addButton}
26497      * @type Array
26498      */
26499     this.buttons = [];
26500     this.allItems = [];
26501     this.addEvents({
26502         /**
26503          * @event clientvalidation
26504          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26505          * @param {Form} this
26506          * @param {Boolean} valid true if the form has passed client-side validation
26507          */
26508         clientvalidation: true,
26509         /**
26510          * @event rendered
26511          * Fires when the form is rendered
26512          * @param {Roo.form.Form} form
26513          */
26514         rendered : true
26515     });
26516     
26517     if (this.progressUrl) {
26518             // push a hidden field onto the list of fields..
26519             this.addxtype( {
26520                     xns: Roo.form, 
26521                     xtype : 'Hidden', 
26522                     name : 'UPLOAD_IDENTIFIER' 
26523             });
26524         }
26525         
26526     
26527     Roo.each(xitems, this.addxtype, this);
26528     
26529     
26530     
26531 };
26532
26533 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26534     /**
26535      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26536      */
26537     /**
26538      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26539      */
26540     /**
26541      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26542      */
26543     buttonAlign:'center',
26544
26545     /**
26546      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26547      */
26548     minButtonWidth:75,
26549
26550     /**
26551      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26552      * This property cascades to child containers if not set.
26553      */
26554     labelAlign:'left',
26555
26556     /**
26557      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26558      * fires a looping event with that state. This is required to bind buttons to the valid
26559      * state using the config value formBind:true on the button.
26560      */
26561     monitorValid : false,
26562
26563     /**
26564      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26565      */
26566     monitorPoll : 200,
26567     
26568     /**
26569      * @cfg {String} progressUrl - Url to return progress data 
26570      */
26571     
26572     progressUrl : false,
26573   
26574     /**
26575      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26576      * fields are added and the column is closed. If no fields are passed the column remains open
26577      * until end() is called.
26578      * @param {Object} config The config to pass to the column
26579      * @param {Field} field1 (optional)
26580      * @param {Field} field2 (optional)
26581      * @param {Field} etc (optional)
26582      * @return Column The column container object
26583      */
26584     column : function(c){
26585         var col = new Roo.form.Column(c);
26586         this.start(col);
26587         if(arguments.length > 1){ // duplicate code required because of Opera
26588             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26589             this.end();
26590         }
26591         return col;
26592     },
26593
26594     /**
26595      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26596      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26597      * until end() is called.
26598      * @param {Object} config The config to pass to the fieldset
26599      * @param {Field} field1 (optional)
26600      * @param {Field} field2 (optional)
26601      * @param {Field} etc (optional)
26602      * @return FieldSet The fieldset container object
26603      */
26604     fieldset : function(c){
26605         var fs = new Roo.form.FieldSet(c);
26606         this.start(fs);
26607         if(arguments.length > 1){ // duplicate code required because of Opera
26608             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26609             this.end();
26610         }
26611         return fs;
26612     },
26613
26614     /**
26615      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26616      * fields are added and the container is closed. If no fields are passed the container remains open
26617      * until end() is called.
26618      * @param {Object} config The config to pass to the Layout
26619      * @param {Field} field1 (optional)
26620      * @param {Field} field2 (optional)
26621      * @param {Field} etc (optional)
26622      * @return Layout The container object
26623      */
26624     container : function(c){
26625         var l = new Roo.form.Layout(c);
26626         this.start(l);
26627         if(arguments.length > 1){ // duplicate code required because of Opera
26628             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26629             this.end();
26630         }
26631         return l;
26632     },
26633
26634     /**
26635      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26636      * @param {Object} container A Roo.form.Layout or subclass of Layout
26637      * @return {Form} this
26638      */
26639     start : function(c){
26640         // cascade label info
26641         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26642         this.active.stack.push(c);
26643         c.ownerCt = this.active;
26644         this.active = c;
26645         return this;
26646     },
26647
26648     /**
26649      * Closes the current open container
26650      * @return {Form} this
26651      */
26652     end : function(){
26653         if(this.active == this.root){
26654             return this;
26655         }
26656         this.active = this.active.ownerCt;
26657         return this;
26658     },
26659
26660     /**
26661      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26662      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26663      * as the label of the field.
26664      * @param {Field} field1
26665      * @param {Field} field2 (optional)
26666      * @param {Field} etc. (optional)
26667      * @return {Form} this
26668      */
26669     add : function(){
26670         this.active.stack.push.apply(this.active.stack, arguments);
26671         this.allItems.push.apply(this.allItems,arguments);
26672         var r = [];
26673         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26674             if(a[i].isFormField){
26675                 r.push(a[i]);
26676             }
26677         }
26678         if(r.length > 0){
26679             Roo.form.Form.superclass.add.apply(this, r);
26680         }
26681         return this;
26682     },
26683     
26684
26685     
26686     
26687     
26688      /**
26689      * Find any element that has been added to a form, using it's ID or name
26690      * This can include framesets, columns etc. along with regular fields..
26691      * @param {String} id - id or name to find.
26692      
26693      * @return {Element} e - or false if nothing found.
26694      */
26695     findbyId : function(id)
26696     {
26697         var ret = false;
26698         if (!id) {
26699             return ret;
26700         }
26701         Ext.each(this.allItems, function(f){
26702             if (f.id == id || f.name == id ){
26703                 ret = f;
26704                 return false;
26705             }
26706         });
26707         return ret;
26708     },
26709
26710     
26711     
26712     /**
26713      * Render this form into the passed container. This should only be called once!
26714      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26715      * @return {Form} this
26716      */
26717     render : function(ct)
26718     {
26719         
26720         
26721         
26722         ct = Roo.get(ct);
26723         var o = this.autoCreate || {
26724             tag: 'form',
26725             method : this.method || 'POST',
26726             id : this.id || Roo.id()
26727         };
26728         this.initEl(ct.createChild(o));
26729
26730         this.root.render(this.el);
26731         
26732        
26733              
26734         this.items.each(function(f){
26735             f.render('x-form-el-'+f.id);
26736         });
26737
26738         if(this.buttons.length > 0){
26739             // tables are required to maintain order and for correct IE layout
26740             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26741                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26742                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26743             }}, null, true);
26744             var tr = tb.getElementsByTagName('tr')[0];
26745             for(var i = 0, len = this.buttons.length; i < len; i++) {
26746                 var b = this.buttons[i];
26747                 var td = document.createElement('td');
26748                 td.className = 'x-form-btn-td';
26749                 b.render(tr.appendChild(td));
26750             }
26751         }
26752         if(this.monitorValid){ // initialize after render
26753             this.startMonitoring();
26754         }
26755         this.fireEvent('rendered', this);
26756         return this;
26757     },
26758
26759     /**
26760      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26761      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26762      * object or a valid Roo.DomHelper element config
26763      * @param {Function} handler The function called when the button is clicked
26764      * @param {Object} scope (optional) The scope of the handler function
26765      * @return {Roo.Button}
26766      */
26767     addButton : function(config, handler, scope){
26768         var bc = {
26769             handler: handler,
26770             scope: scope,
26771             minWidth: this.minButtonWidth,
26772             hideParent:true
26773         };
26774         if(typeof config == "string"){
26775             bc.text = config;
26776         }else{
26777             Roo.apply(bc, config);
26778         }
26779         var btn = new Roo.Button(null, bc);
26780         this.buttons.push(btn);
26781         return btn;
26782     },
26783
26784      /**
26785      * Adds a series of form elements (using the xtype property as the factory method.
26786      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26787      * @param {Object} config 
26788      */
26789     
26790     addxtype : function()
26791     {
26792         var ar = Array.prototype.slice.call(arguments, 0);
26793         var ret = false;
26794         for(var i = 0; i < ar.length; i++) {
26795             if (!ar[i]) {
26796                 continue; // skip -- if this happends something invalid got sent, we 
26797                 // should ignore it, as basically that interface element will not show up
26798                 // and that should be pretty obvious!!
26799             }
26800             
26801             if (Roo.form[ar[i].xtype]) {
26802                 ar[i].form = this;
26803                 var fe = Roo.factory(ar[i], Roo.form);
26804                 if (!ret) {
26805                     ret = fe;
26806                 }
26807                 fe.form = this;
26808                 if (fe.store) {
26809                     fe.store.form = this;
26810                 }
26811                 if (fe.isLayout) {  
26812                          
26813                     this.start(fe);
26814                     this.allItems.push(fe);
26815                     if (fe.items && fe.addxtype) {
26816                         fe.addxtype.apply(fe, fe.items);
26817                         delete fe.items;
26818                     }
26819                      this.end();
26820                     continue;
26821                 }
26822                 
26823                 
26824                  
26825                 this.add(fe);
26826               //  console.log('adding ' + ar[i].xtype);
26827             }
26828             if (ar[i].xtype == 'Button') {  
26829                 //console.log('adding button');
26830                 //console.log(ar[i]);
26831                 this.addButton(ar[i]);
26832                 this.allItems.push(fe);
26833                 continue;
26834             }
26835             
26836             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26837                 alert('end is not supported on xtype any more, use items');
26838             //    this.end();
26839             //    //console.log('adding end');
26840             }
26841             
26842         }
26843         return ret;
26844     },
26845     
26846     /**
26847      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26848      * option "monitorValid"
26849      */
26850     startMonitoring : function(){
26851         if(!this.bound){
26852             this.bound = true;
26853             Roo.TaskMgr.start({
26854                 run : this.bindHandler,
26855                 interval : this.monitorPoll || 200,
26856                 scope: this
26857             });
26858         }
26859     },
26860
26861     /**
26862      * Stops monitoring of the valid state of this form
26863      */
26864     stopMonitoring : function(){
26865         this.bound = false;
26866     },
26867
26868     // private
26869     bindHandler : function(){
26870         if(!this.bound){
26871             return false; // stops binding
26872         }
26873         var valid = true;
26874         this.items.each(function(f){
26875             if(!f.isValid(true)){
26876                 valid = false;
26877                 return false;
26878             }
26879         });
26880         for(var i = 0, len = this.buttons.length; i < len; i++){
26881             var btn = this.buttons[i];
26882             if(btn.formBind === true && btn.disabled === valid){
26883                 btn.setDisabled(!valid);
26884             }
26885         }
26886         this.fireEvent('clientvalidation', this, valid);
26887     }
26888     
26889     
26890     
26891     
26892     
26893     
26894     
26895     
26896 });
26897
26898
26899 // back compat
26900 Roo.Form = Roo.form.Form;
26901 /*
26902  * Based on:
26903  * Ext JS Library 1.1.1
26904  * Copyright(c) 2006-2007, Ext JS, LLC.
26905  *
26906  * Originally Released Under LGPL - original licence link has changed is not relivant.
26907  *
26908  * Fork - LGPL
26909  * <script type="text/javascript">
26910  */
26911  
26912  /**
26913  * @class Roo.form.Action
26914  * Internal Class used to handle form actions
26915  * @constructor
26916  * @param {Roo.form.BasicForm} el The form element or its id
26917  * @param {Object} config Configuration options
26918  */
26919  
26920  
26921 // define the action interface
26922 Roo.form.Action = function(form, options){
26923     this.form = form;
26924     this.options = options || {};
26925 };
26926 /**
26927  * Client Validation Failed
26928  * @const 
26929  */
26930 Roo.form.Action.CLIENT_INVALID = 'client';
26931 /**
26932  * Server Validation Failed
26933  * @const 
26934  */
26935  Roo.form.Action.SERVER_INVALID = 'server';
26936  /**
26937  * Connect to Server Failed
26938  * @const 
26939  */
26940 Roo.form.Action.CONNECT_FAILURE = 'connect';
26941 /**
26942  * Reading Data from Server Failed
26943  * @const 
26944  */
26945 Roo.form.Action.LOAD_FAILURE = 'load';
26946
26947 Roo.form.Action.prototype = {
26948     type : 'default',
26949     failureType : undefined,
26950     response : undefined,
26951     result : undefined,
26952
26953     // interface method
26954     run : function(options){
26955
26956     },
26957
26958     // interface method
26959     success : function(response){
26960
26961     },
26962
26963     // interface method
26964     handleResponse : function(response){
26965
26966     },
26967
26968     // default connection failure
26969     failure : function(response){
26970         this.response = response;
26971         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26972         this.form.afterAction(this, false);
26973     },
26974
26975     processResponse : function(response){
26976         this.response = response;
26977         if(!response.responseText){
26978             return true;
26979         }
26980         this.result = this.handleResponse(response);
26981         return this.result;
26982     },
26983
26984     // utility functions used internally
26985     getUrl : function(appendParams){
26986         var url = this.options.url || this.form.url || this.form.el.dom.action;
26987         if(appendParams){
26988             var p = this.getParams();
26989             if(p){
26990                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26991             }
26992         }
26993         return url;
26994     },
26995
26996     getMethod : function(){
26997         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26998     },
26999
27000     getParams : function(){
27001         var bp = this.form.baseParams;
27002         var p = this.options.params;
27003         if(p){
27004             if(typeof p == "object"){
27005                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27006             }else if(typeof p == 'string' && bp){
27007                 p += '&' + Roo.urlEncode(bp);
27008             }
27009         }else if(bp){
27010             p = Roo.urlEncode(bp);
27011         }
27012         return p;
27013     },
27014
27015     createCallback : function(){
27016         return {
27017             success: this.success,
27018             failure: this.failure,
27019             scope: this,
27020             timeout: (this.form.timeout*1000),
27021             upload: this.form.fileUpload ? this.success : undefined
27022         };
27023     }
27024 };
27025
27026 Roo.form.Action.Submit = function(form, options){
27027     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27028 };
27029
27030 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27031     type : 'submit',
27032
27033     haveProgress : false,
27034     uploadComplete : false,
27035     
27036     // uploadProgress indicator.
27037     uploadProgress : function()
27038     {
27039         if (!this.form.progressUrl) {
27040             return;
27041         }
27042         
27043         if (!this.haveProgress) {
27044             Roo.MessageBox.progress("Uploading", "Uploading");
27045         }
27046         if (this.uploadComplete) {
27047            Roo.MessageBox.hide();
27048            return;
27049         }
27050         
27051         this.haveProgress = true;
27052    
27053         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27054         
27055         var c = new Roo.data.Connection();
27056         c.request({
27057             url : this.form.progressUrl,
27058             params: {
27059                 id : uid
27060             },
27061             method: 'GET',
27062             success : function(req){
27063                //console.log(data);
27064                 var rdata = false;
27065                 var edata;
27066                 try  {
27067                    rdata = Roo.decode(req.responseText)
27068                 } catch (e) {
27069                     Roo.log("Invalid data from server..");
27070                     Roo.log(edata);
27071                     return;
27072                 }
27073                 if (!rdata || !rdata.success) {
27074                     Roo.log(rdata);
27075                     return;
27076                 }
27077                 var data = rdata.data;
27078                 
27079                 if (this.uploadComplete) {
27080                    Roo.MessageBox.hide();
27081                    return;
27082                 }
27083                    
27084                 if (data){
27085                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27086                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27087                     );
27088                 }
27089                 this.uploadProgress.defer(2000,this);
27090             },
27091        
27092             failure: function(data) {
27093                 Roo.log('progress url failed ');
27094                 Roo.log(data);
27095             },
27096             scope : this
27097         });
27098            
27099     },
27100     
27101     
27102     run : function()
27103     {
27104         // run get Values on the form, so it syncs any secondary forms.
27105         this.form.getValues();
27106         
27107         var o = this.options;
27108         var method = this.getMethod();
27109         var isPost = method == 'POST';
27110         if(o.clientValidation === false || this.form.isValid()){
27111             
27112             if (this.form.progressUrl) {
27113                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27114                     (new Date() * 1) + '' + Math.random());
27115                     
27116             } 
27117             
27118             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27119                 form:this.form.el.dom,
27120                 url:this.getUrl(!isPost),
27121                 method: method,
27122                 params:isPost ? this.getParams() : null,
27123                 isUpload: this.form.fileUpload
27124             }));
27125             
27126             this.uploadProgress();
27127
27128         }else if (o.clientValidation !== false){ // client validation failed
27129             this.failureType = Roo.form.Action.CLIENT_INVALID;
27130             this.form.afterAction(this, false);
27131         }
27132     },
27133
27134     success : function(response)
27135     {
27136         this.uploadComplete= true;
27137         if (this.haveProgress) {
27138             Roo.MessageBox.hide();
27139         }
27140         
27141         var result = this.processResponse(response);
27142         if(result === true || result.success){
27143             this.form.afterAction(this, true);
27144             return;
27145         }
27146         if(result.errors){
27147             this.form.markInvalid(result.errors);
27148             this.failureType = Roo.form.Action.SERVER_INVALID;
27149         }
27150         this.form.afterAction(this, false);
27151     },
27152     failure : function(response)
27153     {
27154         this.uploadComplete= true;
27155         if (this.haveProgress) {
27156             Roo.MessageBox.hide();
27157         }
27158         
27159         this.response = response;
27160         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27161         this.form.afterAction(this, false);
27162     },
27163     
27164     handleResponse : function(response){
27165         if(this.form.errorReader){
27166             var rs = this.form.errorReader.read(response);
27167             var errors = [];
27168             if(rs.records){
27169                 for(var i = 0, len = rs.records.length; i < len; i++) {
27170                     var r = rs.records[i];
27171                     errors[i] = r.data;
27172                 }
27173             }
27174             if(errors.length < 1){
27175                 errors = null;
27176             }
27177             return {
27178                 success : rs.success,
27179                 errors : errors
27180             };
27181         }
27182         var ret = false;
27183         try {
27184             ret = Roo.decode(response.responseText);
27185         } catch (e) {
27186             ret = {
27187                 success: false,
27188                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27189                 errors : []
27190             };
27191         }
27192         return ret;
27193         
27194     }
27195 });
27196
27197
27198 Roo.form.Action.Load = function(form, options){
27199     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27200     this.reader = this.form.reader;
27201 };
27202
27203 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27204     type : 'load',
27205
27206     run : function(){
27207         Roo.Ajax.request(Roo.apply(
27208                 this.createCallback(), {
27209                     method:this.getMethod(),
27210                     url:this.getUrl(false),
27211                     params:this.getParams()
27212         }));
27213     },
27214
27215     success : function(response){
27216         var result = this.processResponse(response);
27217         if(result === true || !result.success || !result.data){
27218             this.failureType = Roo.form.Action.LOAD_FAILURE;
27219             this.form.afterAction(this, false);
27220             return;
27221         }
27222         this.form.clearInvalid();
27223         this.form.setValues(result.data);
27224         this.form.afterAction(this, true);
27225     },
27226
27227     handleResponse : function(response){
27228         if(this.form.reader){
27229             var rs = this.form.reader.read(response);
27230             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27231             return {
27232                 success : rs.success,
27233                 data : data
27234             };
27235         }
27236         return Roo.decode(response.responseText);
27237     }
27238 });
27239
27240 Roo.form.Action.ACTION_TYPES = {
27241     'load' : Roo.form.Action.Load,
27242     'submit' : Roo.form.Action.Submit
27243 };/*
27244  * Based on:
27245  * Ext JS Library 1.1.1
27246  * Copyright(c) 2006-2007, Ext JS, LLC.
27247  *
27248  * Originally Released Under LGPL - original licence link has changed is not relivant.
27249  *
27250  * Fork - LGPL
27251  * <script type="text/javascript">
27252  */
27253  
27254 /**
27255  * @class Roo.form.Layout
27256  * @extends Roo.Component
27257  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27258  * @constructor
27259  * @param {Object} config Configuration options
27260  */
27261 Roo.form.Layout = function(config){
27262     var xitems = [];
27263     if (config.items) {
27264         xitems = config.items;
27265         delete config.items;
27266     }
27267     Roo.form.Layout.superclass.constructor.call(this, config);
27268     this.stack = [];
27269     Roo.each(xitems, this.addxtype, this);
27270      
27271 };
27272
27273 Roo.extend(Roo.form.Layout, Roo.Component, {
27274     /**
27275      * @cfg {String/Object} autoCreate
27276      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27277      */
27278     /**
27279      * @cfg {String/Object/Function} style
27280      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27281      * a function which returns such a specification.
27282      */
27283     /**
27284      * @cfg {String} labelAlign
27285      * Valid values are "left," "top" and "right" (defaults to "left")
27286      */
27287     /**
27288      * @cfg {Number} labelWidth
27289      * Fixed width in pixels of all field labels (defaults to undefined)
27290      */
27291     /**
27292      * @cfg {Boolean} clear
27293      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27294      */
27295     clear : true,
27296     /**
27297      * @cfg {String} labelSeparator
27298      * The separator to use after field labels (defaults to ':')
27299      */
27300     labelSeparator : ':',
27301     /**
27302      * @cfg {Boolean} hideLabels
27303      * True to suppress the display of field labels in this layout (defaults to false)
27304      */
27305     hideLabels : false,
27306
27307     // private
27308     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27309     
27310     isLayout : true,
27311     
27312     // private
27313     onRender : function(ct, position){
27314         if(this.el){ // from markup
27315             this.el = Roo.get(this.el);
27316         }else {  // generate
27317             var cfg = this.getAutoCreate();
27318             this.el = ct.createChild(cfg, position);
27319         }
27320         if(this.style){
27321             this.el.applyStyles(this.style);
27322         }
27323         if(this.labelAlign){
27324             this.el.addClass('x-form-label-'+this.labelAlign);
27325         }
27326         if(this.hideLabels){
27327             this.labelStyle = "display:none";
27328             this.elementStyle = "padding-left:0;";
27329         }else{
27330             if(typeof this.labelWidth == 'number'){
27331                 this.labelStyle = "width:"+this.labelWidth+"px;";
27332                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27333             }
27334             if(this.labelAlign == 'top'){
27335                 this.labelStyle = "width:auto;";
27336                 this.elementStyle = "padding-left:0;";
27337             }
27338         }
27339         var stack = this.stack;
27340         var slen = stack.length;
27341         if(slen > 0){
27342             if(!this.fieldTpl){
27343                 var t = new Roo.Template(
27344                     '<div class="x-form-item {5}">',
27345                         '<label for="{0}" style="{2}">{1}{4}</label>',
27346                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27347                         '</div>',
27348                     '</div><div class="x-form-clear-left"></div>'
27349                 );
27350                 t.disableFormats = true;
27351                 t.compile();
27352                 Roo.form.Layout.prototype.fieldTpl = t;
27353             }
27354             for(var i = 0; i < slen; i++) {
27355                 if(stack[i].isFormField){
27356                     this.renderField(stack[i]);
27357                 }else{
27358                     this.renderComponent(stack[i]);
27359                 }
27360             }
27361         }
27362         if(this.clear){
27363             this.el.createChild({cls:'x-form-clear'});
27364         }
27365     },
27366
27367     // private
27368     renderField : function(f){
27369         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27370                f.id, //0
27371                f.fieldLabel, //1
27372                f.labelStyle||this.labelStyle||'', //2
27373                this.elementStyle||'', //3
27374                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27375                f.itemCls||this.itemCls||''  //5
27376        ], true).getPrevSibling());
27377     },
27378
27379     // private
27380     renderComponent : function(c){
27381         c.render(c.isLayout ? this.el : this.el.createChild());    
27382     },
27383     /**
27384      * Adds a object form elements (using the xtype property as the factory method.)
27385      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27386      * @param {Object} config 
27387      */
27388     addxtype : function(o)
27389     {
27390         // create the lement.
27391         o.form = this.form;
27392         var fe = Roo.factory(o, Roo.form);
27393         this.form.allItems.push(fe);
27394         this.stack.push(fe);
27395         
27396         if (fe.isFormField) {
27397             this.form.items.add(fe);
27398         }
27399          
27400         return fe;
27401     }
27402 });
27403
27404 /**
27405  * @class Roo.form.Column
27406  * @extends Roo.form.Layout
27407  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27408  * @constructor
27409  * @param {Object} config Configuration options
27410  */
27411 Roo.form.Column = function(config){
27412     Roo.form.Column.superclass.constructor.call(this, config);
27413 };
27414
27415 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27416     /**
27417      * @cfg {Number/String} width
27418      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27419      */
27420     /**
27421      * @cfg {String/Object} autoCreate
27422      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27423      */
27424
27425     // private
27426     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27427
27428     // private
27429     onRender : function(ct, position){
27430         Roo.form.Column.superclass.onRender.call(this, ct, position);
27431         if(this.width){
27432             this.el.setWidth(this.width);
27433         }
27434     }
27435 });
27436
27437
27438 /**
27439  * @class Roo.form.Row
27440  * @extends Roo.form.Layout
27441  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27442  * @constructor
27443  * @param {Object} config Configuration options
27444  */
27445
27446  
27447 Roo.form.Row = function(config){
27448     Roo.form.Row.superclass.constructor.call(this, config);
27449 };
27450  
27451 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27452       /**
27453      * @cfg {Number/String} width
27454      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27455      */
27456     /**
27457      * @cfg {Number/String} height
27458      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27459      */
27460     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27461     
27462     padWidth : 20,
27463     // private
27464     onRender : function(ct, position){
27465         //console.log('row render');
27466         if(!this.rowTpl){
27467             var t = new Roo.Template(
27468                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27469                     '<label for="{0}" style="{2}">{1}{4}</label>',
27470                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27471                     '</div>',
27472                 '</div>'
27473             );
27474             t.disableFormats = true;
27475             t.compile();
27476             Roo.form.Layout.prototype.rowTpl = t;
27477         }
27478         this.fieldTpl = this.rowTpl;
27479         
27480         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27481         var labelWidth = 100;
27482         
27483         if ((this.labelAlign != 'top')) {
27484             if (typeof this.labelWidth == 'number') {
27485                 labelWidth = this.labelWidth
27486             }
27487             this.padWidth =  20 + labelWidth;
27488             
27489         }
27490         
27491         Roo.form.Column.superclass.onRender.call(this, ct, position);
27492         if(this.width){
27493             this.el.setWidth(this.width);
27494         }
27495         if(this.height){
27496             this.el.setHeight(this.height);
27497         }
27498     },
27499     
27500     // private
27501     renderField : function(f){
27502         f.fieldEl = this.fieldTpl.append(this.el, [
27503                f.id, f.fieldLabel,
27504                f.labelStyle||this.labelStyle||'',
27505                this.elementStyle||'',
27506                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27507                f.itemCls||this.itemCls||'',
27508                f.width ? f.width + this.padWidth : 160 + this.padWidth
27509        ],true);
27510     }
27511 });
27512  
27513
27514 /**
27515  * @class Roo.form.FieldSet
27516  * @extends Roo.form.Layout
27517  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27518  * @constructor
27519  * @param {Object} config Configuration options
27520  */
27521 Roo.form.FieldSet = function(config){
27522     Roo.form.FieldSet.superclass.constructor.call(this, config);
27523 };
27524
27525 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27526     /**
27527      * @cfg {String} legend
27528      * The text to display as the legend for the FieldSet (defaults to '')
27529      */
27530     /**
27531      * @cfg {String/Object} autoCreate
27532      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27533      */
27534
27535     // private
27536     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27537
27538     // private
27539     onRender : function(ct, position){
27540         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27541         if(this.legend){
27542             this.setLegend(this.legend);
27543         }
27544     },
27545
27546     // private
27547     setLegend : function(text){
27548         if(this.rendered){
27549             this.el.child('legend').update(text);
27550         }
27551     }
27552 });/*
27553  * Based on:
27554  * Ext JS Library 1.1.1
27555  * Copyright(c) 2006-2007, Ext JS, LLC.
27556  *
27557  * Originally Released Under LGPL - original licence link has changed is not relivant.
27558  *
27559  * Fork - LGPL
27560  * <script type="text/javascript">
27561  */
27562 /**
27563  * @class Roo.form.VTypes
27564  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27565  * @singleton
27566  */
27567 Roo.form.VTypes = function(){
27568     // closure these in so they are only created once.
27569     var alpha = /^[a-zA-Z_]+$/;
27570     var alphanum = /^[a-zA-Z0-9_]+$/;
27571     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27572     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27573
27574     // All these messages and functions are configurable
27575     return {
27576         /**
27577          * The function used to validate email addresses
27578          * @param {String} value The email address
27579          */
27580         'email' : function(v){
27581             return email.test(v);
27582         },
27583         /**
27584          * The error text to display when the email validation function returns false
27585          * @type String
27586          */
27587         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27588         /**
27589          * The keystroke filter mask to be applied on email input
27590          * @type RegExp
27591          */
27592         'emailMask' : /[a-z0-9_\.\-@]/i,
27593
27594         /**
27595          * The function used to validate URLs
27596          * @param {String} value The URL
27597          */
27598         'url' : function(v){
27599             return url.test(v);
27600         },
27601         /**
27602          * The error text to display when the url validation function returns false
27603          * @type String
27604          */
27605         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27606         
27607         /**
27608          * The function used to validate alpha values
27609          * @param {String} value The value
27610          */
27611         'alpha' : function(v){
27612             return alpha.test(v);
27613         },
27614         /**
27615          * The error text to display when the alpha validation function returns false
27616          * @type String
27617          */
27618         'alphaText' : 'This field should only contain letters and _',
27619         /**
27620          * The keystroke filter mask to be applied on alpha input
27621          * @type RegExp
27622          */
27623         'alphaMask' : /[a-z_]/i,
27624
27625         /**
27626          * The function used to validate alphanumeric values
27627          * @param {String} value The value
27628          */
27629         'alphanum' : function(v){
27630             return alphanum.test(v);
27631         },
27632         /**
27633          * The error text to display when the alphanumeric validation function returns false
27634          * @type String
27635          */
27636         'alphanumText' : 'This field should only contain letters, numbers and _',
27637         /**
27638          * The keystroke filter mask to be applied on alphanumeric input
27639          * @type RegExp
27640          */
27641         'alphanumMask' : /[a-z0-9_]/i
27642     };
27643 }();//<script type="text/javascript">
27644
27645 /**
27646  * @class Roo.form.FCKeditor
27647  * @extends Roo.form.TextArea
27648  * Wrapper around the FCKEditor http://www.fckeditor.net
27649  * @constructor
27650  * Creates a new FCKeditor
27651  * @param {Object} config Configuration options
27652  */
27653 Roo.form.FCKeditor = function(config){
27654     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27655     this.addEvents({
27656          /**
27657          * @event editorinit
27658          * Fired when the editor is initialized - you can add extra handlers here..
27659          * @param {FCKeditor} this
27660          * @param {Object} the FCK object.
27661          */
27662         editorinit : true
27663     });
27664     
27665     
27666 };
27667 Roo.form.FCKeditor.editors = { };
27668 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27669 {
27670     //defaultAutoCreate : {
27671     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27672     //},
27673     // private
27674     /**
27675      * @cfg {Object} fck options - see fck manual for details.
27676      */
27677     fckconfig : false,
27678     
27679     /**
27680      * @cfg {Object} fck toolbar set (Basic or Default)
27681      */
27682     toolbarSet : 'Basic',
27683     /**
27684      * @cfg {Object} fck BasePath
27685      */ 
27686     basePath : '/fckeditor/',
27687     
27688     
27689     frame : false,
27690     
27691     value : '',
27692     
27693    
27694     onRender : function(ct, position)
27695     {
27696         if(!this.el){
27697             this.defaultAutoCreate = {
27698                 tag: "textarea",
27699                 style:"width:300px;height:60px;",
27700                 autocomplete: "off"
27701             };
27702         }
27703         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27704         /*
27705         if(this.grow){
27706             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27707             if(this.preventScrollbars){
27708                 this.el.setStyle("overflow", "hidden");
27709             }
27710             this.el.setHeight(this.growMin);
27711         }
27712         */
27713         //console.log('onrender' + this.getId() );
27714         Roo.form.FCKeditor.editors[this.getId()] = this;
27715          
27716
27717         this.replaceTextarea() ;
27718         
27719     },
27720     
27721     getEditor : function() {
27722         return this.fckEditor;
27723     },
27724     /**
27725      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27726      * @param {Mixed} value The value to set
27727      */
27728     
27729     
27730     setValue : function(value)
27731     {
27732         //console.log('setValue: ' + value);
27733         
27734         if(typeof(value) == 'undefined') { // not sure why this is happending...
27735             return;
27736         }
27737         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27738         
27739         //if(!this.el || !this.getEditor()) {
27740         //    this.value = value;
27741             //this.setValue.defer(100,this,[value]);    
27742         //    return;
27743         //} 
27744         
27745         if(!this.getEditor()) {
27746             return;
27747         }
27748         
27749         this.getEditor().SetData(value);
27750         
27751         //
27752
27753     },
27754
27755     /**
27756      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27757      * @return {Mixed} value The field value
27758      */
27759     getValue : function()
27760     {
27761         
27762         if (this.frame && this.frame.dom.style.display == 'none') {
27763             return Roo.form.FCKeditor.superclass.getValue.call(this);
27764         }
27765         
27766         if(!this.el || !this.getEditor()) {
27767            
27768            // this.getValue.defer(100,this); 
27769             return this.value;
27770         }
27771        
27772         
27773         var value=this.getEditor().GetData();
27774         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27775         return Roo.form.FCKeditor.superclass.getValue.call(this);
27776         
27777
27778     },
27779
27780     /**
27781      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27782      * @return {Mixed} value The field value
27783      */
27784     getRawValue : function()
27785     {
27786         if (this.frame && this.frame.dom.style.display == 'none') {
27787             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27788         }
27789         
27790         if(!this.el || !this.getEditor()) {
27791             //this.getRawValue.defer(100,this); 
27792             return this.value;
27793             return;
27794         }
27795         
27796         
27797         
27798         var value=this.getEditor().GetData();
27799         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27800         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27801          
27802     },
27803     
27804     setSize : function(w,h) {
27805         
27806         
27807         
27808         //if (this.frame && this.frame.dom.style.display == 'none') {
27809         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27810         //    return;
27811         //}
27812         //if(!this.el || !this.getEditor()) {
27813         //    this.setSize.defer(100,this, [w,h]); 
27814         //    return;
27815         //}
27816         
27817         
27818         
27819         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27820         
27821         this.frame.dom.setAttribute('width', w);
27822         this.frame.dom.setAttribute('height', h);
27823         this.frame.setSize(w,h);
27824         
27825     },
27826     
27827     toggleSourceEdit : function(value) {
27828         
27829       
27830          
27831         this.el.dom.style.display = value ? '' : 'none';
27832         this.frame.dom.style.display = value ?  'none' : '';
27833         
27834     },
27835     
27836     
27837     focus: function(tag)
27838     {
27839         if (this.frame.dom.style.display == 'none') {
27840             return Roo.form.FCKeditor.superclass.focus.call(this);
27841         }
27842         if(!this.el || !this.getEditor()) {
27843             this.focus.defer(100,this, [tag]); 
27844             return;
27845         }
27846         
27847         
27848         
27849         
27850         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27851         this.getEditor().Focus();
27852         if (tgs.length) {
27853             if (!this.getEditor().Selection.GetSelection()) {
27854                 this.focus.defer(100,this, [tag]); 
27855                 return;
27856             }
27857             
27858             
27859             var r = this.getEditor().EditorDocument.createRange();
27860             r.setStart(tgs[0],0);
27861             r.setEnd(tgs[0],0);
27862             this.getEditor().Selection.GetSelection().removeAllRanges();
27863             this.getEditor().Selection.GetSelection().addRange(r);
27864             this.getEditor().Focus();
27865         }
27866         
27867     },
27868     
27869     
27870     
27871     replaceTextarea : function()
27872     {
27873         if ( document.getElementById( this.getId() + '___Frame' ) )
27874             return ;
27875         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27876         //{
27877             // We must check the elements firstly using the Id and then the name.
27878         var oTextarea = document.getElementById( this.getId() );
27879         
27880         var colElementsByName = document.getElementsByName( this.getId() ) ;
27881          
27882         oTextarea.style.display = 'none' ;
27883
27884         if ( oTextarea.tabIndex ) {            
27885             this.TabIndex = oTextarea.tabIndex ;
27886         }
27887         
27888         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27889         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27890         this.frame = Roo.get(this.getId() + '___Frame')
27891     },
27892     
27893     _getConfigHtml : function()
27894     {
27895         var sConfig = '' ;
27896
27897         for ( var o in this.fckconfig ) {
27898             sConfig += sConfig.length > 0  ? '&amp;' : '';
27899             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27900         }
27901
27902         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27903     },
27904     
27905     
27906     _getIFrameHtml : function()
27907     {
27908         var sFile = 'fckeditor.html' ;
27909         /* no idea what this is about..
27910         try
27911         {
27912             if ( (/fcksource=true/i).test( window.top.location.search ) )
27913                 sFile = 'fckeditor.original.html' ;
27914         }
27915         catch (e) { 
27916         */
27917
27918         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27919         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27920         
27921         
27922         var html = '<iframe id="' + this.getId() +
27923             '___Frame" src="' + sLink +
27924             '" width="' + this.width +
27925             '" height="' + this.height + '"' +
27926             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27927             ' frameborder="0" scrolling="no"></iframe>' ;
27928
27929         return html ;
27930     },
27931     
27932     _insertHtmlBefore : function( html, element )
27933     {
27934         if ( element.insertAdjacentHTML )       {
27935             // IE
27936             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27937         } else { // Gecko
27938             var oRange = document.createRange() ;
27939             oRange.setStartBefore( element ) ;
27940             var oFragment = oRange.createContextualFragment( html );
27941             element.parentNode.insertBefore( oFragment, element ) ;
27942         }
27943     }
27944     
27945     
27946   
27947     
27948     
27949     
27950     
27951
27952 });
27953
27954 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27955
27956 function FCKeditor_OnComplete(editorInstance){
27957     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27958     f.fckEditor = editorInstance;
27959     //console.log("loaded");
27960     f.fireEvent('editorinit', f, editorInstance);
27961
27962   
27963
27964  
27965
27966
27967
27968
27969
27970
27971
27972
27973
27974
27975
27976
27977
27978
27979
27980 //<script type="text/javascript">
27981 /**
27982  * @class Roo.form.GridField
27983  * @extends Roo.form.Field
27984  * Embed a grid (or editable grid into a form)
27985  * STATUS ALPHA
27986  * 
27987  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27988  * it needs 
27989  * xgrid.store = Roo.data.Store
27990  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27991  * xgrid.store.reader = Roo.data.JsonReader 
27992  * 
27993  * 
27994  * @constructor
27995  * Creates a new GridField
27996  * @param {Object} config Configuration options
27997  */
27998 Roo.form.GridField = function(config){
27999     Roo.form.GridField.superclass.constructor.call(this, config);
28000      
28001 };
28002
28003 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28004     /**
28005      * @cfg {Number} width  - used to restrict width of grid..
28006      */
28007     width : 100,
28008     /**
28009      * @cfg {Number} height - used to restrict height of grid..
28010      */
28011     height : 50,
28012      /**
28013      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28014          * 
28015          *}
28016      */
28017     xgrid : false, 
28018     /**
28019      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28020      * {tag: "input", type: "checkbox", autocomplete: "off"})
28021      */
28022    // defaultAutoCreate : { tag: 'div' },
28023     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28024     /**
28025      * @cfg {String} addTitle Text to include for adding a title.
28026      */
28027     addTitle : false,
28028     //
28029     onResize : function(){
28030         Roo.form.Field.superclass.onResize.apply(this, arguments);
28031     },
28032
28033     initEvents : function(){
28034         // Roo.form.Checkbox.superclass.initEvents.call(this);
28035         // has no events...
28036        
28037     },
28038
28039
28040     getResizeEl : function(){
28041         return this.wrap;
28042     },
28043
28044     getPositionEl : function(){
28045         return this.wrap;
28046     },
28047
28048     // private
28049     onRender : function(ct, position){
28050         
28051         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28052         var style = this.style;
28053         delete this.style;
28054         
28055         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28056         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28057         this.viewEl = this.wrap.createChild({ tag: 'div' });
28058         if (style) {
28059             this.viewEl.applyStyles(style);
28060         }
28061         if (this.width) {
28062             this.viewEl.setWidth(this.width);
28063         }
28064         if (this.height) {
28065             this.viewEl.setHeight(this.height);
28066         }
28067         //if(this.inputValue !== undefined){
28068         //this.setValue(this.value);
28069         
28070         
28071         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28072         
28073         
28074         this.grid.render();
28075         this.grid.getDataSource().on('remove', this.refreshValue, this);
28076         this.grid.getDataSource().on('update', this.refreshValue, this);
28077         this.grid.on('afteredit', this.refreshValue, this);
28078  
28079     },
28080      
28081     
28082     /**
28083      * Sets the value of the item. 
28084      * @param {String} either an object  or a string..
28085      */
28086     setValue : function(v){
28087         //this.value = v;
28088         v = v || []; // empty set..
28089         // this does not seem smart - it really only affects memoryproxy grids..
28090         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28091             var ds = this.grid.getDataSource();
28092             // assumes a json reader..
28093             var data = {}
28094             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28095             ds.loadData( data);
28096         }
28097         Roo.form.GridField.superclass.setValue.call(this, v);
28098         this.refreshValue();
28099         // should load data in the grid really....
28100     },
28101     
28102     // private
28103     refreshValue: function() {
28104          var val = [];
28105         this.grid.getDataSource().each(function(r) {
28106             val.push(r.data);
28107         });
28108         this.el.dom.value = Roo.encode(val);
28109     }
28110     
28111      
28112     
28113     
28114 });/*
28115  * Based on:
28116  * Ext JS Library 1.1.1
28117  * Copyright(c) 2006-2007, Ext JS, LLC.
28118  *
28119  * Originally Released Under LGPL - original licence link has changed is not relivant.
28120  *
28121  * Fork - LGPL
28122  * <script type="text/javascript">
28123  */
28124 /**
28125  * @class Roo.form.DisplayField
28126  * @extends Roo.form.Field
28127  * A generic Field to display non-editable data.
28128  * @constructor
28129  * Creates a new Display Field item.
28130  * @param {Object} config Configuration options
28131  */
28132 Roo.form.DisplayField = function(config){
28133     Roo.form.DisplayField.superclass.constructor.call(this, config);
28134     
28135 };
28136
28137 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28138     inputType:      'hidden',
28139     allowBlank:     true,
28140     readOnly:         true,
28141     
28142  
28143     /**
28144      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28145      */
28146     focusClass : undefined,
28147     /**
28148      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28149      */
28150     fieldClass: 'x-form-field',
28151     
28152      /**
28153      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28154      */
28155     valueRenderer: undefined,
28156     
28157     width: 100,
28158     /**
28159      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28160      * {tag: "input", type: "checkbox", autocomplete: "off"})
28161      */
28162      
28163  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28164
28165     onResize : function(){
28166         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28167         
28168     },
28169
28170     initEvents : function(){
28171         // Roo.form.Checkbox.superclass.initEvents.call(this);
28172         // has no events...
28173        
28174     },
28175
28176
28177     getResizeEl : function(){
28178         return this.wrap;
28179     },
28180
28181     getPositionEl : function(){
28182         return this.wrap;
28183     },
28184
28185     // private
28186     onRender : function(ct, position){
28187         
28188         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28189         //if(this.inputValue !== undefined){
28190         this.wrap = this.el.wrap();
28191         
28192         this.viewEl = this.wrap.createChild({ tag: 'div'});
28193         
28194         if (this.bodyStyle) {
28195             this.viewEl.applyStyles(this.bodyStyle);
28196         }
28197         //this.viewEl.setStyle('padding', '2px');
28198         
28199         this.setValue(this.value);
28200         
28201     },
28202 /*
28203     // private
28204     initValue : Roo.emptyFn,
28205
28206   */
28207
28208         // private
28209     onClick : function(){
28210         
28211     },
28212
28213     /**
28214      * Sets the checked state of the checkbox.
28215      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28216      */
28217     setValue : function(v){
28218         this.value = v;
28219         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28220         // this might be called before we have a dom element..
28221         if (!this.viewEl) {
28222             return;
28223         }
28224         this.viewEl.dom.innerHTML = html;
28225         Roo.form.DisplayField.superclass.setValue.call(this, v);
28226
28227     }
28228 });//<script type="text/javasscript">
28229  
28230
28231 /**
28232  * @class Roo.DDView
28233  * A DnD enabled version of Roo.View.
28234  * @param {Element/String} container The Element in which to create the View.
28235  * @param {String} tpl The template string used to create the markup for each element of the View
28236  * @param {Object} config The configuration properties. These include all the config options of
28237  * {@link Roo.View} plus some specific to this class.<br>
28238  * <p>
28239  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28240  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28241  * <p>
28242  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28243 .x-view-drag-insert-above {
28244         border-top:1px dotted #3366cc;
28245 }
28246 .x-view-drag-insert-below {
28247         border-bottom:1px dotted #3366cc;
28248 }
28249 </code></pre>
28250  * 
28251  */
28252  
28253 Roo.DDView = function(container, tpl, config) {
28254     Roo.DDView.superclass.constructor.apply(this, arguments);
28255     this.getEl().setStyle("outline", "0px none");
28256     this.getEl().unselectable();
28257     if (this.dragGroup) {
28258                 this.setDraggable(this.dragGroup.split(","));
28259     }
28260     if (this.dropGroup) {
28261                 this.setDroppable(this.dropGroup.split(","));
28262     }
28263     if (this.deletable) {
28264         this.setDeletable();
28265     }
28266     this.isDirtyFlag = false;
28267         this.addEvents({
28268                 "drop" : true
28269         });
28270 };
28271
28272 Roo.extend(Roo.DDView, Roo.View, {
28273 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28274 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28275 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28276 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28277
28278         isFormField: true,
28279
28280         reset: Roo.emptyFn,
28281         
28282         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28283
28284         validate: function() {
28285                 return true;
28286         },
28287         
28288         destroy: function() {
28289                 this.purgeListeners();
28290                 this.getEl.removeAllListeners();
28291                 this.getEl().remove();
28292                 if (this.dragZone) {
28293                         if (this.dragZone.destroy) {
28294                                 this.dragZone.destroy();
28295                         }
28296                 }
28297                 if (this.dropZone) {
28298                         if (this.dropZone.destroy) {
28299                                 this.dropZone.destroy();
28300                         }
28301                 }
28302         },
28303
28304 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28305         getName: function() {
28306                 return this.name;
28307         },
28308
28309 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28310         setValue: function(v) {
28311                 if (!this.store) {
28312                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28313                 }
28314                 var data = {};
28315                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28316                 this.store.proxy = new Roo.data.MemoryProxy(data);
28317                 this.store.load();
28318         },
28319
28320 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28321         getValue: function() {
28322                 var result = '(';
28323                 this.store.each(function(rec) {
28324                         result += rec.id + ',';
28325                 });
28326                 return result.substr(0, result.length - 1) + ')';
28327         },
28328         
28329         getIds: function() {
28330                 var i = 0, result = new Array(this.store.getCount());
28331                 this.store.each(function(rec) {
28332                         result[i++] = rec.id;
28333                 });
28334                 return result;
28335         },
28336         
28337         isDirty: function() {
28338                 return this.isDirtyFlag;
28339         },
28340
28341 /**
28342  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28343  *      whole Element becomes the target, and this causes the drop gesture to append.
28344  */
28345     getTargetFromEvent : function(e) {
28346                 var target = e.getTarget();
28347                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28348                 target = target.parentNode;
28349                 }
28350                 if (!target) {
28351                         target = this.el.dom.lastChild || this.el.dom;
28352                 }
28353                 return target;
28354     },
28355
28356 /**
28357  *      Create the drag data which consists of an object which has the property "ddel" as
28358  *      the drag proxy element. 
28359  */
28360     getDragData : function(e) {
28361         var target = this.findItemFromChild(e.getTarget());
28362                 if(target) {
28363                         this.handleSelection(e);
28364                         var selNodes = this.getSelectedNodes();
28365             var dragData = {
28366                 source: this,
28367                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28368                 nodes: selNodes,
28369                 records: []
28370                         };
28371                         var selectedIndices = this.getSelectedIndexes();
28372                         for (var i = 0; i < selectedIndices.length; i++) {
28373                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28374                         }
28375                         if (selNodes.length == 1) {
28376                                 dragData.ddel = target.cloneNode(true); // the div element
28377                         } else {
28378                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28379                                 div.className = 'multi-proxy';
28380                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28381                                         div.appendChild(selNodes[i].cloneNode(true));
28382                                 }
28383                                 dragData.ddel = div;
28384                         }
28385             //console.log(dragData)
28386             //console.log(dragData.ddel.innerHTML)
28387                         return dragData;
28388                 }
28389         //console.log('nodragData')
28390                 return false;
28391     },
28392     
28393 /**     Specify to which ddGroup items in this DDView may be dragged. */
28394     setDraggable: function(ddGroup) {
28395         if (ddGroup instanceof Array) {
28396                 Roo.each(ddGroup, this.setDraggable, this);
28397                 return;
28398         }
28399         if (this.dragZone) {
28400                 this.dragZone.addToGroup(ddGroup);
28401         } else {
28402                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28403                                 containerScroll: true,
28404                                 ddGroup: ddGroup 
28405
28406                         });
28407 //                      Draggability implies selection. DragZone's mousedown selects the element.
28408                         if (!this.multiSelect) { this.singleSelect = true; }
28409
28410 //                      Wire the DragZone's handlers up to methods in *this*
28411                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28412                 }
28413     },
28414
28415 /**     Specify from which ddGroup this DDView accepts drops. */
28416     setDroppable: function(ddGroup) {
28417         if (ddGroup instanceof Array) {
28418                 Roo.each(ddGroup, this.setDroppable, this);
28419                 return;
28420         }
28421         if (this.dropZone) {
28422                 this.dropZone.addToGroup(ddGroup);
28423         } else {
28424                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28425                                 containerScroll: true,
28426                                 ddGroup: ddGroup
28427                         });
28428
28429 //                      Wire the DropZone's handlers up to methods in *this*
28430                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28431                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28432                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28433                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28434                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28435                 }
28436     },
28437
28438 /**     Decide whether to drop above or below a View node. */
28439     getDropPoint : function(e, n, dd){
28440         if (n == this.el.dom) { return "above"; }
28441                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28442                 var c = t + (b - t) / 2;
28443                 var y = Roo.lib.Event.getPageY(e);
28444                 if(y <= c) {
28445                         return "above";
28446                 }else{
28447                         return "below";
28448                 }
28449     },
28450
28451     onNodeEnter : function(n, dd, e, data){
28452                 return false;
28453     },
28454     
28455     onNodeOver : function(n, dd, e, data){
28456                 var pt = this.getDropPoint(e, n, dd);
28457                 // set the insert point style on the target node
28458                 var dragElClass = this.dropNotAllowed;
28459                 if (pt) {
28460                         var targetElClass;
28461                         if (pt == "above"){
28462                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28463                                 targetElClass = "x-view-drag-insert-above";
28464                         } else {
28465                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28466                                 targetElClass = "x-view-drag-insert-below";
28467                         }
28468                         if (this.lastInsertClass != targetElClass){
28469                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28470                                 this.lastInsertClass = targetElClass;
28471                         }
28472                 }
28473                 return dragElClass;
28474         },
28475
28476     onNodeOut : function(n, dd, e, data){
28477                 this.removeDropIndicators(n);
28478     },
28479
28480     onNodeDrop : function(n, dd, e, data){
28481         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28482                 return false;
28483         }
28484         var pt = this.getDropPoint(e, n, dd);
28485                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28486                 if (pt == "below") { insertAt++; }
28487                 for (var i = 0; i < data.records.length; i++) {
28488                         var r = data.records[i];
28489                         var dup = this.store.getById(r.id);
28490                         if (dup && (dd != this.dragZone)) {
28491                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28492                         } else {
28493                                 if (data.copy) {
28494                                         this.store.insert(insertAt++, r.copy());
28495                                 } else {
28496                                         data.source.isDirtyFlag = true;
28497                                         r.store.remove(r);
28498                                         this.store.insert(insertAt++, r);
28499                                 }
28500                                 this.isDirtyFlag = true;
28501                         }
28502                 }
28503                 this.dragZone.cachedTarget = null;
28504                 return true;
28505     },
28506
28507     removeDropIndicators : function(n){
28508                 if(n){
28509                         Roo.fly(n).removeClass([
28510                                 "x-view-drag-insert-above",
28511                                 "x-view-drag-insert-below"]);
28512                         this.lastInsertClass = "_noclass";
28513                 }
28514     },
28515
28516 /**
28517  *      Utility method. Add a delete option to the DDView's context menu.
28518  *      @param {String} imageUrl The URL of the "delete" icon image.
28519  */
28520         setDeletable: function(imageUrl) {
28521                 if (!this.singleSelect && !this.multiSelect) {
28522                         this.singleSelect = true;
28523                 }
28524                 var c = this.getContextMenu();
28525                 this.contextMenu.on("itemclick", function(item) {
28526                         switch (item.id) {
28527                                 case "delete":
28528                                         this.remove(this.getSelectedIndexes());
28529                                         break;
28530                         }
28531                 }, this);
28532                 this.contextMenu.add({
28533                         icon: imageUrl,
28534                         id: "delete",
28535                         text: 'Delete'
28536                 });
28537         },
28538         
28539 /**     Return the context menu for this DDView. */
28540         getContextMenu: function() {
28541                 if (!this.contextMenu) {
28542 //                      Create the View's context menu
28543                         this.contextMenu = new Roo.menu.Menu({
28544                                 id: this.id + "-contextmenu"
28545                         });
28546                         this.el.on("contextmenu", this.showContextMenu, this);
28547                 }
28548                 return this.contextMenu;
28549         },
28550         
28551         disableContextMenu: function() {
28552                 if (this.contextMenu) {
28553                         this.el.un("contextmenu", this.showContextMenu, this);
28554                 }
28555         },
28556
28557         showContextMenu: function(e, item) {
28558         item = this.findItemFromChild(e.getTarget());
28559                 if (item) {
28560                         e.stopEvent();
28561                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28562                         this.contextMenu.showAt(e.getXY());
28563             }
28564     },
28565
28566 /**
28567  *      Remove {@link Roo.data.Record}s at the specified indices.
28568  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28569  */
28570     remove: function(selectedIndices) {
28571                 selectedIndices = [].concat(selectedIndices);
28572                 for (var i = 0; i < selectedIndices.length; i++) {
28573                         var rec = this.store.getAt(selectedIndices[i]);
28574                         this.store.remove(rec);
28575                 }
28576     },
28577
28578 /**
28579  *      Double click fires the event, but also, if this is draggable, and there is only one other
28580  *      related DropZone, it transfers the selected node.
28581  */
28582     onDblClick : function(e){
28583         var item = this.findItemFromChild(e.getTarget());
28584         if(item){
28585             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28586                 return false;
28587             }
28588             if (this.dragGroup) {
28589                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28590                     while (targets.indexOf(this.dropZone) > -1) {
28591                             targets.remove(this.dropZone);
28592                                 }
28593                     if (targets.length == 1) {
28594                                         this.dragZone.cachedTarget = null;
28595                         var el = Roo.get(targets[0].getEl());
28596                         var box = el.getBox(true);
28597                         targets[0].onNodeDrop(el.dom, {
28598                                 target: el.dom,
28599                                 xy: [box.x, box.y + box.height - 1]
28600                         }, null, this.getDragData(e));
28601                     }
28602                 }
28603         }
28604     },
28605     
28606     handleSelection: function(e) {
28607                 this.dragZone.cachedTarget = null;
28608         var item = this.findItemFromChild(e.getTarget());
28609         if (!item) {
28610                 this.clearSelections(true);
28611                 return;
28612         }
28613                 if (item && (this.multiSelect || this.singleSelect)){
28614                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28615                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28616                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28617                                 this.unselect(item);
28618                         } else {
28619                                 this.select(item, this.multiSelect && e.ctrlKey);
28620                                 this.lastSelection = item;
28621                         }
28622                 }
28623     },
28624
28625     onItemClick : function(item, index, e){
28626                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28627                         return false;
28628                 }
28629                 return true;
28630     },
28631
28632     unselect : function(nodeInfo, suppressEvent){
28633                 var node = this.getNode(nodeInfo);
28634                 if(node && this.isSelected(node)){
28635                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28636                                 Roo.fly(node).removeClass(this.selectedClass);
28637                                 this.selections.remove(node);
28638                                 if(!suppressEvent){
28639                                         this.fireEvent("selectionchange", this, this.selections);
28640                                 }
28641                         }
28642                 }
28643     }
28644 });
28645 /*
28646  * Based on:
28647  * Ext JS Library 1.1.1
28648  * Copyright(c) 2006-2007, Ext JS, LLC.
28649  *
28650  * Originally Released Under LGPL - original licence link has changed is not relivant.
28651  *
28652  * Fork - LGPL
28653  * <script type="text/javascript">
28654  */
28655  
28656 /**
28657  * @class Roo.LayoutManager
28658  * @extends Roo.util.Observable
28659  * Base class for layout managers.
28660  */
28661 Roo.LayoutManager = function(container, config){
28662     Roo.LayoutManager.superclass.constructor.call(this);
28663     this.el = Roo.get(container);
28664     // ie scrollbar fix
28665     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28666         document.body.scroll = "no";
28667     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28668         this.el.position('relative');
28669     }
28670     this.id = this.el.id;
28671     this.el.addClass("x-layout-container");
28672     /** false to disable window resize monitoring @type Boolean */
28673     this.monitorWindowResize = true;
28674     this.regions = {};
28675     this.addEvents({
28676         /**
28677          * @event layout
28678          * Fires when a layout is performed. 
28679          * @param {Roo.LayoutManager} this
28680          */
28681         "layout" : true,
28682         /**
28683          * @event regionresized
28684          * Fires when the user resizes a region. 
28685          * @param {Roo.LayoutRegion} region The resized region
28686          * @param {Number} newSize The new size (width for east/west, height for north/south)
28687          */
28688         "regionresized" : true,
28689         /**
28690          * @event regioncollapsed
28691          * Fires when a region is collapsed. 
28692          * @param {Roo.LayoutRegion} region The collapsed region
28693          */
28694         "regioncollapsed" : true,
28695         /**
28696          * @event regionexpanded
28697          * Fires when a region is expanded.  
28698          * @param {Roo.LayoutRegion} region The expanded region
28699          */
28700         "regionexpanded" : true
28701     });
28702     this.updating = false;
28703     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28704 };
28705
28706 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28707     /**
28708      * Returns true if this layout is currently being updated
28709      * @return {Boolean}
28710      */
28711     isUpdating : function(){
28712         return this.updating; 
28713     },
28714     
28715     /**
28716      * Suspend the LayoutManager from doing auto-layouts while
28717      * making multiple add or remove calls
28718      */
28719     beginUpdate : function(){
28720         this.updating = true;    
28721     },
28722     
28723     /**
28724      * Restore auto-layouts and optionally disable the manager from performing a layout
28725      * @param {Boolean} noLayout true to disable a layout update 
28726      */
28727     endUpdate : function(noLayout){
28728         this.updating = false;
28729         if(!noLayout){
28730             this.layout();
28731         }    
28732     },
28733     
28734     layout: function(){
28735         
28736     },
28737     
28738     onRegionResized : function(region, newSize){
28739         this.fireEvent("regionresized", region, newSize);
28740         this.layout();
28741     },
28742     
28743     onRegionCollapsed : function(region){
28744         this.fireEvent("regioncollapsed", region);
28745     },
28746     
28747     onRegionExpanded : function(region){
28748         this.fireEvent("regionexpanded", region);
28749     },
28750         
28751     /**
28752      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28753      * performs box-model adjustments.
28754      * @return {Object} The size as an object {width: (the width), height: (the height)}
28755      */
28756     getViewSize : function(){
28757         var size;
28758         if(this.el.dom != document.body){
28759             size = this.el.getSize();
28760         }else{
28761             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28762         }
28763         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28764         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28765         return size;
28766     },
28767     
28768     /**
28769      * Returns the Element this layout is bound to.
28770      * @return {Roo.Element}
28771      */
28772     getEl : function(){
28773         return this.el;
28774     },
28775     
28776     /**
28777      * Returns the specified region.
28778      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28779      * @return {Roo.LayoutRegion}
28780      */
28781     getRegion : function(target){
28782         return this.regions[target.toLowerCase()];
28783     },
28784     
28785     onWindowResize : function(){
28786         if(this.monitorWindowResize){
28787             this.layout();
28788         }
28789     }
28790 });/*
28791  * Based on:
28792  * Ext JS Library 1.1.1
28793  * Copyright(c) 2006-2007, Ext JS, LLC.
28794  *
28795  * Originally Released Under LGPL - original licence link has changed is not relivant.
28796  *
28797  * Fork - LGPL
28798  * <script type="text/javascript">
28799  */
28800 /**
28801  * @class Roo.BorderLayout
28802  * @extends Roo.LayoutManager
28803  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28804  * please see: <br><br>
28805  * <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>
28806  * <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>
28807  * Example:
28808  <pre><code>
28809  var layout = new Roo.BorderLayout(document.body, {
28810     north: {
28811         initialSize: 25,
28812         titlebar: false
28813     },
28814     west: {
28815         split:true,
28816         initialSize: 200,
28817         minSize: 175,
28818         maxSize: 400,
28819         titlebar: true,
28820         collapsible: true
28821     },
28822     east: {
28823         split:true,
28824         initialSize: 202,
28825         minSize: 175,
28826         maxSize: 400,
28827         titlebar: true,
28828         collapsible: true
28829     },
28830     south: {
28831         split:true,
28832         initialSize: 100,
28833         minSize: 100,
28834         maxSize: 200,
28835         titlebar: true,
28836         collapsible: true
28837     },
28838     center: {
28839         titlebar: true,
28840         autoScroll:true,
28841         resizeTabs: true,
28842         minTabWidth: 50,
28843         preferredTabWidth: 150
28844     }
28845 });
28846
28847 // shorthand
28848 var CP = Roo.ContentPanel;
28849
28850 layout.beginUpdate();
28851 layout.add("north", new CP("north", "North"));
28852 layout.add("south", new CP("south", {title: "South", closable: true}));
28853 layout.add("west", new CP("west", {title: "West"}));
28854 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28855 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28856 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28857 layout.getRegion("center").showPanel("center1");
28858 layout.endUpdate();
28859 </code></pre>
28860
28861 <b>The container the layout is rendered into can be either the body element or any other element.
28862 If it is not the body element, the container needs to either be an absolute positioned element,
28863 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28864 the container size if it is not the body element.</b>
28865
28866 * @constructor
28867 * Create a new BorderLayout
28868 * @param {String/HTMLElement/Element} container The container this layout is bound to
28869 * @param {Object} config Configuration options
28870  */
28871 Roo.BorderLayout = function(container, config){
28872     config = config || {};
28873     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28874     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28875     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28876         var target = this.factory.validRegions[i];
28877         if(config[target]){
28878             this.addRegion(target, config[target]);
28879         }
28880     }
28881 };
28882
28883 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28884     /**
28885      * Creates and adds a new region if it doesn't already exist.
28886      * @param {String} target The target region key (north, south, east, west or center).
28887      * @param {Object} config The regions config object
28888      * @return {BorderLayoutRegion} The new region
28889      */
28890     addRegion : function(target, config){
28891         if(!this.regions[target]){
28892             var r = this.factory.create(target, this, config);
28893             this.bindRegion(target, r);
28894         }
28895         return this.regions[target];
28896     },
28897
28898     // private (kinda)
28899     bindRegion : function(name, r){
28900         this.regions[name] = r;
28901         r.on("visibilitychange", this.layout, this);
28902         r.on("paneladded", this.layout, this);
28903         r.on("panelremoved", this.layout, this);
28904         r.on("invalidated", this.layout, this);
28905         r.on("resized", this.onRegionResized, this);
28906         r.on("collapsed", this.onRegionCollapsed, this);
28907         r.on("expanded", this.onRegionExpanded, this);
28908     },
28909
28910     /**
28911      * Performs a layout update.
28912      */
28913     layout : function(){
28914         if(this.updating) return;
28915         var size = this.getViewSize();
28916         var w = size.width;
28917         var h = size.height;
28918         var centerW = w;
28919         var centerH = h;
28920         var centerY = 0;
28921         var centerX = 0;
28922         //var x = 0, y = 0;
28923
28924         var rs = this.regions;
28925         var north = rs["north"];
28926         var south = rs["south"]; 
28927         var west = rs["west"];
28928         var east = rs["east"];
28929         var center = rs["center"];
28930         //if(this.hideOnLayout){ // not supported anymore
28931             //c.el.setStyle("display", "none");
28932         //}
28933         if(north && north.isVisible()){
28934             var b = north.getBox();
28935             var m = north.getMargins();
28936             b.width = w - (m.left+m.right);
28937             b.x = m.left;
28938             b.y = m.top;
28939             centerY = b.height + b.y + m.bottom;
28940             centerH -= centerY;
28941             north.updateBox(this.safeBox(b));
28942         }
28943         if(south && south.isVisible()){
28944             var b = south.getBox();
28945             var m = south.getMargins();
28946             b.width = w - (m.left+m.right);
28947             b.x = m.left;
28948             var totalHeight = (b.height + m.top + m.bottom);
28949             b.y = h - totalHeight + m.top;
28950             centerH -= totalHeight;
28951             south.updateBox(this.safeBox(b));
28952         }
28953         if(west && west.isVisible()){
28954             var b = west.getBox();
28955             var m = west.getMargins();
28956             b.height = centerH - (m.top+m.bottom);
28957             b.x = m.left;
28958             b.y = centerY + m.top;
28959             var totalWidth = (b.width + m.left + m.right);
28960             centerX += totalWidth;
28961             centerW -= totalWidth;
28962             west.updateBox(this.safeBox(b));
28963         }
28964         if(east && east.isVisible()){
28965             var b = east.getBox();
28966             var m = east.getMargins();
28967             b.height = centerH - (m.top+m.bottom);
28968             var totalWidth = (b.width + m.left + m.right);
28969             b.x = w - totalWidth + m.left;
28970             b.y = centerY + m.top;
28971             centerW -= totalWidth;
28972             east.updateBox(this.safeBox(b));
28973         }
28974         if(center){
28975             var m = center.getMargins();
28976             var centerBox = {
28977                 x: centerX + m.left,
28978                 y: centerY + m.top,
28979                 width: centerW - (m.left+m.right),
28980                 height: centerH - (m.top+m.bottom)
28981             };
28982             //if(this.hideOnLayout){
28983                 //center.el.setStyle("display", "block");
28984             //}
28985             center.updateBox(this.safeBox(centerBox));
28986         }
28987         this.el.repaint();
28988         this.fireEvent("layout", this);
28989     },
28990
28991     // private
28992     safeBox : function(box){
28993         box.width = Math.max(0, box.width);
28994         box.height = Math.max(0, box.height);
28995         return box;
28996     },
28997
28998     /**
28999      * Adds a ContentPanel (or subclass) to this layout.
29000      * @param {String} target The target region key (north, south, east, west or center).
29001      * @param {Roo.ContentPanel} panel The panel to add
29002      * @return {Roo.ContentPanel} The added panel
29003      */
29004     add : function(target, panel){
29005          
29006         target = target.toLowerCase();
29007         return this.regions[target].add(panel);
29008     },
29009
29010     /**
29011      * Remove a ContentPanel (or subclass) to this layout.
29012      * @param {String} target The target region key (north, south, east, west or center).
29013      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29014      * @return {Roo.ContentPanel} The removed panel
29015      */
29016     remove : function(target, panel){
29017         target = target.toLowerCase();
29018         return this.regions[target].remove(panel);
29019     },
29020
29021     /**
29022      * Searches all regions for a panel with the specified id
29023      * @param {String} panelId
29024      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29025      */
29026     findPanel : function(panelId){
29027         var rs = this.regions;
29028         for(var target in rs){
29029             if(typeof rs[target] != "function"){
29030                 var p = rs[target].getPanel(panelId);
29031                 if(p){
29032                     return p;
29033                 }
29034             }
29035         }
29036         return null;
29037     },
29038
29039     /**
29040      * Searches all regions for a panel with the specified id and activates (shows) it.
29041      * @param {String/ContentPanel} panelId The panels id or the panel itself
29042      * @return {Roo.ContentPanel} The shown panel or null
29043      */
29044     showPanel : function(panelId) {
29045       var rs = this.regions;
29046       for(var target in rs){
29047          var r = rs[target];
29048          if(typeof r != "function"){
29049             if(r.hasPanel(panelId)){
29050                return r.showPanel(panelId);
29051             }
29052          }
29053       }
29054       return null;
29055    },
29056
29057    /**
29058      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29059      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29060      */
29061     restoreState : function(provider){
29062         if(!provider){
29063             provider = Roo.state.Manager;
29064         }
29065         var sm = new Roo.LayoutStateManager();
29066         sm.init(this, provider);
29067     },
29068
29069     /**
29070      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29071      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29072      * a valid ContentPanel config object.  Example:
29073      * <pre><code>
29074 // Create the main layout
29075 var layout = new Roo.BorderLayout('main-ct', {
29076     west: {
29077         split:true,
29078         minSize: 175,
29079         titlebar: true
29080     },
29081     center: {
29082         title:'Components'
29083     }
29084 }, 'main-ct');
29085
29086 // Create and add multiple ContentPanels at once via configs
29087 layout.batchAdd({
29088    west: {
29089        id: 'source-files',
29090        autoCreate:true,
29091        title:'Ext Source Files',
29092        autoScroll:true,
29093        fitToFrame:true
29094    },
29095    center : {
29096        el: cview,
29097        autoScroll:true,
29098        fitToFrame:true,
29099        toolbar: tb,
29100        resizeEl:'cbody'
29101    }
29102 });
29103 </code></pre>
29104      * @param {Object} regions An object containing ContentPanel configs by region name
29105      */
29106     batchAdd : function(regions){
29107         this.beginUpdate();
29108         for(var rname in regions){
29109             var lr = this.regions[rname];
29110             if(lr){
29111                 this.addTypedPanels(lr, regions[rname]);
29112             }
29113         }
29114         this.endUpdate();
29115     },
29116
29117     // private
29118     addTypedPanels : function(lr, ps){
29119         if(typeof ps == 'string'){
29120             lr.add(new Roo.ContentPanel(ps));
29121         }
29122         else if(ps instanceof Array){
29123             for(var i =0, len = ps.length; i < len; i++){
29124                 this.addTypedPanels(lr, ps[i]);
29125             }
29126         }
29127         else if(!ps.events){ // raw config?
29128             var el = ps.el;
29129             delete ps.el; // prevent conflict
29130             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29131         }
29132         else {  // panel object assumed!
29133             lr.add(ps);
29134         }
29135     },
29136     /**
29137      * Adds a xtype elements to the layout.
29138      * <pre><code>
29139
29140 layout.addxtype({
29141        xtype : 'ContentPanel',
29142        region: 'west',
29143        items: [ .... ]
29144    }
29145 );
29146
29147 layout.addxtype({
29148         xtype : 'NestedLayoutPanel',
29149         region: 'west',
29150         layout: {
29151            center: { },
29152            west: { }   
29153         },
29154         items : [ ... list of content panels or nested layout panels.. ]
29155    }
29156 );
29157 </code></pre>
29158      * @param {Object} cfg Xtype definition of item to add.
29159      */
29160     addxtype : function(cfg)
29161     {
29162         // basically accepts a pannel...
29163         // can accept a layout region..!?!?
29164        // console.log('BorderLayout add ' + cfg.xtype)
29165         
29166         if (!cfg.xtype.match(/Panel$/)) {
29167             return false;
29168         }
29169         var ret = false;
29170         var region = cfg.region;
29171         delete cfg.region;
29172         
29173           
29174         var xitems = [];
29175         if (cfg.items) {
29176             xitems = cfg.items;
29177             delete cfg.items;
29178         }
29179         
29180         
29181         switch(cfg.xtype) 
29182         {
29183             case 'ContentPanel':  // ContentPanel (el, cfg)
29184             case 'ScrollPanel':  // ContentPanel (el, cfg)
29185                 if(cfg.autoCreate) {
29186                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29187                 } else {
29188                     var el = this.el.createChild();
29189                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29190                 }
29191                 
29192                 this.add(region, ret);
29193                 break;
29194             
29195             
29196             case 'TreePanel': // our new panel!
29197                 cfg.el = this.el.createChild();
29198                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29199                 this.add(region, ret);
29200                 break;
29201             
29202             case 'NestedLayoutPanel': 
29203                 // create a new Layout (which is  a Border Layout...
29204                 var el = this.el.createChild();
29205                 var clayout = cfg.layout;
29206                 delete cfg.layout;
29207                 clayout.items   = clayout.items  || [];
29208                 // replace this exitems with the clayout ones..
29209                 xitems = clayout.items;
29210                  
29211                 
29212                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29213                     cfg.background = false;
29214                 }
29215                 var layout = new Roo.BorderLayout(el, clayout);
29216                 
29217                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29218                 //console.log('adding nested layout panel '  + cfg.toSource());
29219                 this.add(region, ret);
29220                 
29221                 break;
29222                 
29223             case 'GridPanel': 
29224             
29225                 // needs grid and region
29226                 
29227                 //var el = this.getRegion(region).el.createChild();
29228                 var el = this.el.createChild();
29229                 // create the grid first...
29230                 
29231                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29232                 delete cfg.grid;
29233                 if (region == 'center' && this.active ) {
29234                     cfg.background = false;
29235                 }
29236                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29237                 
29238                 this.add(region, ret);
29239                 if (cfg.background) {
29240                     ret.on('activate', function(gp) {
29241                         if (!gp.grid.rendered) {
29242                             gp.grid.render();
29243                         }
29244                     });
29245                 } else {
29246                     grid.render();
29247                 }
29248                 break;
29249            
29250                
29251                 
29252                 
29253             default: 
29254                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29255                 return;
29256              // GridPanel (grid, cfg)
29257             
29258         }
29259         this.beginUpdate();
29260         // add children..
29261         Roo.each(xitems, function(i)  {
29262             ret.addxtype(i);
29263         });
29264         this.endUpdate();
29265         return ret;
29266         
29267     }
29268 });
29269
29270 /**
29271  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29272  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29273  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29274  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29275  * <pre><code>
29276 // shorthand
29277 var CP = Roo.ContentPanel;
29278
29279 var layout = Roo.BorderLayout.create({
29280     north: {
29281         initialSize: 25,
29282         titlebar: false,
29283         panels: [new CP("north", "North")]
29284     },
29285     west: {
29286         split:true,
29287         initialSize: 200,
29288         minSize: 175,
29289         maxSize: 400,
29290         titlebar: true,
29291         collapsible: true,
29292         panels: [new CP("west", {title: "West"})]
29293     },
29294     east: {
29295         split:true,
29296         initialSize: 202,
29297         minSize: 175,
29298         maxSize: 400,
29299         titlebar: true,
29300         collapsible: true,
29301         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29302     },
29303     south: {
29304         split:true,
29305         initialSize: 100,
29306         minSize: 100,
29307         maxSize: 200,
29308         titlebar: true,
29309         collapsible: true,
29310         panels: [new CP("south", {title: "South", closable: true})]
29311     },
29312     center: {
29313         titlebar: true,
29314         autoScroll:true,
29315         resizeTabs: true,
29316         minTabWidth: 50,
29317         preferredTabWidth: 150,
29318         panels: [
29319             new CP("center1", {title: "Close Me", closable: true}),
29320             new CP("center2", {title: "Center Panel", closable: false})
29321         ]
29322     }
29323 }, document.body);
29324
29325 layout.getRegion("center").showPanel("center1");
29326 </code></pre>
29327  * @param config
29328  * @param targetEl
29329  */
29330 Roo.BorderLayout.create = function(config, targetEl){
29331     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29332     layout.beginUpdate();
29333     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29334     for(var j = 0, jlen = regions.length; j < jlen; j++){
29335         var lr = regions[j];
29336         if(layout.regions[lr] && config[lr].panels){
29337             var r = layout.regions[lr];
29338             var ps = config[lr].panels;
29339             layout.addTypedPanels(r, ps);
29340         }
29341     }
29342     layout.endUpdate();
29343     return layout;
29344 };
29345
29346 // private
29347 Roo.BorderLayout.RegionFactory = {
29348     // private
29349     validRegions : ["north","south","east","west","center"],
29350
29351     // private
29352     create : function(target, mgr, config){
29353         target = target.toLowerCase();
29354         if(config.lightweight || config.basic){
29355             return new Roo.BasicLayoutRegion(mgr, config, target);
29356         }
29357         switch(target){
29358             case "north":
29359                 return new Roo.NorthLayoutRegion(mgr, config);
29360             case "south":
29361                 return new Roo.SouthLayoutRegion(mgr, config);
29362             case "east":
29363                 return new Roo.EastLayoutRegion(mgr, config);
29364             case "west":
29365                 return new Roo.WestLayoutRegion(mgr, config);
29366             case "center":
29367                 return new Roo.CenterLayoutRegion(mgr, config);
29368         }
29369         throw 'Layout region "'+target+'" not supported.';
29370     }
29371 };/*
29372  * Based on:
29373  * Ext JS Library 1.1.1
29374  * Copyright(c) 2006-2007, Ext JS, LLC.
29375  *
29376  * Originally Released Under LGPL - original licence link has changed is not relivant.
29377  *
29378  * Fork - LGPL
29379  * <script type="text/javascript">
29380  */
29381  
29382 /**
29383  * @class Roo.BasicLayoutRegion
29384  * @extends Roo.util.Observable
29385  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29386  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29387  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29388  */
29389 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29390     this.mgr = mgr;
29391     this.position  = pos;
29392     this.events = {
29393         /**
29394          * @scope Roo.BasicLayoutRegion
29395          */
29396         
29397         /**
29398          * @event beforeremove
29399          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29400          * @param {Roo.LayoutRegion} this
29401          * @param {Roo.ContentPanel} panel The panel
29402          * @param {Object} e The cancel event object
29403          */
29404         "beforeremove" : true,
29405         /**
29406          * @event invalidated
29407          * Fires when the layout for this region is changed.
29408          * @param {Roo.LayoutRegion} this
29409          */
29410         "invalidated" : true,
29411         /**
29412          * @event visibilitychange
29413          * Fires when this region is shown or hidden 
29414          * @param {Roo.LayoutRegion} this
29415          * @param {Boolean} visibility true or false
29416          */
29417         "visibilitychange" : true,
29418         /**
29419          * @event paneladded
29420          * Fires when a panel is added. 
29421          * @param {Roo.LayoutRegion} this
29422          * @param {Roo.ContentPanel} panel The panel
29423          */
29424         "paneladded" : true,
29425         /**
29426          * @event panelremoved
29427          * Fires when a panel is removed. 
29428          * @param {Roo.LayoutRegion} this
29429          * @param {Roo.ContentPanel} panel The panel
29430          */
29431         "panelremoved" : true,
29432         /**
29433          * @event collapsed
29434          * Fires when this region is collapsed.
29435          * @param {Roo.LayoutRegion} this
29436          */
29437         "collapsed" : true,
29438         /**
29439          * @event expanded
29440          * Fires when this region is expanded.
29441          * @param {Roo.LayoutRegion} this
29442          */
29443         "expanded" : true,
29444         /**
29445          * @event slideshow
29446          * Fires when this region is slid into view.
29447          * @param {Roo.LayoutRegion} this
29448          */
29449         "slideshow" : true,
29450         /**
29451          * @event slidehide
29452          * Fires when this region slides out of view. 
29453          * @param {Roo.LayoutRegion} this
29454          */
29455         "slidehide" : true,
29456         /**
29457          * @event panelactivated
29458          * Fires when a panel is activated. 
29459          * @param {Roo.LayoutRegion} this
29460          * @param {Roo.ContentPanel} panel The activated panel
29461          */
29462         "panelactivated" : true,
29463         /**
29464          * @event resized
29465          * Fires when the user resizes this region. 
29466          * @param {Roo.LayoutRegion} this
29467          * @param {Number} newSize The new size (width for east/west, height for north/south)
29468          */
29469         "resized" : true
29470     };
29471     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29472     this.panels = new Roo.util.MixedCollection();
29473     this.panels.getKey = this.getPanelId.createDelegate(this);
29474     this.box = null;
29475     this.activePanel = null;
29476     // ensure listeners are added...
29477     
29478     if (config.listeners || config.events) {
29479         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29480             listeners : config.listeners || {},
29481             events : config.events || {}
29482         });
29483     }
29484     
29485     if(skipConfig !== true){
29486         this.applyConfig(config);
29487     }
29488 };
29489
29490 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29491     getPanelId : function(p){
29492         return p.getId();
29493     },
29494     
29495     applyConfig : function(config){
29496         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29497         this.config = config;
29498         
29499     },
29500     
29501     /**
29502      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29503      * the width, for horizontal (north, south) the height.
29504      * @param {Number} newSize The new width or height
29505      */
29506     resizeTo : function(newSize){
29507         var el = this.el ? this.el :
29508                  (this.activePanel ? this.activePanel.getEl() : null);
29509         if(el){
29510             switch(this.position){
29511                 case "east":
29512                 case "west":
29513                     el.setWidth(newSize);
29514                     this.fireEvent("resized", this, newSize);
29515                 break;
29516                 case "north":
29517                 case "south":
29518                     el.setHeight(newSize);
29519                     this.fireEvent("resized", this, newSize);
29520                 break;                
29521             }
29522         }
29523     },
29524     
29525     getBox : function(){
29526         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29527     },
29528     
29529     getMargins : function(){
29530         return this.margins;
29531     },
29532     
29533     updateBox : function(box){
29534         this.box = box;
29535         var el = this.activePanel.getEl();
29536         el.dom.style.left = box.x + "px";
29537         el.dom.style.top = box.y + "px";
29538         this.activePanel.setSize(box.width, box.height);
29539     },
29540     
29541     /**
29542      * Returns the container element for this region.
29543      * @return {Roo.Element}
29544      */
29545     getEl : function(){
29546         return this.activePanel;
29547     },
29548     
29549     /**
29550      * Returns true if this region is currently visible.
29551      * @return {Boolean}
29552      */
29553     isVisible : function(){
29554         return this.activePanel ? true : false;
29555     },
29556     
29557     setActivePanel : function(panel){
29558         panel = this.getPanel(panel);
29559         if(this.activePanel && this.activePanel != panel){
29560             this.activePanel.setActiveState(false);
29561             this.activePanel.getEl().setLeftTop(-10000,-10000);
29562         }
29563         this.activePanel = panel;
29564         panel.setActiveState(true);
29565         if(this.box){
29566             panel.setSize(this.box.width, this.box.height);
29567         }
29568         this.fireEvent("panelactivated", this, panel);
29569         this.fireEvent("invalidated");
29570     },
29571     
29572     /**
29573      * Show the specified panel.
29574      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29575      * @return {Roo.ContentPanel} The shown panel or null
29576      */
29577     showPanel : function(panel){
29578         if(panel = this.getPanel(panel)){
29579             this.setActivePanel(panel);
29580         }
29581         return panel;
29582     },
29583     
29584     /**
29585      * Get the active panel for this region.
29586      * @return {Roo.ContentPanel} The active panel or null
29587      */
29588     getActivePanel : function(){
29589         return this.activePanel;
29590     },
29591     
29592     /**
29593      * Add the passed ContentPanel(s)
29594      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29595      * @return {Roo.ContentPanel} The panel added (if only one was added)
29596      */
29597     add : function(panel){
29598         if(arguments.length > 1){
29599             for(var i = 0, len = arguments.length; i < len; i++) {
29600                 this.add(arguments[i]);
29601             }
29602             return null;
29603         }
29604         if(this.hasPanel(panel)){
29605             this.showPanel(panel);
29606             return panel;
29607         }
29608         var el = panel.getEl();
29609         if(el.dom.parentNode != this.mgr.el.dom){
29610             this.mgr.el.dom.appendChild(el.dom);
29611         }
29612         if(panel.setRegion){
29613             panel.setRegion(this);
29614         }
29615         this.panels.add(panel);
29616         el.setStyle("position", "absolute");
29617         if(!panel.background){
29618             this.setActivePanel(panel);
29619             if(this.config.initialSize && this.panels.getCount()==1){
29620                 this.resizeTo(this.config.initialSize);
29621             }
29622         }
29623         this.fireEvent("paneladded", this, panel);
29624         return panel;
29625     },
29626     
29627     /**
29628      * Returns true if the panel is in this region.
29629      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29630      * @return {Boolean}
29631      */
29632     hasPanel : function(panel){
29633         if(typeof panel == "object"){ // must be panel obj
29634             panel = panel.getId();
29635         }
29636         return this.getPanel(panel) ? true : false;
29637     },
29638     
29639     /**
29640      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29641      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29642      * @param {Boolean} preservePanel Overrides the config preservePanel option
29643      * @return {Roo.ContentPanel} The panel that was removed
29644      */
29645     remove : function(panel, preservePanel){
29646         panel = this.getPanel(panel);
29647         if(!panel){
29648             return null;
29649         }
29650         var e = {};
29651         this.fireEvent("beforeremove", this, panel, e);
29652         if(e.cancel === true){
29653             return null;
29654         }
29655         var panelId = panel.getId();
29656         this.panels.removeKey(panelId);
29657         return panel;
29658     },
29659     
29660     /**
29661      * Returns the panel specified or null if it's not in this region.
29662      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29663      * @return {Roo.ContentPanel}
29664      */
29665     getPanel : function(id){
29666         if(typeof id == "object"){ // must be panel obj
29667             return id;
29668         }
29669         return this.panels.get(id);
29670     },
29671     
29672     /**
29673      * Returns this regions position (north/south/east/west/center).
29674      * @return {String} 
29675      */
29676     getPosition: function(){
29677         return this.position;    
29678     }
29679 });/*
29680  * Based on:
29681  * Ext JS Library 1.1.1
29682  * Copyright(c) 2006-2007, Ext JS, LLC.
29683  *
29684  * Originally Released Under LGPL - original licence link has changed is not relivant.
29685  *
29686  * Fork - LGPL
29687  * <script type="text/javascript">
29688  */
29689  
29690 /**
29691  * @class Roo.LayoutRegion
29692  * @extends Roo.BasicLayoutRegion
29693  * This class represents a region in a layout manager.
29694  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29695  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29696  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29697  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29698  * @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})
29699  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29700  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29701  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29702  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29703  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29704  * @cfg {String} title The title for the region (overrides panel titles)
29705  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29706  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29707  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29708  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29709  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29710  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29711  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29712  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29713  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29714  * @cfg {Boolean} showPin True to show a pin button
29715 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29716 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29717 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29718 * @cfg {Number} width  For East/West panels
29719 * @cfg {Number} height For North/South panels
29720 * @cfg {Boolean} split To show the splitter
29721  */
29722 Roo.LayoutRegion = function(mgr, config, pos){
29723     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29724     var dh = Roo.DomHelper;
29725     /** This region's container element 
29726     * @type Roo.Element */
29727     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29728     /** This region's title element 
29729     * @type Roo.Element */
29730
29731     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29732         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29733         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29734     ]}, true);
29735     this.titleEl.enableDisplayMode();
29736     /** This region's title text element 
29737     * @type HTMLElement */
29738     this.titleTextEl = this.titleEl.dom.firstChild;
29739     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29740     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29741     this.closeBtn.enableDisplayMode();
29742     this.closeBtn.on("click", this.closeClicked, this);
29743     this.closeBtn.hide();
29744
29745     this.createBody(config);
29746     this.visible = true;
29747     this.collapsed = false;
29748
29749     if(config.hideWhenEmpty){
29750         this.hide();
29751         this.on("paneladded", this.validateVisibility, this);
29752         this.on("panelremoved", this.validateVisibility, this);
29753     }
29754     this.applyConfig(config);
29755 };
29756
29757 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29758
29759     createBody : function(){
29760         /** This region's body element 
29761         * @type Roo.Element */
29762         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29763     },
29764
29765     applyConfig : function(c){
29766         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29767             var dh = Roo.DomHelper;
29768             if(c.titlebar !== false){
29769                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29770                 this.collapseBtn.on("click", this.collapse, this);
29771                 this.collapseBtn.enableDisplayMode();
29772
29773                 if(c.showPin === true || this.showPin){
29774                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29775                     this.stickBtn.enableDisplayMode();
29776                     this.stickBtn.on("click", this.expand, this);
29777                     this.stickBtn.hide();
29778                 }
29779             }
29780             /** This region's collapsed element
29781             * @type Roo.Element */
29782             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29783                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29784             ]}, true);
29785             if(c.floatable !== false){
29786                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29787                this.collapsedEl.on("click", this.collapseClick, this);
29788             }
29789
29790             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29791                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29792                    id: "message", unselectable: "on", style:{"float":"left"}});
29793                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29794              }
29795             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29796             this.expandBtn.on("click", this.expand, this);
29797         }
29798         if(this.collapseBtn){
29799             this.collapseBtn.setVisible(c.collapsible == true);
29800         }
29801         this.cmargins = c.cmargins || this.cmargins ||
29802                          (this.position == "west" || this.position == "east" ?
29803                              {top: 0, left: 2, right:2, bottom: 0} :
29804                              {top: 2, left: 0, right:0, bottom: 2});
29805         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29806         this.bottomTabs = c.tabPosition != "top";
29807         this.autoScroll = c.autoScroll || false;
29808         if(this.autoScroll){
29809             this.bodyEl.setStyle("overflow", "auto");
29810         }else{
29811             this.bodyEl.setStyle("overflow", "hidden");
29812         }
29813         //if(c.titlebar !== false){
29814             if((!c.titlebar && !c.title) || c.titlebar === false){
29815                 this.titleEl.hide();
29816             }else{
29817                 this.titleEl.show();
29818                 if(c.title){
29819                     this.titleTextEl.innerHTML = c.title;
29820                 }
29821             }
29822         //}
29823         this.duration = c.duration || .30;
29824         this.slideDuration = c.slideDuration || .45;
29825         this.config = c;
29826         if(c.collapsed){
29827             this.collapse(true);
29828         }
29829         if(c.hidden){
29830             this.hide();
29831         }
29832     },
29833     /**
29834      * Returns true if this region is currently visible.
29835      * @return {Boolean}
29836      */
29837     isVisible : function(){
29838         return this.visible;
29839     },
29840
29841     /**
29842      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29843      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29844      */
29845     setCollapsedTitle : function(title){
29846         title = title || "&#160;";
29847         if(this.collapsedTitleTextEl){
29848             this.collapsedTitleTextEl.innerHTML = title;
29849         }
29850     },
29851
29852     getBox : function(){
29853         var b;
29854         if(!this.collapsed){
29855             b = this.el.getBox(false, true);
29856         }else{
29857             b = this.collapsedEl.getBox(false, true);
29858         }
29859         return b;
29860     },
29861
29862     getMargins : function(){
29863         return this.collapsed ? this.cmargins : this.margins;
29864     },
29865
29866     highlight : function(){
29867         this.el.addClass("x-layout-panel-dragover");
29868     },
29869
29870     unhighlight : function(){
29871         this.el.removeClass("x-layout-panel-dragover");
29872     },
29873
29874     updateBox : function(box){
29875         this.box = box;
29876         if(!this.collapsed){
29877             this.el.dom.style.left = box.x + "px";
29878             this.el.dom.style.top = box.y + "px";
29879             this.updateBody(box.width, box.height);
29880         }else{
29881             this.collapsedEl.dom.style.left = box.x + "px";
29882             this.collapsedEl.dom.style.top = box.y + "px";
29883             this.collapsedEl.setSize(box.width, box.height);
29884         }
29885         if(this.tabs){
29886             this.tabs.autoSizeTabs();
29887         }
29888     },
29889
29890     updateBody : function(w, h){
29891         if(w !== null){
29892             this.el.setWidth(w);
29893             w -= this.el.getBorderWidth("rl");
29894             if(this.config.adjustments){
29895                 w += this.config.adjustments[0];
29896             }
29897         }
29898         if(h !== null){
29899             this.el.setHeight(h);
29900             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29901             h -= this.el.getBorderWidth("tb");
29902             if(this.config.adjustments){
29903                 h += this.config.adjustments[1];
29904             }
29905             this.bodyEl.setHeight(h);
29906             if(this.tabs){
29907                 h = this.tabs.syncHeight(h);
29908             }
29909         }
29910         if(this.panelSize){
29911             w = w !== null ? w : this.panelSize.width;
29912             h = h !== null ? h : this.panelSize.height;
29913         }
29914         if(this.activePanel){
29915             var el = this.activePanel.getEl();
29916             w = w !== null ? w : el.getWidth();
29917             h = h !== null ? h : el.getHeight();
29918             this.panelSize = {width: w, height: h};
29919             this.activePanel.setSize(w, h);
29920         }
29921         if(Roo.isIE && this.tabs){
29922             this.tabs.el.repaint();
29923         }
29924     },
29925
29926     /**
29927      * Returns the container element for this region.
29928      * @return {Roo.Element}
29929      */
29930     getEl : function(){
29931         return this.el;
29932     },
29933
29934     /**
29935      * Hides this region.
29936      */
29937     hide : function(){
29938         if(!this.collapsed){
29939             this.el.dom.style.left = "-2000px";
29940             this.el.hide();
29941         }else{
29942             this.collapsedEl.dom.style.left = "-2000px";
29943             this.collapsedEl.hide();
29944         }
29945         this.visible = false;
29946         this.fireEvent("visibilitychange", this, false);
29947     },
29948
29949     /**
29950      * Shows this region if it was previously hidden.
29951      */
29952     show : function(){
29953         if(!this.collapsed){
29954             this.el.show();
29955         }else{
29956             this.collapsedEl.show();
29957         }
29958         this.visible = true;
29959         this.fireEvent("visibilitychange", this, true);
29960     },
29961
29962     closeClicked : function(){
29963         if(this.activePanel){
29964             this.remove(this.activePanel);
29965         }
29966     },
29967
29968     collapseClick : function(e){
29969         if(this.isSlid){
29970            e.stopPropagation();
29971            this.slideIn();
29972         }else{
29973            e.stopPropagation();
29974            this.slideOut();
29975         }
29976     },
29977
29978     /**
29979      * Collapses this region.
29980      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29981      */
29982     collapse : function(skipAnim){
29983         if(this.collapsed) return;
29984         this.collapsed = true;
29985         if(this.split){
29986             this.split.el.hide();
29987         }
29988         if(this.config.animate && skipAnim !== true){
29989             this.fireEvent("invalidated", this);
29990             this.animateCollapse();
29991         }else{
29992             this.el.setLocation(-20000,-20000);
29993             this.el.hide();
29994             this.collapsedEl.show();
29995             this.fireEvent("collapsed", this);
29996             this.fireEvent("invalidated", this);
29997         }
29998     },
29999
30000     animateCollapse : function(){
30001         // overridden
30002     },
30003
30004     /**
30005      * Expands this region if it was previously collapsed.
30006      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30007      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30008      */
30009     expand : function(e, skipAnim){
30010         if(e) e.stopPropagation();
30011         if(!this.collapsed || this.el.hasActiveFx()) return;
30012         if(this.isSlid){
30013             this.afterSlideIn();
30014             skipAnim = true;
30015         }
30016         this.collapsed = false;
30017         if(this.config.animate && skipAnim !== true){
30018             this.animateExpand();
30019         }else{
30020             this.el.show();
30021             if(this.split){
30022                 this.split.el.show();
30023             }
30024             this.collapsedEl.setLocation(-2000,-2000);
30025             this.collapsedEl.hide();
30026             this.fireEvent("invalidated", this);
30027             this.fireEvent("expanded", this);
30028         }
30029     },
30030
30031     animateExpand : function(){
30032         // overridden
30033     },
30034
30035     initTabs : function(){
30036         this.bodyEl.setStyle("overflow", "hidden");
30037         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30038             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30039             disableTooltips: this.config.disableTabTips
30040         });
30041         if(this.config.hideTabs){
30042             ts.stripWrap.setDisplayed(false);
30043         }
30044         this.tabs = ts;
30045         ts.resizeTabs = this.config.resizeTabs === true;
30046         ts.minTabWidth = this.config.minTabWidth || 40;
30047         ts.maxTabWidth = this.config.maxTabWidth || 250;
30048         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30049         ts.monitorResize = false;
30050         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30051         ts.bodyEl.addClass('x-layout-tabs-body');
30052         this.panels.each(this.initPanelAsTab, this);
30053     },
30054
30055     initPanelAsTab : function(panel){
30056         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30057                     this.config.closeOnTab && panel.isClosable());
30058         if(panel.tabTip !== undefined){
30059             ti.setTooltip(panel.tabTip);
30060         }
30061         ti.on("activate", function(){
30062               this.setActivePanel(panel);
30063         }, this);
30064         if(this.config.closeOnTab){
30065             ti.on("beforeclose", function(t, e){
30066                 e.cancel = true;
30067                 this.remove(panel);
30068             }, this);
30069         }
30070         return ti;
30071     },
30072
30073     updatePanelTitle : function(panel, title){
30074         if(this.activePanel == panel){
30075             this.updateTitle(title);
30076         }
30077         if(this.tabs){
30078             var ti = this.tabs.getTab(panel.getEl().id);
30079             ti.setText(title);
30080             if(panel.tabTip !== undefined){
30081                 ti.setTooltip(panel.tabTip);
30082             }
30083         }
30084     },
30085
30086     updateTitle : function(title){
30087         if(this.titleTextEl && !this.config.title){
30088             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30089         }
30090     },
30091
30092     setActivePanel : function(panel){
30093         panel = this.getPanel(panel);
30094         if(this.activePanel && this.activePanel != panel){
30095             this.activePanel.setActiveState(false);
30096         }
30097         this.activePanel = panel;
30098         panel.setActiveState(true);
30099         if(this.panelSize){
30100             panel.setSize(this.panelSize.width, this.panelSize.height);
30101         }
30102         if(this.closeBtn){
30103             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30104         }
30105         this.updateTitle(panel.getTitle());
30106         if(this.tabs){
30107             this.fireEvent("invalidated", this);
30108         }
30109         this.fireEvent("panelactivated", this, panel);
30110     },
30111
30112     /**
30113      * Shows the specified panel.
30114      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30115      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30116      */
30117     showPanel : function(panel){
30118         if(panel = this.getPanel(panel)){
30119             if(this.tabs){
30120                 var tab = this.tabs.getTab(panel.getEl().id);
30121                 if(tab.isHidden()){
30122                     this.tabs.unhideTab(tab.id);
30123                 }
30124                 tab.activate();
30125             }else{
30126                 this.setActivePanel(panel);
30127             }
30128         }
30129         return panel;
30130     },
30131
30132     /**
30133      * Get the active panel for this region.
30134      * @return {Roo.ContentPanel} The active panel or null
30135      */
30136     getActivePanel : function(){
30137         return this.activePanel;
30138     },
30139
30140     validateVisibility : function(){
30141         if(this.panels.getCount() < 1){
30142             this.updateTitle("&#160;");
30143             this.closeBtn.hide();
30144             this.hide();
30145         }else{
30146             if(!this.isVisible()){
30147                 this.show();
30148             }
30149         }
30150     },
30151
30152     /**
30153      * Adds the passed ContentPanel(s) to this region.
30154      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30155      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30156      */
30157     add : function(panel){
30158         if(arguments.length > 1){
30159             for(var i = 0, len = arguments.length; i < len; i++) {
30160                 this.add(arguments[i]);
30161             }
30162             return null;
30163         }
30164         if(this.hasPanel(panel)){
30165             this.showPanel(panel);
30166             return panel;
30167         }
30168         panel.setRegion(this);
30169         this.panels.add(panel);
30170         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30171             this.bodyEl.dom.appendChild(panel.getEl().dom);
30172             if(panel.background !== true){
30173                 this.setActivePanel(panel);
30174             }
30175             this.fireEvent("paneladded", this, panel);
30176             return panel;
30177         }
30178         if(!this.tabs){
30179             this.initTabs();
30180         }else{
30181             this.initPanelAsTab(panel);
30182         }
30183         if(panel.background !== true){
30184             this.tabs.activate(panel.getEl().id);
30185         }
30186         this.fireEvent("paneladded", this, panel);
30187         return panel;
30188     },
30189
30190     /**
30191      * Hides the tab for the specified panel.
30192      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30193      */
30194     hidePanel : function(panel){
30195         if(this.tabs && (panel = this.getPanel(panel))){
30196             this.tabs.hideTab(panel.getEl().id);
30197         }
30198     },
30199
30200     /**
30201      * Unhides the tab for a previously hidden panel.
30202      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30203      */
30204     unhidePanel : function(panel){
30205         if(this.tabs && (panel = this.getPanel(panel))){
30206             this.tabs.unhideTab(panel.getEl().id);
30207         }
30208     },
30209
30210     clearPanels : function(){
30211         while(this.panels.getCount() > 0){
30212              this.remove(this.panels.first());
30213         }
30214     },
30215
30216     /**
30217      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30218      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30219      * @param {Boolean} preservePanel Overrides the config preservePanel option
30220      * @return {Roo.ContentPanel} The panel that was removed
30221      */
30222     remove : function(panel, preservePanel){
30223         panel = this.getPanel(panel);
30224         if(!panel){
30225             return null;
30226         }
30227         var e = {};
30228         this.fireEvent("beforeremove", this, panel, e);
30229         if(e.cancel === true){
30230             return null;
30231         }
30232         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30233         var panelId = panel.getId();
30234         this.panels.removeKey(panelId);
30235         if(preservePanel){
30236             document.body.appendChild(panel.getEl().dom);
30237         }
30238         if(this.tabs){
30239             this.tabs.removeTab(panel.getEl().id);
30240         }else if (!preservePanel){
30241             this.bodyEl.dom.removeChild(panel.getEl().dom);
30242         }
30243         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30244             var p = this.panels.first();
30245             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30246             tempEl.appendChild(p.getEl().dom);
30247             this.bodyEl.update("");
30248             this.bodyEl.dom.appendChild(p.getEl().dom);
30249             tempEl = null;
30250             this.updateTitle(p.getTitle());
30251             this.tabs = null;
30252             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30253             this.setActivePanel(p);
30254         }
30255         panel.setRegion(null);
30256         if(this.activePanel == panel){
30257             this.activePanel = null;
30258         }
30259         if(this.config.autoDestroy !== false && preservePanel !== true){
30260             try{panel.destroy();}catch(e){}
30261         }
30262         this.fireEvent("panelremoved", this, panel);
30263         return panel;
30264     },
30265
30266     /**
30267      * Returns the TabPanel component used by this region
30268      * @return {Roo.TabPanel}
30269      */
30270     getTabs : function(){
30271         return this.tabs;
30272     },
30273
30274     createTool : function(parentEl, className){
30275         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30276             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30277         btn.addClassOnOver("x-layout-tools-button-over");
30278         return btn;
30279     }
30280 });/*
30281  * Based on:
30282  * Ext JS Library 1.1.1
30283  * Copyright(c) 2006-2007, Ext JS, LLC.
30284  *
30285  * Originally Released Under LGPL - original licence link has changed is not relivant.
30286  *
30287  * Fork - LGPL
30288  * <script type="text/javascript">
30289  */
30290  
30291
30292
30293 /**
30294  * @class Roo.SplitLayoutRegion
30295  * @extends Roo.LayoutRegion
30296  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30297  */
30298 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30299     this.cursor = cursor;
30300     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30301 };
30302
30303 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30304     splitTip : "Drag to resize.",
30305     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30306     useSplitTips : false,
30307
30308     applyConfig : function(config){
30309         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30310         if(config.split){
30311             if(!this.split){
30312                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30313                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30314                 /** The SplitBar for this region 
30315                 * @type Roo.SplitBar */
30316                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30317                 this.split.on("moved", this.onSplitMove, this);
30318                 this.split.useShim = config.useShim === true;
30319                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30320                 if(this.useSplitTips){
30321                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30322                 }
30323                 if(config.collapsible){
30324                     this.split.el.on("dblclick", this.collapse,  this);
30325                 }
30326             }
30327             if(typeof config.minSize != "undefined"){
30328                 this.split.minSize = config.minSize;
30329             }
30330             if(typeof config.maxSize != "undefined"){
30331                 this.split.maxSize = config.maxSize;
30332             }
30333             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30334                 this.hideSplitter();
30335             }
30336         }
30337     },
30338
30339     getHMaxSize : function(){
30340          var cmax = this.config.maxSize || 10000;
30341          var center = this.mgr.getRegion("center");
30342          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30343     },
30344
30345     getVMaxSize : function(){
30346          var cmax = this.config.maxSize || 10000;
30347          var center = this.mgr.getRegion("center");
30348          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30349     },
30350
30351     onSplitMove : function(split, newSize){
30352         this.fireEvent("resized", this, newSize);
30353     },
30354     
30355     /** 
30356      * Returns the {@link Roo.SplitBar} for this region.
30357      * @return {Roo.SplitBar}
30358      */
30359     getSplitBar : function(){
30360         return this.split;
30361     },
30362     
30363     hide : function(){
30364         this.hideSplitter();
30365         Roo.SplitLayoutRegion.superclass.hide.call(this);
30366     },
30367
30368     hideSplitter : function(){
30369         if(this.split){
30370             this.split.el.setLocation(-2000,-2000);
30371             this.split.el.hide();
30372         }
30373     },
30374
30375     show : function(){
30376         if(this.split){
30377             this.split.el.show();
30378         }
30379         Roo.SplitLayoutRegion.superclass.show.call(this);
30380     },
30381     
30382     beforeSlide: function(){
30383         if(Roo.isGecko){// firefox overflow auto bug workaround
30384             this.bodyEl.clip();
30385             if(this.tabs) this.tabs.bodyEl.clip();
30386             if(this.activePanel){
30387                 this.activePanel.getEl().clip();
30388                 
30389                 if(this.activePanel.beforeSlide){
30390                     this.activePanel.beforeSlide();
30391                 }
30392             }
30393         }
30394     },
30395     
30396     afterSlide : function(){
30397         if(Roo.isGecko){// firefox overflow auto bug workaround
30398             this.bodyEl.unclip();
30399             if(this.tabs) this.tabs.bodyEl.unclip();
30400             if(this.activePanel){
30401                 this.activePanel.getEl().unclip();
30402                 if(this.activePanel.afterSlide){
30403                     this.activePanel.afterSlide();
30404                 }
30405             }
30406         }
30407     },
30408
30409     initAutoHide : function(){
30410         if(this.autoHide !== false){
30411             if(!this.autoHideHd){
30412                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30413                 this.autoHideHd = {
30414                     "mouseout": function(e){
30415                         if(!e.within(this.el, true)){
30416                             st.delay(500);
30417                         }
30418                     },
30419                     "mouseover" : function(e){
30420                         st.cancel();
30421                     },
30422                     scope : this
30423                 };
30424             }
30425             this.el.on(this.autoHideHd);
30426         }
30427     },
30428
30429     clearAutoHide : function(){
30430         if(this.autoHide !== false){
30431             this.el.un("mouseout", this.autoHideHd.mouseout);
30432             this.el.un("mouseover", this.autoHideHd.mouseover);
30433         }
30434     },
30435
30436     clearMonitor : function(){
30437         Roo.get(document).un("click", this.slideInIf, this);
30438     },
30439
30440     // these names are backwards but not changed for compat
30441     slideOut : function(){
30442         if(this.isSlid || this.el.hasActiveFx()){
30443             return;
30444         }
30445         this.isSlid = true;
30446         if(this.collapseBtn){
30447             this.collapseBtn.hide();
30448         }
30449         this.closeBtnState = this.closeBtn.getStyle('display');
30450         this.closeBtn.hide();
30451         if(this.stickBtn){
30452             this.stickBtn.show();
30453         }
30454         this.el.show();
30455         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30456         this.beforeSlide();
30457         this.el.setStyle("z-index", 10001);
30458         this.el.slideIn(this.getSlideAnchor(), {
30459             callback: function(){
30460                 this.afterSlide();
30461                 this.initAutoHide();
30462                 Roo.get(document).on("click", this.slideInIf, this);
30463                 this.fireEvent("slideshow", this);
30464             },
30465             scope: this,
30466             block: true
30467         });
30468     },
30469
30470     afterSlideIn : function(){
30471         this.clearAutoHide();
30472         this.isSlid = false;
30473         this.clearMonitor();
30474         this.el.setStyle("z-index", "");
30475         if(this.collapseBtn){
30476             this.collapseBtn.show();
30477         }
30478         this.closeBtn.setStyle('display', this.closeBtnState);
30479         if(this.stickBtn){
30480             this.stickBtn.hide();
30481         }
30482         this.fireEvent("slidehide", this);
30483     },
30484
30485     slideIn : function(cb){
30486         if(!this.isSlid || this.el.hasActiveFx()){
30487             Roo.callback(cb);
30488             return;
30489         }
30490         this.isSlid = false;
30491         this.beforeSlide();
30492         this.el.slideOut(this.getSlideAnchor(), {
30493             callback: function(){
30494                 this.el.setLeftTop(-10000, -10000);
30495                 this.afterSlide();
30496                 this.afterSlideIn();
30497                 Roo.callback(cb);
30498             },
30499             scope: this,
30500             block: true
30501         });
30502     },
30503     
30504     slideInIf : function(e){
30505         if(!e.within(this.el)){
30506             this.slideIn();
30507         }
30508     },
30509
30510     animateCollapse : function(){
30511         this.beforeSlide();
30512         this.el.setStyle("z-index", 20000);
30513         var anchor = this.getSlideAnchor();
30514         this.el.slideOut(anchor, {
30515             callback : function(){
30516                 this.el.setStyle("z-index", "");
30517                 this.collapsedEl.slideIn(anchor, {duration:.3});
30518                 this.afterSlide();
30519                 this.el.setLocation(-10000,-10000);
30520                 this.el.hide();
30521                 this.fireEvent("collapsed", this);
30522             },
30523             scope: this,
30524             block: true
30525         });
30526     },
30527
30528     animateExpand : function(){
30529         this.beforeSlide();
30530         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30531         this.el.setStyle("z-index", 20000);
30532         this.collapsedEl.hide({
30533             duration:.1
30534         });
30535         this.el.slideIn(this.getSlideAnchor(), {
30536             callback : function(){
30537                 this.el.setStyle("z-index", "");
30538                 this.afterSlide();
30539                 if(this.split){
30540                     this.split.el.show();
30541                 }
30542                 this.fireEvent("invalidated", this);
30543                 this.fireEvent("expanded", this);
30544             },
30545             scope: this,
30546             block: true
30547         });
30548     },
30549
30550     anchors : {
30551         "west" : "left",
30552         "east" : "right",
30553         "north" : "top",
30554         "south" : "bottom"
30555     },
30556
30557     sanchors : {
30558         "west" : "l",
30559         "east" : "r",
30560         "north" : "t",
30561         "south" : "b"
30562     },
30563
30564     canchors : {
30565         "west" : "tl-tr",
30566         "east" : "tr-tl",
30567         "north" : "tl-bl",
30568         "south" : "bl-tl"
30569     },
30570
30571     getAnchor : function(){
30572         return this.anchors[this.position];
30573     },
30574
30575     getCollapseAnchor : function(){
30576         return this.canchors[this.position];
30577     },
30578
30579     getSlideAnchor : function(){
30580         return this.sanchors[this.position];
30581     },
30582
30583     getAlignAdj : function(){
30584         var cm = this.cmargins;
30585         switch(this.position){
30586             case "west":
30587                 return [0, 0];
30588             break;
30589             case "east":
30590                 return [0, 0];
30591             break;
30592             case "north":
30593                 return [0, 0];
30594             break;
30595             case "south":
30596                 return [0, 0];
30597             break;
30598         }
30599     },
30600
30601     getExpandAdj : function(){
30602         var c = this.collapsedEl, cm = this.cmargins;
30603         switch(this.position){
30604             case "west":
30605                 return [-(cm.right+c.getWidth()+cm.left), 0];
30606             break;
30607             case "east":
30608                 return [cm.right+c.getWidth()+cm.left, 0];
30609             break;
30610             case "north":
30611                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30612             break;
30613             case "south":
30614                 return [0, cm.top+cm.bottom+c.getHeight()];
30615             break;
30616         }
30617     }
30618 });/*
30619  * Based on:
30620  * Ext JS Library 1.1.1
30621  * Copyright(c) 2006-2007, Ext JS, LLC.
30622  *
30623  * Originally Released Under LGPL - original licence link has changed is not relivant.
30624  *
30625  * Fork - LGPL
30626  * <script type="text/javascript">
30627  */
30628 /*
30629  * These classes are private internal classes
30630  */
30631 Roo.CenterLayoutRegion = function(mgr, config){
30632     Roo.LayoutRegion.call(this, mgr, config, "center");
30633     this.visible = true;
30634     this.minWidth = config.minWidth || 20;
30635     this.minHeight = config.minHeight || 20;
30636 };
30637
30638 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30639     hide : function(){
30640         // center panel can't be hidden
30641     },
30642     
30643     show : function(){
30644         // center panel can't be hidden
30645     },
30646     
30647     getMinWidth: function(){
30648         return this.minWidth;
30649     },
30650     
30651     getMinHeight: function(){
30652         return this.minHeight;
30653     }
30654 });
30655
30656
30657 Roo.NorthLayoutRegion = function(mgr, config){
30658     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30659     if(this.split){
30660         this.split.placement = Roo.SplitBar.TOP;
30661         this.split.orientation = Roo.SplitBar.VERTICAL;
30662         this.split.el.addClass("x-layout-split-v");
30663     }
30664     var size = config.initialSize || config.height;
30665     if(typeof size != "undefined"){
30666         this.el.setHeight(size);
30667     }
30668 };
30669 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30670     orientation: Roo.SplitBar.VERTICAL,
30671     getBox : function(){
30672         if(this.collapsed){
30673             return this.collapsedEl.getBox();
30674         }
30675         var box = this.el.getBox();
30676         if(this.split){
30677             box.height += this.split.el.getHeight();
30678         }
30679         return box;
30680     },
30681     
30682     updateBox : function(box){
30683         if(this.split && !this.collapsed){
30684             box.height -= this.split.el.getHeight();
30685             this.split.el.setLeft(box.x);
30686             this.split.el.setTop(box.y+box.height);
30687             this.split.el.setWidth(box.width);
30688         }
30689         if(this.collapsed){
30690             this.updateBody(box.width, null);
30691         }
30692         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30693     }
30694 });
30695
30696 Roo.SouthLayoutRegion = function(mgr, config){
30697     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30698     if(this.split){
30699         this.split.placement = Roo.SplitBar.BOTTOM;
30700         this.split.orientation = Roo.SplitBar.VERTICAL;
30701         this.split.el.addClass("x-layout-split-v");
30702     }
30703     var size = config.initialSize || config.height;
30704     if(typeof size != "undefined"){
30705         this.el.setHeight(size);
30706     }
30707 };
30708 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30709     orientation: Roo.SplitBar.VERTICAL,
30710     getBox : function(){
30711         if(this.collapsed){
30712             return this.collapsedEl.getBox();
30713         }
30714         var box = this.el.getBox();
30715         if(this.split){
30716             var sh = this.split.el.getHeight();
30717             box.height += sh;
30718             box.y -= sh;
30719         }
30720         return box;
30721     },
30722     
30723     updateBox : function(box){
30724         if(this.split && !this.collapsed){
30725             var sh = this.split.el.getHeight();
30726             box.height -= sh;
30727             box.y += sh;
30728             this.split.el.setLeft(box.x);
30729             this.split.el.setTop(box.y-sh);
30730             this.split.el.setWidth(box.width);
30731         }
30732         if(this.collapsed){
30733             this.updateBody(box.width, null);
30734         }
30735         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30736     }
30737 });
30738
30739 Roo.EastLayoutRegion = function(mgr, config){
30740     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30741     if(this.split){
30742         this.split.placement = Roo.SplitBar.RIGHT;
30743         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30744         this.split.el.addClass("x-layout-split-h");
30745     }
30746     var size = config.initialSize || config.width;
30747     if(typeof size != "undefined"){
30748         this.el.setWidth(size);
30749     }
30750 };
30751 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30752     orientation: Roo.SplitBar.HORIZONTAL,
30753     getBox : function(){
30754         if(this.collapsed){
30755             return this.collapsedEl.getBox();
30756         }
30757         var box = this.el.getBox();
30758         if(this.split){
30759             var sw = this.split.el.getWidth();
30760             box.width += sw;
30761             box.x -= sw;
30762         }
30763         return box;
30764     },
30765
30766     updateBox : function(box){
30767         if(this.split && !this.collapsed){
30768             var sw = this.split.el.getWidth();
30769             box.width -= sw;
30770             this.split.el.setLeft(box.x);
30771             this.split.el.setTop(box.y);
30772             this.split.el.setHeight(box.height);
30773             box.x += sw;
30774         }
30775         if(this.collapsed){
30776             this.updateBody(null, box.height);
30777         }
30778         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30779     }
30780 });
30781
30782 Roo.WestLayoutRegion = function(mgr, config){
30783     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30784     if(this.split){
30785         this.split.placement = Roo.SplitBar.LEFT;
30786         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30787         this.split.el.addClass("x-layout-split-h");
30788     }
30789     var size = config.initialSize || config.width;
30790     if(typeof size != "undefined"){
30791         this.el.setWidth(size);
30792     }
30793 };
30794 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30795     orientation: Roo.SplitBar.HORIZONTAL,
30796     getBox : function(){
30797         if(this.collapsed){
30798             return this.collapsedEl.getBox();
30799         }
30800         var box = this.el.getBox();
30801         if(this.split){
30802             box.width += this.split.el.getWidth();
30803         }
30804         return box;
30805     },
30806     
30807     updateBox : function(box){
30808         if(this.split && !this.collapsed){
30809             var sw = this.split.el.getWidth();
30810             box.width -= sw;
30811             this.split.el.setLeft(box.x+box.width);
30812             this.split.el.setTop(box.y);
30813             this.split.el.setHeight(box.height);
30814         }
30815         if(this.collapsed){
30816             this.updateBody(null, box.height);
30817         }
30818         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30819     }
30820 });
30821 /*
30822  * Based on:
30823  * Ext JS Library 1.1.1
30824  * Copyright(c) 2006-2007, Ext JS, LLC.
30825  *
30826  * Originally Released Under LGPL - original licence link has changed is not relivant.
30827  *
30828  * Fork - LGPL
30829  * <script type="text/javascript">
30830  */
30831  
30832  
30833 /*
30834  * Private internal class for reading and applying state
30835  */
30836 Roo.LayoutStateManager = function(layout){
30837      // default empty state
30838      this.state = {
30839         north: {},
30840         south: {},
30841         east: {},
30842         west: {}       
30843     };
30844 };
30845
30846 Roo.LayoutStateManager.prototype = {
30847     init : function(layout, provider){
30848         this.provider = provider;
30849         var state = provider.get(layout.id+"-layout-state");
30850         if(state){
30851             var wasUpdating = layout.isUpdating();
30852             if(!wasUpdating){
30853                 layout.beginUpdate();
30854             }
30855             for(var key in state){
30856                 if(typeof state[key] != "function"){
30857                     var rstate = state[key];
30858                     var r = layout.getRegion(key);
30859                     if(r && rstate){
30860                         if(rstate.size){
30861                             r.resizeTo(rstate.size);
30862                         }
30863                         if(rstate.collapsed == true){
30864                             r.collapse(true);
30865                         }else{
30866                             r.expand(null, true);
30867                         }
30868                     }
30869                 }
30870             }
30871             if(!wasUpdating){
30872                 layout.endUpdate();
30873             }
30874             this.state = state; 
30875         }
30876         this.layout = layout;
30877         layout.on("regionresized", this.onRegionResized, this);
30878         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30879         layout.on("regionexpanded", this.onRegionExpanded, this);
30880     },
30881     
30882     storeState : function(){
30883         this.provider.set(this.layout.id+"-layout-state", this.state);
30884     },
30885     
30886     onRegionResized : function(region, newSize){
30887         this.state[region.getPosition()].size = newSize;
30888         this.storeState();
30889     },
30890     
30891     onRegionCollapsed : function(region){
30892         this.state[region.getPosition()].collapsed = true;
30893         this.storeState();
30894     },
30895     
30896     onRegionExpanded : function(region){
30897         this.state[region.getPosition()].collapsed = false;
30898         this.storeState();
30899     }
30900 };/*
30901  * Based on:
30902  * Ext JS Library 1.1.1
30903  * Copyright(c) 2006-2007, Ext JS, LLC.
30904  *
30905  * Originally Released Under LGPL - original licence link has changed is not relivant.
30906  *
30907  * Fork - LGPL
30908  * <script type="text/javascript">
30909  */
30910 /**
30911  * @class Roo.ContentPanel
30912  * @extends Roo.util.Observable
30913  * A basic ContentPanel element.
30914  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30915  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30916  * @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
30917  * @cfg {Boolean} closable True if the panel can be closed/removed
30918  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30919  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30920  * @cfg {Toolbar} toolbar A toolbar for this panel
30921  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30922  * @cfg {String} title The title for this panel
30923  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30924  * @cfg {String} url Calls {@link #setUrl} with this value
30925  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30926  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30927  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30928  * @constructor
30929  * Create a new ContentPanel.
30930  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30931  * @param {String/Object} config A string to set only the title or a config object
30932  * @param {String} content (optional) Set the HTML content for this panel
30933  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30934  */
30935 Roo.ContentPanel = function(el, config, content){
30936     
30937      
30938     /*
30939     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30940         config = el;
30941         el = Roo.id();
30942     }
30943     if (config && config.parentLayout) { 
30944         el = config.parentLayout.el.createChild(); 
30945     }
30946     */
30947     if(el.autoCreate){ // xtype is available if this is called from factory
30948         config = el;
30949         el = Roo.id();
30950     }
30951     this.el = Roo.get(el);
30952     if(!this.el && config && config.autoCreate){
30953         if(typeof config.autoCreate == "object"){
30954             if(!config.autoCreate.id){
30955                 config.autoCreate.id = config.id||el;
30956             }
30957             this.el = Roo.DomHelper.append(document.body,
30958                         config.autoCreate, true);
30959         }else{
30960             this.el = Roo.DomHelper.append(document.body,
30961                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30962         }
30963     }
30964     this.closable = false;
30965     this.loaded = false;
30966     this.active = false;
30967     if(typeof config == "string"){
30968         this.title = config;
30969     }else{
30970         Roo.apply(this, config);
30971     }
30972     
30973     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30974         this.wrapEl = this.el.wrap();    
30975         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30976         
30977     }
30978     
30979     
30980     
30981     if(this.resizeEl){
30982         this.resizeEl = Roo.get(this.resizeEl, true);
30983     }else{
30984         this.resizeEl = this.el;
30985     }
30986     this.addEvents({
30987         /**
30988          * @event activate
30989          * Fires when this panel is activated. 
30990          * @param {Roo.ContentPanel} this
30991          */
30992         "activate" : true,
30993         /**
30994          * @event deactivate
30995          * Fires when this panel is activated. 
30996          * @param {Roo.ContentPanel} this
30997          */
30998         "deactivate" : true,
30999
31000         /**
31001          * @event resize
31002          * Fires when this panel is resized if fitToFrame is true.
31003          * @param {Roo.ContentPanel} this
31004          * @param {Number} width The width after any component adjustments
31005          * @param {Number} height The height after any component adjustments
31006          */
31007         "resize" : true
31008     });
31009     if(this.autoScroll){
31010         this.resizeEl.setStyle("overflow", "auto");
31011     } else {
31012         // fix randome scrolling
31013         this.el.on('scroll', function() {
31014             this.scrollTo('top',0); 
31015         });
31016     }
31017     content = content || this.content;
31018     if(content){
31019         this.setContent(content);
31020     }
31021     if(config && config.url){
31022         this.setUrl(this.url, this.params, this.loadOnce);
31023     }
31024     
31025     
31026     
31027     Roo.ContentPanel.superclass.constructor.call(this);
31028 };
31029
31030 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31031     tabTip:'',
31032     setRegion : function(region){
31033         this.region = region;
31034         if(region){
31035            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31036         }else{
31037            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31038         } 
31039     },
31040     
31041     /**
31042      * Returns the toolbar for this Panel if one was configured. 
31043      * @return {Roo.Toolbar} 
31044      */
31045     getToolbar : function(){
31046         return this.toolbar;
31047     },
31048     
31049     setActiveState : function(active){
31050         this.active = active;
31051         if(!active){
31052             this.fireEvent("deactivate", this);
31053         }else{
31054             this.fireEvent("activate", this);
31055         }
31056     },
31057     /**
31058      * Updates this panel's element
31059      * @param {String} content The new content
31060      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31061     */
31062     setContent : function(content, loadScripts){
31063         this.el.update(content, loadScripts);
31064     },
31065
31066     ignoreResize : function(w, h){
31067         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31068             return true;
31069         }else{
31070             this.lastSize = {width: w, height: h};
31071             return false;
31072         }
31073     },
31074     /**
31075      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31076      * @return {Roo.UpdateManager} The UpdateManager
31077      */
31078     getUpdateManager : function(){
31079         return this.el.getUpdateManager();
31080     },
31081      /**
31082      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31083      * @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:
31084 <pre><code>
31085 panel.load({
31086     url: "your-url.php",
31087     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31088     callback: yourFunction,
31089     scope: yourObject, //(optional scope)
31090     discardUrl: false,
31091     nocache: false,
31092     text: "Loading...",
31093     timeout: 30,
31094     scripts: false
31095 });
31096 </code></pre>
31097      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31098      * 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.
31099      * @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}
31100      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31101      * @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.
31102      * @return {Roo.ContentPanel} this
31103      */
31104     load : function(){
31105         var um = this.el.getUpdateManager();
31106         um.update.apply(um, arguments);
31107         return this;
31108     },
31109
31110
31111     /**
31112      * 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.
31113      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31114      * @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)
31115      * @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)
31116      * @return {Roo.UpdateManager} The UpdateManager
31117      */
31118     setUrl : function(url, params, loadOnce){
31119         if(this.refreshDelegate){
31120             this.removeListener("activate", this.refreshDelegate);
31121         }
31122         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31123         this.on("activate", this.refreshDelegate);
31124         return this.el.getUpdateManager();
31125     },
31126     
31127     _handleRefresh : function(url, params, loadOnce){
31128         if(!loadOnce || !this.loaded){
31129             var updater = this.el.getUpdateManager();
31130             updater.update(url, params, this._setLoaded.createDelegate(this));
31131         }
31132     },
31133     
31134     _setLoaded : function(){
31135         this.loaded = true;
31136     }, 
31137     
31138     /**
31139      * Returns this panel's id
31140      * @return {String} 
31141      */
31142     getId : function(){
31143         return this.el.id;
31144     },
31145     
31146     /** 
31147      * Returns this panel's element - used by regiosn to add.
31148      * @return {Roo.Element} 
31149      */
31150     getEl : function(){
31151         return this.wrapEl || this.el;
31152     },
31153     
31154     adjustForComponents : function(width, height){
31155         if(this.resizeEl != this.el){
31156             width -= this.el.getFrameWidth('lr');
31157             height -= this.el.getFrameWidth('tb');
31158         }
31159         if(this.toolbar){
31160             var te = this.toolbar.getEl();
31161             height -= te.getHeight();
31162             te.setWidth(width);
31163         }
31164         if(this.adjustments){
31165             width += this.adjustments[0];
31166             height += this.adjustments[1];
31167         }
31168         return {"width": width, "height": height};
31169     },
31170     
31171     setSize : function(width, height){
31172         if(this.fitToFrame && !this.ignoreResize(width, height)){
31173             if(this.fitContainer && this.resizeEl != this.el){
31174                 this.el.setSize(width, height);
31175             }
31176             var size = this.adjustForComponents(width, height);
31177             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31178             this.fireEvent('resize', this, size.width, size.height);
31179         }
31180     },
31181     
31182     /**
31183      * Returns this panel's title
31184      * @return {String} 
31185      */
31186     getTitle : function(){
31187         return this.title;
31188     },
31189     
31190     /**
31191      * Set this panel's title
31192      * @param {String} title
31193      */
31194     setTitle : function(title){
31195         this.title = title;
31196         if(this.region){
31197             this.region.updatePanelTitle(this, title);
31198         }
31199     },
31200     
31201     /**
31202      * Returns true is this panel was configured to be closable
31203      * @return {Boolean} 
31204      */
31205     isClosable : function(){
31206         return this.closable;
31207     },
31208     
31209     beforeSlide : function(){
31210         this.el.clip();
31211         this.resizeEl.clip();
31212     },
31213     
31214     afterSlide : function(){
31215         this.el.unclip();
31216         this.resizeEl.unclip();
31217     },
31218     
31219     /**
31220      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31221      *   Will fail silently if the {@link #setUrl} method has not been called.
31222      *   This does not activate the panel, just updates its content.
31223      */
31224     refresh : function(){
31225         if(this.refreshDelegate){
31226            this.loaded = false;
31227            this.refreshDelegate();
31228         }
31229     },
31230     
31231     /**
31232      * Destroys this panel
31233      */
31234     destroy : function(){
31235         this.el.removeAllListeners();
31236         var tempEl = document.createElement("span");
31237         tempEl.appendChild(this.el.dom);
31238         tempEl.innerHTML = "";
31239         this.el.remove();
31240         this.el = null;
31241     },
31242     
31243       /**
31244      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31245      * <pre><code>
31246
31247 layout.addxtype({
31248        xtype : 'Form',
31249        items: [ .... ]
31250    }
31251 );
31252
31253 </code></pre>
31254      * @param {Object} cfg Xtype definition of item to add.
31255      */
31256     
31257     addxtype : function(cfg) {
31258         // add form..
31259         if (cfg.xtype.match(/^Form$/)) {
31260             var el = this.el.createChild();
31261
31262             this.form = new  Roo.form.Form(cfg);
31263             
31264             
31265             if ( this.form.allItems.length) this.form.render(el.dom);
31266             return this.form;
31267         }
31268         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31269             // views..
31270             cfg.el = this.el.appendChild(document.createElement("div"));
31271             // factory?
31272             var ret = new Roo[cfg.xtype](cfg);
31273             ret.render(false, ''); // render blank..
31274             return ret;
31275             
31276         }
31277         return false;
31278         
31279     }
31280 });
31281
31282 /**
31283  * @class Roo.GridPanel
31284  * @extends Roo.ContentPanel
31285  * @constructor
31286  * Create a new GridPanel.
31287  * @param {Roo.grid.Grid} grid The grid for this panel
31288  * @param {String/Object} config A string to set only the panel's title, or a config object
31289  */
31290 Roo.GridPanel = function(grid, config){
31291     
31292   
31293     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31294         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31295         
31296     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31297     
31298     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31299     
31300     if(this.toolbar){
31301         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31302     }
31303     // xtype created footer. - not sure if will work as we normally have to render first..
31304     if (this.footer && !this.footer.el && this.footer.xtype) {
31305         
31306         this.footer.container = this.grid.getView().getFooterPanel(true);
31307         this.footer.dataSource = this.grid.dataSource;
31308         this.footer = Roo.factory(this.footer, Roo);
31309         
31310     }
31311     
31312     grid.monitorWindowResize = false; // turn off autosizing
31313     grid.autoHeight = false;
31314     grid.autoWidth = false;
31315     this.grid = grid;
31316     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31317 };
31318
31319 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31320     getId : function(){
31321         return this.grid.id;
31322     },
31323     
31324     /**
31325      * Returns the grid for this panel
31326      * @return {Roo.grid.Grid} 
31327      */
31328     getGrid : function(){
31329         return this.grid;    
31330     },
31331     
31332     setSize : function(width, height){
31333         if(!this.ignoreResize(width, height)){
31334             var grid = this.grid;
31335             var size = this.adjustForComponents(width, height);
31336             grid.getGridEl().setSize(size.width, size.height);
31337             grid.autoSize();
31338         }
31339     },
31340     
31341     beforeSlide : function(){
31342         this.grid.getView().scroller.clip();
31343     },
31344     
31345     afterSlide : function(){
31346         this.grid.getView().scroller.unclip();
31347     },
31348     
31349     destroy : function(){
31350         this.grid.destroy();
31351         delete this.grid;
31352         Roo.GridPanel.superclass.destroy.call(this); 
31353     }
31354 });
31355
31356
31357 /**
31358  * @class Roo.NestedLayoutPanel
31359  * @extends Roo.ContentPanel
31360  * @constructor
31361  * Create a new NestedLayoutPanel.
31362  * 
31363  * 
31364  * @param {Roo.BorderLayout} layout The layout for this panel
31365  * @param {String/Object} config A string to set only the title or a config object
31366  */
31367 Roo.NestedLayoutPanel = function(layout, config)
31368 {
31369     // construct with only one argument..
31370     /* FIXME - implement nicer consturctors
31371     if (layout.layout) {
31372         config = layout;
31373         layout = config.layout;
31374         delete config.layout;
31375     }
31376     if (layout.xtype && !layout.getEl) {
31377         // then layout needs constructing..
31378         layout = Roo.factory(layout, Roo);
31379     }
31380     */
31381     
31382     
31383     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31384     
31385     layout.monitorWindowResize = false; // turn off autosizing
31386     this.layout = layout;
31387     this.layout.getEl().addClass("x-layout-nested-layout");
31388     
31389     
31390     
31391     
31392 };
31393
31394 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31395
31396     setSize : function(width, height){
31397         if(!this.ignoreResize(width, height)){
31398             var size = this.adjustForComponents(width, height);
31399             var el = this.layout.getEl();
31400             el.setSize(size.width, size.height);
31401             var touch = el.dom.offsetWidth;
31402             this.layout.layout();
31403             // ie requires a double layout on the first pass
31404             if(Roo.isIE && !this.initialized){
31405                 this.initialized = true;
31406                 this.layout.layout();
31407             }
31408         }
31409     },
31410     
31411     // activate all subpanels if not currently active..
31412     
31413     setActiveState : function(active){
31414         this.active = active;
31415         if(!active){
31416             this.fireEvent("deactivate", this);
31417             return;
31418         }
31419         
31420         this.fireEvent("activate", this);
31421         // not sure if this should happen before or after..
31422         if (!this.layout) {
31423             return; // should not happen..
31424         }
31425         var reg = false;
31426         for (var r in this.layout.regions) {
31427             reg = this.layout.getRegion(r);
31428             if (reg.getActivePanel()) {
31429                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31430                 reg.setActivePanel(reg.getActivePanel());
31431                 continue;
31432             }
31433             if (!reg.panels.length) {
31434                 continue;
31435             }
31436             reg.showPanel(reg.getPanel(0));
31437         }
31438         
31439         
31440         
31441         
31442     },
31443     
31444     /**
31445      * Returns the nested BorderLayout for this panel
31446      * @return {Roo.BorderLayout} 
31447      */
31448     getLayout : function(){
31449         return this.layout;
31450     },
31451     
31452      /**
31453      * Adds a xtype elements to the layout of the nested panel
31454      * <pre><code>
31455
31456 panel.addxtype({
31457        xtype : 'ContentPanel',
31458        region: 'west',
31459        items: [ .... ]
31460    }
31461 );
31462
31463 panel.addxtype({
31464         xtype : 'NestedLayoutPanel',
31465         region: 'west',
31466         layout: {
31467            center: { },
31468            west: { }   
31469         },
31470         items : [ ... list of content panels or nested layout panels.. ]
31471    }
31472 );
31473 </code></pre>
31474      * @param {Object} cfg Xtype definition of item to add.
31475      */
31476     addxtype : function(cfg) {
31477         return this.layout.addxtype(cfg);
31478     
31479     }
31480 });
31481
31482 Roo.ScrollPanel = function(el, config, content){
31483     config = config || {};
31484     config.fitToFrame = true;
31485     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31486     
31487     this.el.dom.style.overflow = "hidden";
31488     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31489     this.el.removeClass("x-layout-inactive-content");
31490     this.el.on("mousewheel", this.onWheel, this);
31491
31492     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31493     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31494     up.unselectable(); down.unselectable();
31495     up.on("click", this.scrollUp, this);
31496     down.on("click", this.scrollDown, this);
31497     up.addClassOnOver("x-scroller-btn-over");
31498     down.addClassOnOver("x-scroller-btn-over");
31499     up.addClassOnClick("x-scroller-btn-click");
31500     down.addClassOnClick("x-scroller-btn-click");
31501     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31502
31503     this.resizeEl = this.el;
31504     this.el = wrap; this.up = up; this.down = down;
31505 };
31506
31507 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31508     increment : 100,
31509     wheelIncrement : 5,
31510     scrollUp : function(){
31511         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31512     },
31513
31514     scrollDown : function(){
31515         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31516     },
31517
31518     afterScroll : function(){
31519         var el = this.resizeEl;
31520         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31521         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31522         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31523     },
31524
31525     setSize : function(){
31526         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31527         this.afterScroll();
31528     },
31529
31530     onWheel : function(e){
31531         var d = e.getWheelDelta();
31532         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31533         this.afterScroll();
31534         e.stopEvent();
31535     },
31536
31537     setContent : function(content, loadScripts){
31538         this.resizeEl.update(content, loadScripts);
31539     }
31540
31541 });
31542
31543
31544
31545
31546
31547
31548
31549
31550
31551 /**
31552  * @class Roo.TreePanel
31553  * @extends Roo.ContentPanel
31554  * @constructor
31555  * Create a new TreePanel. - defaults to fit/scoll contents.
31556  * @param {String/Object} config A string to set only the panel's title, or a config object
31557  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31558  */
31559 Roo.TreePanel = function(config){
31560     var el = config.el;
31561     var tree = config.tree;
31562     delete config.tree; 
31563     delete config.el; // hopefull!
31564     
31565     // wrapper for IE7 strict & safari scroll issue
31566     
31567     var treeEl = el.createChild();
31568     config.resizeEl = treeEl;
31569     
31570     
31571     
31572     Roo.TreePanel.superclass.constructor.call(this, el, config);
31573  
31574  
31575     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31576     //console.log(tree);
31577     this.on('activate', function()
31578     {
31579         if (this.tree.rendered) {
31580             return;
31581         }
31582         //console.log('render tree');
31583         this.tree.render();
31584     });
31585     
31586     this.on('resize',  function (cp, w, h) {
31587             this.tree.innerCt.setWidth(w);
31588             this.tree.innerCt.setHeight(h);
31589             this.tree.innerCt.setStyle('overflow-y', 'auto');
31590     });
31591
31592         
31593     
31594 };
31595
31596 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31597     fitToFrame : true,
31598     autoScroll : true
31599 });
31600
31601
31602
31603
31604
31605
31606
31607
31608
31609
31610
31611 /*
31612  * Based on:
31613  * Ext JS Library 1.1.1
31614  * Copyright(c) 2006-2007, Ext JS, LLC.
31615  *
31616  * Originally Released Under LGPL - original licence link has changed is not relivant.
31617  *
31618  * Fork - LGPL
31619  * <script type="text/javascript">
31620  */
31621  
31622
31623 /**
31624  * @class Roo.ReaderLayout
31625  * @extends Roo.BorderLayout
31626  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31627  * center region containing two nested regions (a top one for a list view and one for item preview below),
31628  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31629  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31630  * expedites the setup of the overall layout and regions for this common application style.
31631  * Example:
31632  <pre><code>
31633 var reader = new Roo.ReaderLayout();
31634 var CP = Roo.ContentPanel;  // shortcut for adding
31635
31636 reader.beginUpdate();
31637 reader.add("north", new CP("north", "North"));
31638 reader.add("west", new CP("west", {title: "West"}));
31639 reader.add("east", new CP("east", {title: "East"}));
31640
31641 reader.regions.listView.add(new CP("listView", "List"));
31642 reader.regions.preview.add(new CP("preview", "Preview"));
31643 reader.endUpdate();
31644 </code></pre>
31645 * @constructor
31646 * Create a new ReaderLayout
31647 * @param {Object} config Configuration options
31648 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31649 * document.body if omitted)
31650 */
31651 Roo.ReaderLayout = function(config, renderTo){
31652     var c = config || {size:{}};
31653     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31654         north: c.north !== false ? Roo.apply({
31655             split:false,
31656             initialSize: 32,
31657             titlebar: false
31658         }, c.north) : false,
31659         west: c.west !== false ? Roo.apply({
31660             split:true,
31661             initialSize: 200,
31662             minSize: 175,
31663             maxSize: 400,
31664             titlebar: true,
31665             collapsible: true,
31666             animate: true,
31667             margins:{left:5,right:0,bottom:5,top:5},
31668             cmargins:{left:5,right:5,bottom:5,top:5}
31669         }, c.west) : false,
31670         east: c.east !== false ? Roo.apply({
31671             split:true,
31672             initialSize: 200,
31673             minSize: 175,
31674             maxSize: 400,
31675             titlebar: true,
31676             collapsible: true,
31677             animate: true,
31678             margins:{left:0,right:5,bottom:5,top:5},
31679             cmargins:{left:5,right:5,bottom:5,top:5}
31680         }, c.east) : false,
31681         center: Roo.apply({
31682             tabPosition: 'top',
31683             autoScroll:false,
31684             closeOnTab: true,
31685             titlebar:false,
31686             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31687         }, c.center)
31688     });
31689
31690     this.el.addClass('x-reader');
31691
31692     this.beginUpdate();
31693
31694     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31695         south: c.preview !== false ? Roo.apply({
31696             split:true,
31697             initialSize: 200,
31698             minSize: 100,
31699             autoScroll:true,
31700             collapsible:true,
31701             titlebar: true,
31702             cmargins:{top:5,left:0, right:0, bottom:0}
31703         }, c.preview) : false,
31704         center: Roo.apply({
31705             autoScroll:false,
31706             titlebar:false,
31707             minHeight:200
31708         }, c.listView)
31709     });
31710     this.add('center', new Roo.NestedLayoutPanel(inner,
31711             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31712
31713     this.endUpdate();
31714
31715     this.regions.preview = inner.getRegion('south');
31716     this.regions.listView = inner.getRegion('center');
31717 };
31718
31719 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31720  * Based on:
31721  * Ext JS Library 1.1.1
31722  * Copyright(c) 2006-2007, Ext JS, LLC.
31723  *
31724  * Originally Released Under LGPL - original licence link has changed is not relivant.
31725  *
31726  * Fork - LGPL
31727  * <script type="text/javascript">
31728  */
31729  
31730 /**
31731  * @class Roo.grid.Grid
31732  * @extends Roo.util.Observable
31733  * This class represents the primary interface of a component based grid control.
31734  * <br><br>Usage:<pre><code>
31735  var grid = new Roo.grid.Grid("my-container-id", {
31736      ds: myDataStore,
31737      cm: myColModel,
31738      selModel: mySelectionModel,
31739      autoSizeColumns: true,
31740      monitorWindowResize: false,
31741      trackMouseOver: true
31742  });
31743  // set any options
31744  grid.render();
31745  * </code></pre>
31746  * <b>Common Problems:</b><br/>
31747  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31748  * element will correct this<br/>
31749  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31750  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31751  * are unpredictable.<br/>
31752  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31753  * grid to calculate dimensions/offsets.<br/>
31754   * @constructor
31755  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31756  * The container MUST have some type of size defined for the grid to fill. The container will be
31757  * automatically set to position relative if it isn't already.
31758  * @param {Object} config A config object that sets properties on this grid.
31759  */
31760 Roo.grid.Grid = function(container, config){
31761         // initialize the container
31762         this.container = Roo.get(container);
31763         this.container.update("");
31764         this.container.setStyle("overflow", "hidden");
31765     this.container.addClass('x-grid-container');
31766
31767     this.id = this.container.id;
31768
31769     Roo.apply(this, config);
31770     // check and correct shorthanded configs
31771     if(this.ds){
31772         this.dataSource = this.ds;
31773         delete this.ds;
31774     }
31775     if(this.cm){
31776         this.colModel = this.cm;
31777         delete this.cm;
31778     }
31779     if(this.sm){
31780         this.selModel = this.sm;
31781         delete this.sm;
31782     }
31783
31784     if (this.selModel) {
31785         this.selModel = Roo.factory(this.selModel, Roo.grid);
31786         this.sm = this.selModel;
31787         this.sm.xmodule = this.xmodule || false;
31788     }
31789     if (typeof(this.colModel.config) == 'undefined') {
31790         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31791         this.cm = this.colModel;
31792         this.cm.xmodule = this.xmodule || false;
31793     }
31794     if (this.dataSource) {
31795         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31796         this.ds = this.dataSource;
31797         this.ds.xmodule = this.xmodule || false;
31798         
31799     }
31800     
31801     
31802     
31803     if(this.width){
31804         this.container.setWidth(this.width);
31805     }
31806
31807     if(this.height){
31808         this.container.setHeight(this.height);
31809     }
31810     /** @private */
31811         this.addEvents({
31812             // raw events
31813             /**
31814              * @event click
31815              * The raw click event for the entire grid.
31816              * @param {Roo.EventObject} e
31817              */
31818             "click" : true,
31819             /**
31820              * @event dblclick
31821              * The raw dblclick event for the entire grid.
31822              * @param {Roo.EventObject} e
31823              */
31824             "dblclick" : true,
31825             /**
31826              * @event contextmenu
31827              * The raw contextmenu event for the entire grid.
31828              * @param {Roo.EventObject} e
31829              */
31830             "contextmenu" : true,
31831             /**
31832              * @event mousedown
31833              * The raw mousedown event for the entire grid.
31834              * @param {Roo.EventObject} e
31835              */
31836             "mousedown" : true,
31837             /**
31838              * @event mouseup
31839              * The raw mouseup event for the entire grid.
31840              * @param {Roo.EventObject} e
31841              */
31842             "mouseup" : true,
31843             /**
31844              * @event mouseover
31845              * The raw mouseover event for the entire grid.
31846              * @param {Roo.EventObject} e
31847              */
31848             "mouseover" : true,
31849             /**
31850              * @event mouseout
31851              * The raw mouseout event for the entire grid.
31852              * @param {Roo.EventObject} e
31853              */
31854             "mouseout" : true,
31855             /**
31856              * @event keypress
31857              * The raw keypress event for the entire grid.
31858              * @param {Roo.EventObject} e
31859              */
31860             "keypress" : true,
31861             /**
31862              * @event keydown
31863              * The raw keydown event for the entire grid.
31864              * @param {Roo.EventObject} e
31865              */
31866             "keydown" : true,
31867
31868             // custom events
31869
31870             /**
31871              * @event cellclick
31872              * Fires when a cell is clicked
31873              * @param {Grid} this
31874              * @param {Number} rowIndex
31875              * @param {Number} columnIndex
31876              * @param {Roo.EventObject} e
31877              */
31878             "cellclick" : true,
31879             /**
31880              * @event celldblclick
31881              * Fires when a cell is double clicked
31882              * @param {Grid} this
31883              * @param {Number} rowIndex
31884              * @param {Number} columnIndex
31885              * @param {Roo.EventObject} e
31886              */
31887             "celldblclick" : true,
31888             /**
31889              * @event rowclick
31890              * Fires when a row is clicked
31891              * @param {Grid} this
31892              * @param {Number} rowIndex
31893              * @param {Roo.EventObject} e
31894              */
31895             "rowclick" : true,
31896             /**
31897              * @event rowdblclick
31898              * Fires when a row is double clicked
31899              * @param {Grid} this
31900              * @param {Number} rowIndex
31901              * @param {Roo.EventObject} e
31902              */
31903             "rowdblclick" : true,
31904             /**
31905              * @event headerclick
31906              * Fires when a header is clicked
31907              * @param {Grid} this
31908              * @param {Number} columnIndex
31909              * @param {Roo.EventObject} e
31910              */
31911             "headerclick" : true,
31912             /**
31913              * @event headerdblclick
31914              * Fires when a header cell is double clicked
31915              * @param {Grid} this
31916              * @param {Number} columnIndex
31917              * @param {Roo.EventObject} e
31918              */
31919             "headerdblclick" : true,
31920             /**
31921              * @event rowcontextmenu
31922              * Fires when a row is right clicked
31923              * @param {Grid} this
31924              * @param {Number} rowIndex
31925              * @param {Roo.EventObject} e
31926              */
31927             "rowcontextmenu" : true,
31928             /**
31929          * @event cellcontextmenu
31930          * Fires when a cell is right clicked
31931          * @param {Grid} this
31932          * @param {Number} rowIndex
31933          * @param {Number} cellIndex
31934          * @param {Roo.EventObject} e
31935          */
31936          "cellcontextmenu" : true,
31937             /**
31938              * @event headercontextmenu
31939              * Fires when a header is right clicked
31940              * @param {Grid} this
31941              * @param {Number} columnIndex
31942              * @param {Roo.EventObject} e
31943              */
31944             "headercontextmenu" : true,
31945             /**
31946              * @event bodyscroll
31947              * Fires when the body element is scrolled
31948              * @param {Number} scrollLeft
31949              * @param {Number} scrollTop
31950              */
31951             "bodyscroll" : true,
31952             /**
31953              * @event columnresize
31954              * Fires when the user resizes a column
31955              * @param {Number} columnIndex
31956              * @param {Number} newSize
31957              */
31958             "columnresize" : true,
31959             /**
31960              * @event columnmove
31961              * Fires when the user moves a column
31962              * @param {Number} oldIndex
31963              * @param {Number} newIndex
31964              */
31965             "columnmove" : true,
31966             /**
31967              * @event startdrag
31968              * Fires when row(s) start being dragged
31969              * @param {Grid} this
31970              * @param {Roo.GridDD} dd The drag drop object
31971              * @param {event} e The raw browser event
31972              */
31973             "startdrag" : true,
31974             /**
31975              * @event enddrag
31976              * Fires when a drag operation is complete
31977              * @param {Grid} this
31978              * @param {Roo.GridDD} dd The drag drop object
31979              * @param {event} e The raw browser event
31980              */
31981             "enddrag" : true,
31982             /**
31983              * @event dragdrop
31984              * Fires when dragged row(s) are dropped on a valid DD target
31985              * @param {Grid} this
31986              * @param {Roo.GridDD} dd The drag drop object
31987              * @param {String} targetId The target drag drop object
31988              * @param {event} e The raw browser event
31989              */
31990             "dragdrop" : true,
31991             /**
31992              * @event dragover
31993              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31994              * @param {Grid} this
31995              * @param {Roo.GridDD} dd The drag drop object
31996              * @param {String} targetId The target drag drop object
31997              * @param {event} e The raw browser event
31998              */
31999             "dragover" : true,
32000             /**
32001              * @event dragenter
32002              *  Fires when the dragged row(s) first cross another DD target while being dragged
32003              * @param {Grid} this
32004              * @param {Roo.GridDD} dd The drag drop object
32005              * @param {String} targetId The target drag drop object
32006              * @param {event} e The raw browser event
32007              */
32008             "dragenter" : true,
32009             /**
32010              * @event dragout
32011              * Fires when the dragged row(s) leave another DD target while being dragged
32012              * @param {Grid} this
32013              * @param {Roo.GridDD} dd The drag drop object
32014              * @param {String} targetId The target drag drop object
32015              * @param {event} e The raw browser event
32016              */
32017             "dragout" : true,
32018         /**
32019          * @event render
32020          * Fires when the grid is rendered
32021          * @param {Grid} grid
32022          */
32023         render : true
32024     });
32025
32026     Roo.grid.Grid.superclass.constructor.call(this);
32027 };
32028 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32029     
32030     /**
32031      * @cfg {String} ddGroup - drag drop group.
32032          */
32033     
32034     /**
32035      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32036          */
32037         minColumnWidth : 25,
32038
32039     /**
32040          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32041          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32042          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32043          */
32044         autoSizeColumns : false,
32045
32046         /**
32047          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32048          */
32049         autoSizeHeaders : true,
32050
32051         /**
32052          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32053          */
32054         monitorWindowResize : true,
32055
32056         /**
32057          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32058          * rows measured to get a columns size. Default is 0 (all rows).
32059          */
32060         maxRowsToMeasure : 0,
32061
32062         /**
32063          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32064          */
32065         trackMouseOver : true,
32066
32067     /**
32068          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32069          */
32070     
32071         /**
32072          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32073          */
32074         enableDragDrop : false,
32075
32076         /**
32077          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32078          */
32079         enableColumnMove : true,
32080
32081         /**
32082          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32083          */
32084         enableColumnHide : true,
32085
32086         /**
32087          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32088          */
32089         enableRowHeightSync : false,
32090
32091         /**
32092          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32093          */
32094         stripeRows : true,
32095
32096         /**
32097          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32098          */
32099         autoHeight : false,
32100
32101     /**
32102      * @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.
32103      */
32104     autoExpandColumn : false,
32105
32106     /**
32107     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32108     * Default is 50.
32109     */
32110     autoExpandMin : 50,
32111
32112     /**
32113     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32114     */
32115     autoExpandMax : 1000,
32116
32117     /**
32118          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32119          */
32120         view : null,
32121
32122         /**
32123      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32124          */
32125         loadMask : false,
32126     /**
32127      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32128          */
32129         dropTarget: false,
32130     // private
32131     rendered : false,
32132
32133     /**
32134     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32135     * of a fixed width. Default is false.
32136     */
32137     /**
32138     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32139     */
32140     /**
32141      * Called once after all setup has been completed and the grid is ready to be rendered.
32142      * @return {Roo.grid.Grid} this
32143      */
32144     render : function(){
32145         var c = this.container;
32146         // try to detect autoHeight/width mode
32147         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32148             this.autoHeight = true;
32149         }
32150         var view = this.getView();
32151         view.init(this);
32152
32153         c.on("click", this.onClick, this);
32154         c.on("dblclick", this.onDblClick, this);
32155         c.on("contextmenu", this.onContextMenu, this);
32156         c.on("keydown", this.onKeyDown, this);
32157
32158         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32159
32160         this.getSelectionModel().init(this);
32161
32162         view.render();
32163
32164         if(this.loadMask){
32165             this.loadMask = new Roo.LoadMask(this.container,
32166                     Roo.apply({store:this.dataSource}, this.loadMask));
32167         }
32168         
32169         
32170         if (this.toolbar && this.toolbar.xtype) {
32171             this.toolbar.container = this.getView().getHeaderPanel(true);
32172             this.toolbar = new Ext.Toolbar(this.toolbar);
32173         }
32174         if (this.footer && this.footer.xtype) {
32175             this.footer.dataSource = this.getDataSource();
32176             this.footer.container = this.getView().getFooterPanel(true);
32177             this.footer = Roo.factory(this.footer, Roo);
32178         }
32179         if (this.dropTarget && this.dropTarget.xtype) {
32180             delete this.dropTarget.xtype;
32181             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32182         }
32183         
32184         
32185         this.rendered = true;
32186         this.fireEvent('render', this);
32187         return this;
32188     },
32189
32190         /**
32191          * Reconfigures the grid to use a different Store and Column Model.
32192          * The View will be bound to the new objects and refreshed.
32193          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32194          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32195          */
32196     reconfigure : function(dataSource, colModel){
32197         if(this.loadMask){
32198             this.loadMask.destroy();
32199             this.loadMask = new Roo.LoadMask(this.container,
32200                     Roo.apply({store:dataSource}, this.loadMask));
32201         }
32202         this.view.bind(dataSource, colModel);
32203         this.dataSource = dataSource;
32204         this.colModel = colModel;
32205         this.view.refresh(true);
32206     },
32207
32208     // private
32209     onKeyDown : function(e){
32210         this.fireEvent("keydown", e);
32211     },
32212
32213     /**
32214      * Destroy this grid.
32215      * @param {Boolean} removeEl True to remove the element
32216      */
32217     destroy : function(removeEl, keepListeners){
32218         if(this.loadMask){
32219             this.loadMask.destroy();
32220         }
32221         var c = this.container;
32222         c.removeAllListeners();
32223         this.view.destroy();
32224         this.colModel.purgeListeners();
32225         if(!keepListeners){
32226             this.purgeListeners();
32227         }
32228         c.update("");
32229         if(removeEl === true){
32230             c.remove();
32231         }
32232     },
32233
32234     // private
32235     processEvent : function(name, e){
32236         this.fireEvent(name, e);
32237         var t = e.getTarget();
32238         var v = this.view;
32239         var header = v.findHeaderIndex(t);
32240         if(header !== false){
32241             this.fireEvent("header" + name, this, header, e);
32242         }else{
32243             var row = v.findRowIndex(t);
32244             var cell = v.findCellIndex(t);
32245             if(row !== false){
32246                 this.fireEvent("row" + name, this, row, e);
32247                 if(cell !== false){
32248                     this.fireEvent("cell" + name, this, row, cell, e);
32249                 }
32250             }
32251         }
32252     },
32253
32254     // private
32255     onClick : function(e){
32256         this.processEvent("click", e);
32257     },
32258
32259     // private
32260     onContextMenu : function(e, t){
32261         this.processEvent("contextmenu", e);
32262     },
32263
32264     // private
32265     onDblClick : function(e){
32266         this.processEvent("dblclick", e);
32267     },
32268
32269     // private
32270     walkCells : function(row, col, step, fn, scope){
32271         var cm = this.colModel, clen = cm.getColumnCount();
32272         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32273         if(step < 0){
32274             if(col < 0){
32275                 row--;
32276                 first = false;
32277             }
32278             while(row >= 0){
32279                 if(!first){
32280                     col = clen-1;
32281                 }
32282                 first = false;
32283                 while(col >= 0){
32284                     if(fn.call(scope || this, row, col, cm) === true){
32285                         return [row, col];
32286                     }
32287                     col--;
32288                 }
32289                 row--;
32290             }
32291         } else {
32292             if(col >= clen){
32293                 row++;
32294                 first = false;
32295             }
32296             while(row < rlen){
32297                 if(!first){
32298                     col = 0;
32299                 }
32300                 first = false;
32301                 while(col < clen){
32302                     if(fn.call(scope || this, row, col, cm) === true){
32303                         return [row, col];
32304                     }
32305                     col++;
32306                 }
32307                 row++;
32308             }
32309         }
32310         return null;
32311     },
32312
32313     // private
32314     getSelections : function(){
32315         return this.selModel.getSelections();
32316     },
32317
32318     /**
32319      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32320      * but if manual update is required this method will initiate it.
32321      */
32322     autoSize : function(){
32323         if(this.rendered){
32324             this.view.layout();
32325             if(this.view.adjustForScroll){
32326                 this.view.adjustForScroll();
32327             }
32328         }
32329     },
32330
32331     /**
32332      * Returns the grid's underlying element.
32333      * @return {Element} The element
32334      */
32335     getGridEl : function(){
32336         return this.container;
32337     },
32338
32339     // private for compatibility, overridden by editor grid
32340     stopEditing : function(){},
32341
32342     /**
32343      * Returns the grid's SelectionModel.
32344      * @return {SelectionModel}
32345      */
32346     getSelectionModel : function(){
32347         if(!this.selModel){
32348             this.selModel = new Roo.grid.RowSelectionModel();
32349         }
32350         return this.selModel;
32351     },
32352
32353     /**
32354      * Returns the grid's DataSource.
32355      * @return {DataSource}
32356      */
32357     getDataSource : function(){
32358         return this.dataSource;
32359     },
32360
32361     /**
32362      * Returns the grid's ColumnModel.
32363      * @return {ColumnModel}
32364      */
32365     getColumnModel : function(){
32366         return this.colModel;
32367     },
32368
32369     /**
32370      * Returns the grid's GridView object.
32371      * @return {GridView}
32372      */
32373     getView : function(){
32374         if(!this.view){
32375             this.view = new Roo.grid.GridView(this.viewConfig);
32376         }
32377         return this.view;
32378     },
32379     /**
32380      * Called to get grid's drag proxy text, by default returns this.ddText.
32381      * @return {String}
32382      */
32383     getDragDropText : function(){
32384         var count = this.selModel.getCount();
32385         return String.format(this.ddText, count, count == 1 ? '' : 's');
32386     }
32387 });
32388 /**
32389  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32390  * %0 is replaced with the number of selected rows.
32391  * @type String
32392  */
32393 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32394  * Based on:
32395  * Ext JS Library 1.1.1
32396  * Copyright(c) 2006-2007, Ext JS, LLC.
32397  *
32398  * Originally Released Under LGPL - original licence link has changed is not relivant.
32399  *
32400  * Fork - LGPL
32401  * <script type="text/javascript">
32402  */
32403  
32404 Roo.grid.AbstractGridView = function(){
32405         this.grid = null;
32406         
32407         this.events = {
32408             "beforerowremoved" : true,
32409             "beforerowsinserted" : true,
32410             "beforerefresh" : true,
32411             "rowremoved" : true,
32412             "rowsinserted" : true,
32413             "rowupdated" : true,
32414             "refresh" : true
32415         };
32416     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32417 };
32418
32419 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32420     rowClass : "x-grid-row",
32421     cellClass : "x-grid-cell",
32422     tdClass : "x-grid-td",
32423     hdClass : "x-grid-hd",
32424     splitClass : "x-grid-hd-split",
32425     
32426         init: function(grid){
32427         this.grid = grid;
32428                 var cid = this.grid.getGridEl().id;
32429         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32430         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32431         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32432         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32433         },
32434         
32435         getColumnRenderers : function(){
32436         var renderers = [];
32437         var cm = this.grid.colModel;
32438         var colCount = cm.getColumnCount();
32439         for(var i = 0; i < colCount; i++){
32440             renderers[i] = cm.getRenderer(i);
32441         }
32442         return renderers;
32443     },
32444     
32445     getColumnIds : function(){
32446         var ids = [];
32447         var cm = this.grid.colModel;
32448         var colCount = cm.getColumnCount();
32449         for(var i = 0; i < colCount; i++){
32450             ids[i] = cm.getColumnId(i);
32451         }
32452         return ids;
32453     },
32454     
32455     getDataIndexes : function(){
32456         if(!this.indexMap){
32457             this.indexMap = this.buildIndexMap();
32458         }
32459         return this.indexMap.colToData;
32460     },
32461     
32462     getColumnIndexByDataIndex : function(dataIndex){
32463         if(!this.indexMap){
32464             this.indexMap = this.buildIndexMap();
32465         }
32466         return this.indexMap.dataToCol[dataIndex];
32467     },
32468     
32469     /**
32470      * Set a css style for a column dynamically. 
32471      * @param {Number} colIndex The index of the column
32472      * @param {String} name The css property name
32473      * @param {String} value The css value
32474      */
32475     setCSSStyle : function(colIndex, name, value){
32476         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32477         Roo.util.CSS.updateRule(selector, name, value);
32478     },
32479     
32480     generateRules : function(cm){
32481         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32482         Roo.util.CSS.removeStyleSheet(rulesId);
32483         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32484             var cid = cm.getColumnId(i);
32485             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32486                          this.tdSelector, cid, " {\n}\n",
32487                          this.hdSelector, cid, " {\n}\n",
32488                          this.splitSelector, cid, " {\n}\n");
32489         }
32490         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32491     }
32492 });/*
32493  * Based on:
32494  * Ext JS Library 1.1.1
32495  * Copyright(c) 2006-2007, Ext JS, LLC.
32496  *
32497  * Originally Released Under LGPL - original licence link has changed is not relivant.
32498  *
32499  * Fork - LGPL
32500  * <script type="text/javascript">
32501  */
32502
32503 // private
32504 // This is a support class used internally by the Grid components
32505 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32506     this.grid = grid;
32507     this.view = grid.getView();
32508     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32509     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32510     if(hd2){
32511         this.setHandleElId(Roo.id(hd));
32512         this.setOuterHandleElId(Roo.id(hd2));
32513     }
32514     this.scroll = false;
32515 };
32516 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32517     maxDragWidth: 120,
32518     getDragData : function(e){
32519         var t = Roo.lib.Event.getTarget(e);
32520         var h = this.view.findHeaderCell(t);
32521         if(h){
32522             return {ddel: h.firstChild, header:h};
32523         }
32524         return false;
32525     },
32526
32527     onInitDrag : function(e){
32528         this.view.headersDisabled = true;
32529         var clone = this.dragData.ddel.cloneNode(true);
32530         clone.id = Roo.id();
32531         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32532         this.proxy.update(clone);
32533         return true;
32534     },
32535
32536     afterValidDrop : function(){
32537         var v = this.view;
32538         setTimeout(function(){
32539             v.headersDisabled = false;
32540         }, 50);
32541     },
32542
32543     afterInvalidDrop : function(){
32544         var v = this.view;
32545         setTimeout(function(){
32546             v.headersDisabled = false;
32547         }, 50);
32548     }
32549 });
32550 /*
32551  * Based on:
32552  * Ext JS Library 1.1.1
32553  * Copyright(c) 2006-2007, Ext JS, LLC.
32554  *
32555  * Originally Released Under LGPL - original licence link has changed is not relivant.
32556  *
32557  * Fork - LGPL
32558  * <script type="text/javascript">
32559  */
32560 // private
32561 // This is a support class used internally by the Grid components
32562 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32563     this.grid = grid;
32564     this.view = grid.getView();
32565     // split the proxies so they don't interfere with mouse events
32566     this.proxyTop = Roo.DomHelper.append(document.body, {
32567         cls:"col-move-top", html:"&#160;"
32568     }, true);
32569     this.proxyBottom = Roo.DomHelper.append(document.body, {
32570         cls:"col-move-bottom", html:"&#160;"
32571     }, true);
32572     this.proxyTop.hide = this.proxyBottom.hide = function(){
32573         this.setLeftTop(-100,-100);
32574         this.setStyle("visibility", "hidden");
32575     };
32576     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32577     // temporarily disabled
32578     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32579     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32580 };
32581 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32582     proxyOffsets : [-4, -9],
32583     fly: Roo.Element.fly,
32584
32585     getTargetFromEvent : function(e){
32586         var t = Roo.lib.Event.getTarget(e);
32587         var cindex = this.view.findCellIndex(t);
32588         if(cindex !== false){
32589             return this.view.getHeaderCell(cindex);
32590         }
32591     },
32592
32593     nextVisible : function(h){
32594         var v = this.view, cm = this.grid.colModel;
32595         h = h.nextSibling;
32596         while(h){
32597             if(!cm.isHidden(v.getCellIndex(h))){
32598                 return h;
32599             }
32600             h = h.nextSibling;
32601         }
32602         return null;
32603     },
32604
32605     prevVisible : function(h){
32606         var v = this.view, cm = this.grid.colModel;
32607         h = h.prevSibling;
32608         while(h){
32609             if(!cm.isHidden(v.getCellIndex(h))){
32610                 return h;
32611             }
32612             h = h.prevSibling;
32613         }
32614         return null;
32615     },
32616
32617     positionIndicator : function(h, n, e){
32618         var x = Roo.lib.Event.getPageX(e);
32619         var r = Roo.lib.Dom.getRegion(n.firstChild);
32620         var px, pt, py = r.top + this.proxyOffsets[1];
32621         if((r.right - x) <= (r.right-r.left)/2){
32622             px = r.right+this.view.borderWidth;
32623             pt = "after";
32624         }else{
32625             px = r.left;
32626             pt = "before";
32627         }
32628         var oldIndex = this.view.getCellIndex(h);
32629         var newIndex = this.view.getCellIndex(n);
32630
32631         if(this.grid.colModel.isFixed(newIndex)){
32632             return false;
32633         }
32634
32635         var locked = this.grid.colModel.isLocked(newIndex);
32636
32637         if(pt == "after"){
32638             newIndex++;
32639         }
32640         if(oldIndex < newIndex){
32641             newIndex--;
32642         }
32643         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32644             return false;
32645         }
32646         px +=  this.proxyOffsets[0];
32647         this.proxyTop.setLeftTop(px, py);
32648         this.proxyTop.show();
32649         if(!this.bottomOffset){
32650             this.bottomOffset = this.view.mainHd.getHeight();
32651         }
32652         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32653         this.proxyBottom.show();
32654         return pt;
32655     },
32656
32657     onNodeEnter : function(n, dd, e, data){
32658         if(data.header != n){
32659             this.positionIndicator(data.header, n, e);
32660         }
32661     },
32662
32663     onNodeOver : function(n, dd, e, data){
32664         var result = false;
32665         if(data.header != n){
32666             result = this.positionIndicator(data.header, n, e);
32667         }
32668         if(!result){
32669             this.proxyTop.hide();
32670             this.proxyBottom.hide();
32671         }
32672         return result ? this.dropAllowed : this.dropNotAllowed;
32673     },
32674
32675     onNodeOut : function(n, dd, e, data){
32676         this.proxyTop.hide();
32677         this.proxyBottom.hide();
32678     },
32679
32680     onNodeDrop : function(n, dd, e, data){
32681         var h = data.header;
32682         if(h != n){
32683             var cm = this.grid.colModel;
32684             var x = Roo.lib.Event.getPageX(e);
32685             var r = Roo.lib.Dom.getRegion(n.firstChild);
32686             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32687             var oldIndex = this.view.getCellIndex(h);
32688             var newIndex = this.view.getCellIndex(n);
32689             var locked = cm.isLocked(newIndex);
32690             if(pt == "after"){
32691                 newIndex++;
32692             }
32693             if(oldIndex < newIndex){
32694                 newIndex--;
32695             }
32696             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32697                 return false;
32698             }
32699             cm.setLocked(oldIndex, locked, true);
32700             cm.moveColumn(oldIndex, newIndex);
32701             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32702             return true;
32703         }
32704         return false;
32705     }
32706 });
32707 /*
32708  * Based on:
32709  * Ext JS Library 1.1.1
32710  * Copyright(c) 2006-2007, Ext JS, LLC.
32711  *
32712  * Originally Released Under LGPL - original licence link has changed is not relivant.
32713  *
32714  * Fork - LGPL
32715  * <script type="text/javascript">
32716  */
32717   
32718 /**
32719  * @class Roo.grid.GridView
32720  * @extends Roo.util.Observable
32721  *
32722  * @constructor
32723  * @param {Object} config
32724  */
32725 Roo.grid.GridView = function(config){
32726     Roo.grid.GridView.superclass.constructor.call(this);
32727     this.el = null;
32728
32729     Roo.apply(this, config);
32730 };
32731
32732 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32733
32734     /**
32735      * Override this function to apply custom css classes to rows during rendering
32736      * @param {Record} record The record
32737      * @param {Number} index
32738      * @method getRowClass
32739      */
32740     rowClass : "x-grid-row",
32741
32742     cellClass : "x-grid-col",
32743
32744     tdClass : "x-grid-td",
32745
32746     hdClass : "x-grid-hd",
32747
32748     splitClass : "x-grid-split",
32749
32750     sortClasses : ["sort-asc", "sort-desc"],
32751
32752     enableMoveAnim : false,
32753
32754     hlColor: "C3DAF9",
32755
32756     dh : Roo.DomHelper,
32757
32758     fly : Roo.Element.fly,
32759
32760     css : Roo.util.CSS,
32761
32762     borderWidth: 1,
32763
32764     splitOffset: 3,
32765
32766     scrollIncrement : 22,
32767
32768     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32769
32770     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32771
32772     bind : function(ds, cm){
32773         if(this.ds){
32774             this.ds.un("load", this.onLoad, this);
32775             this.ds.un("datachanged", this.onDataChange, this);
32776             this.ds.un("add", this.onAdd, this);
32777             this.ds.un("remove", this.onRemove, this);
32778             this.ds.un("update", this.onUpdate, this);
32779             this.ds.un("clear", this.onClear, this);
32780         }
32781         if(ds){
32782             ds.on("load", this.onLoad, this);
32783             ds.on("datachanged", this.onDataChange, this);
32784             ds.on("add", this.onAdd, this);
32785             ds.on("remove", this.onRemove, this);
32786             ds.on("update", this.onUpdate, this);
32787             ds.on("clear", this.onClear, this);
32788         }
32789         this.ds = ds;
32790
32791         if(this.cm){
32792             this.cm.un("widthchange", this.onColWidthChange, this);
32793             this.cm.un("headerchange", this.onHeaderChange, this);
32794             this.cm.un("hiddenchange", this.onHiddenChange, this);
32795             this.cm.un("columnmoved", this.onColumnMove, this);
32796             this.cm.un("columnlockchange", this.onColumnLock, this);
32797         }
32798         if(cm){
32799             this.generateRules(cm);
32800             cm.on("widthchange", this.onColWidthChange, this);
32801             cm.on("headerchange", this.onHeaderChange, this);
32802             cm.on("hiddenchange", this.onHiddenChange, this);
32803             cm.on("columnmoved", this.onColumnMove, this);
32804             cm.on("columnlockchange", this.onColumnLock, this);
32805         }
32806         this.cm = cm;
32807     },
32808
32809     init: function(grid){
32810                 Roo.grid.GridView.superclass.init.call(this, grid);
32811
32812                 this.bind(grid.dataSource, grid.colModel);
32813
32814             grid.on("headerclick", this.handleHeaderClick, this);
32815
32816         if(grid.trackMouseOver){
32817             grid.on("mouseover", this.onRowOver, this);
32818                 grid.on("mouseout", this.onRowOut, this);
32819             }
32820             grid.cancelTextSelection = function(){};
32821                 this.gridId = grid.id;
32822
32823                 var tpls = this.templates || {};
32824
32825                 if(!tpls.master){
32826                     tpls.master = new Roo.Template(
32827                        '<div class="x-grid" hidefocus="true">',
32828                           '<div class="x-grid-topbar"></div>',
32829                           '<div class="x-grid-scroller"><div></div></div>',
32830                           '<div class="x-grid-locked">',
32831                               '<div class="x-grid-header">{lockedHeader}</div>',
32832                               '<div class="x-grid-body">{lockedBody}</div>',
32833                           "</div>",
32834                           '<div class="x-grid-viewport">',
32835                               '<div class="x-grid-header">{header}</div>',
32836                               '<div class="x-grid-body">{body}</div>',
32837                           "</div>",
32838                           '<div class="x-grid-bottombar"></div>',
32839                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32840                           '<div class="x-grid-resize-proxy">&#160;</div>',
32841                        "</div>"
32842                     );
32843                     tpls.master.disableformats = true;
32844                 }
32845
32846                 if(!tpls.header){
32847                     tpls.header = new Roo.Template(
32848                        '<table border="0" cellspacing="0" cellpadding="0">',
32849                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32850                        "</table>{splits}"
32851                     );
32852                     tpls.header.disableformats = true;
32853                 }
32854                 tpls.header.compile();
32855
32856                 if(!tpls.hcell){
32857                     tpls.hcell = new Roo.Template(
32858                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32859                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32860                         "</div></td>"
32861                      );
32862                      tpls.hcell.disableFormats = true;
32863                 }
32864                 tpls.hcell.compile();
32865
32866                 if(!tpls.hsplit){
32867                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32868                     tpls.hsplit.disableFormats = true;
32869                 }
32870                 tpls.hsplit.compile();
32871
32872                 if(!tpls.body){
32873                     tpls.body = new Roo.Template(
32874                        '<table border="0" cellspacing="0" cellpadding="0">',
32875                        "<tbody>{rows}</tbody>",
32876                        "</table>"
32877                     );
32878                     tpls.body.disableFormats = true;
32879                 }
32880                 tpls.body.compile();
32881
32882                 if(!tpls.row){
32883                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32884                     tpls.row.disableFormats = true;
32885                 }
32886                 tpls.row.compile();
32887
32888                 if(!tpls.cell){
32889                     tpls.cell = new Roo.Template(
32890                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32891                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32892                         "</td>"
32893                     );
32894             tpls.cell.disableFormats = true;
32895         }
32896                 tpls.cell.compile();
32897
32898                 this.templates = tpls;
32899         },
32900
32901         // remap these for backwards compat
32902     onColWidthChange : function(){
32903         this.updateColumns.apply(this, arguments);
32904     },
32905     onHeaderChange : function(){
32906         this.updateHeaders.apply(this, arguments);
32907     }, 
32908     onHiddenChange : function(){
32909         this.handleHiddenChange.apply(this, arguments);
32910     },
32911     onColumnMove : function(){
32912         this.handleColumnMove.apply(this, arguments);
32913     },
32914     onColumnLock : function(){
32915         this.handleLockChange.apply(this, arguments);
32916     },
32917
32918     onDataChange : function(){
32919         this.refresh();
32920         this.updateHeaderSortState();
32921     },
32922
32923         onClear : function(){
32924         this.refresh();
32925     },
32926
32927         onUpdate : function(ds, record){
32928         this.refreshRow(record);
32929     },
32930
32931     refreshRow : function(record){
32932         var ds = this.ds, index;
32933         if(typeof record == 'number'){
32934             index = record;
32935             record = ds.getAt(index);
32936         }else{
32937             index = ds.indexOf(record);
32938         }
32939         this.insertRows(ds, index, index, true);
32940         this.onRemove(ds, record, index+1, true);
32941         this.syncRowHeights(index, index);
32942         this.layout();
32943         this.fireEvent("rowupdated", this, index, record);
32944     },
32945
32946     onAdd : function(ds, records, index){
32947         this.insertRows(ds, index, index + (records.length-1));
32948     },
32949
32950     onRemove : function(ds, record, index, isUpdate){
32951         if(isUpdate !== true){
32952             this.fireEvent("beforerowremoved", this, index, record);
32953         }
32954         var bt = this.getBodyTable(), lt = this.getLockedTable();
32955         if(bt.rows[index]){
32956             bt.firstChild.removeChild(bt.rows[index]);
32957         }
32958         if(lt.rows[index]){
32959             lt.firstChild.removeChild(lt.rows[index]);
32960         }
32961         if(isUpdate !== true){
32962             this.stripeRows(index);
32963             this.syncRowHeights(index, index);
32964             this.layout();
32965             this.fireEvent("rowremoved", this, index, record);
32966         }
32967     },
32968
32969     onLoad : function(){
32970         this.scrollToTop();
32971     },
32972
32973     /**
32974      * Scrolls the grid to the top
32975      */
32976     scrollToTop : function(){
32977         if(this.scroller){
32978             this.scroller.dom.scrollTop = 0;
32979             this.syncScroll();
32980         }
32981     },
32982
32983     /**
32984      * Gets a panel in the header of the grid that can be used for toolbars etc.
32985      * After modifying the contents of this panel a call to grid.autoSize() may be
32986      * required to register any changes in size.
32987      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32988      * @return Roo.Element
32989      */
32990     getHeaderPanel : function(doShow){
32991         if(doShow){
32992             this.headerPanel.show();
32993         }
32994         return this.headerPanel;
32995         },
32996
32997         /**
32998      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32999      * After modifying the contents of this panel a call to grid.autoSize() may be
33000      * required to register any changes in size.
33001      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33002      * @return Roo.Element
33003      */
33004     getFooterPanel : function(doShow){
33005         if(doShow){
33006             this.footerPanel.show();
33007         }
33008         return this.footerPanel;
33009         },
33010
33011         initElements : function(){
33012             var E = Roo.Element;
33013             var el = this.grid.getGridEl().dom.firstChild;
33014             var cs = el.childNodes;
33015
33016             this.el = new E(el);
33017             this.headerPanel = new E(el.firstChild);
33018             this.headerPanel.enableDisplayMode("block");
33019
33020         this.scroller = new E(cs[1]);
33021             this.scrollSizer = new E(this.scroller.dom.firstChild);
33022
33023             this.lockedWrap = new E(cs[2]);
33024             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33025             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33026
33027             this.mainWrap = new E(cs[3]);
33028             this.mainHd = new E(this.mainWrap.dom.firstChild);
33029             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33030
33031             this.footerPanel = new E(cs[4]);
33032             this.footerPanel.enableDisplayMode("block");
33033
33034         this.focusEl = new E(cs[5]);
33035         this.focusEl.swallowEvent("click", true);
33036         this.resizeProxy = new E(cs[6]);
33037
33038             this.headerSelector = String.format(
33039                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33040                this.lockedHd.id, this.mainHd.id
33041             );
33042
33043             this.splitterSelector = String.format(
33044                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33045                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33046             );
33047     },
33048     idToCssName : function(s)
33049     {
33050         return s.replace(/[^a-z0-9]+/ig, '-');
33051     },
33052
33053         getHeaderCell : function(index){
33054             return Roo.DomQuery.select(this.headerSelector)[index];
33055         },
33056
33057         getHeaderCellMeasure : function(index){
33058             return this.getHeaderCell(index).firstChild;
33059         },
33060
33061         getHeaderCellText : function(index){
33062             return this.getHeaderCell(index).firstChild.firstChild;
33063         },
33064
33065         getLockedTable : function(){
33066             return this.lockedBody.dom.firstChild;
33067         },
33068
33069         getBodyTable : function(){
33070             return this.mainBody.dom.firstChild;
33071         },
33072
33073         getLockedRow : function(index){
33074             return this.getLockedTable().rows[index];
33075         },
33076
33077         getRow : function(index){
33078             return this.getBodyTable().rows[index];
33079         },
33080
33081         getRowComposite : function(index){
33082             if(!this.rowEl){
33083                 this.rowEl = new Roo.CompositeElementLite();
33084             }
33085         var els = [], lrow, mrow;
33086         if(lrow = this.getLockedRow(index)){
33087             els.push(lrow);
33088         }
33089         if(mrow = this.getRow(index)){
33090             els.push(mrow);
33091         }
33092         this.rowEl.elements = els;
33093             return this.rowEl;
33094         },
33095
33096         getCell : function(rowIndex, colIndex){
33097             var locked = this.cm.getLockedCount();
33098             var source;
33099             if(colIndex < locked){
33100                 source = this.lockedBody.dom.firstChild;
33101             }else{
33102                 source = this.mainBody.dom.firstChild;
33103                 colIndex -= locked;
33104             }
33105         return source.rows[rowIndex].childNodes[colIndex];
33106         },
33107
33108         getCellText : function(rowIndex, colIndex){
33109             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33110         },
33111
33112         getCellBox : function(cell){
33113             var b = this.fly(cell).getBox();
33114         if(Roo.isOpera){ // opera fails to report the Y
33115             b.y = cell.offsetTop + this.mainBody.getY();
33116         }
33117         return b;
33118     },
33119
33120     getCellIndex : function(cell){
33121         var id = String(cell.className).match(this.cellRE);
33122         if(id){
33123             return parseInt(id[1], 10);
33124         }
33125         return 0;
33126     },
33127
33128     findHeaderIndex : function(n){
33129         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33130         return r ? this.getCellIndex(r) : false;
33131     },
33132
33133     findHeaderCell : function(n){
33134         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33135         return r ? r : false;
33136     },
33137
33138     findRowIndex : function(n){
33139         if(!n){
33140             return false;
33141         }
33142         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33143         return r ? r.rowIndex : false;
33144     },
33145
33146     findCellIndex : function(node){
33147         var stop = this.el.dom;
33148         while(node && node != stop){
33149             if(this.findRE.test(node.className)){
33150                 return this.getCellIndex(node);
33151             }
33152             node = node.parentNode;
33153         }
33154         return false;
33155     },
33156
33157     getColumnId : function(index){
33158             return this.cm.getColumnId(index);
33159         },
33160
33161         getSplitters : function(){
33162             if(this.splitterSelector){
33163                return Roo.DomQuery.select(this.splitterSelector);
33164             }else{
33165                 return null;
33166             }
33167         },
33168
33169         getSplitter : function(index){
33170             return this.getSplitters()[index];
33171         },
33172
33173     onRowOver : function(e, t){
33174         var row;
33175         if((row = this.findRowIndex(t)) !== false){
33176             this.getRowComposite(row).addClass("x-grid-row-over");
33177         }
33178     },
33179
33180     onRowOut : function(e, t){
33181         var row;
33182         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33183             this.getRowComposite(row).removeClass("x-grid-row-over");
33184         }
33185     },
33186
33187     renderHeaders : function(){
33188             var cm = this.cm;
33189         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33190         var cb = [], lb = [], sb = [], lsb = [], p = {};
33191         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33192             p.cellId = "x-grid-hd-0-" + i;
33193             p.splitId = "x-grid-csplit-0-" + i;
33194             p.id = cm.getColumnId(i);
33195             p.title = cm.getColumnTooltip(i) || "";
33196             p.value = cm.getColumnHeader(i) || "";
33197             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33198             if(!cm.isLocked(i)){
33199                 cb[cb.length] = ct.apply(p);
33200                 sb[sb.length] = st.apply(p);
33201             }else{
33202                 lb[lb.length] = ct.apply(p);
33203                 lsb[lsb.length] = st.apply(p);
33204             }
33205         }
33206         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33207                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33208         },
33209
33210         updateHeaders : function(){
33211         var html = this.renderHeaders();
33212         this.lockedHd.update(html[0]);
33213         this.mainHd.update(html[1]);
33214     },
33215
33216     /**
33217      * Focuses the specified row.
33218      * @param {Number} row The row index
33219      */
33220     focusRow : function(row){
33221         var x = this.scroller.dom.scrollLeft;
33222         this.focusCell(row, 0, false);
33223         this.scroller.dom.scrollLeft = x;
33224     },
33225
33226     /**
33227      * Focuses the specified cell.
33228      * @param {Number} row The row index
33229      * @param {Number} col The column index
33230      * @param {Boolean} hscroll false to disable horizontal scrolling
33231      */
33232     focusCell : function(row, col, hscroll){
33233         var el = this.ensureVisible(row, col, hscroll);
33234         this.focusEl.alignTo(el, "tl-tl");
33235         if(Roo.isGecko){
33236             this.focusEl.focus();
33237         }else{
33238             this.focusEl.focus.defer(1, this.focusEl);
33239         }
33240     },
33241
33242     /**
33243      * Scrolls the specified cell into view
33244      * @param {Number} row The row index
33245      * @param {Number} col The column index
33246      * @param {Boolean} hscroll false to disable horizontal scrolling
33247      */
33248     ensureVisible : function(row, col, hscroll){
33249         if(typeof row != "number"){
33250             row = row.rowIndex;
33251         }
33252         if(row < 0 && row >= this.ds.getCount()){
33253             return;
33254         }
33255         col = (col !== undefined ? col : 0);
33256         var cm = this.grid.colModel;
33257         while(cm.isHidden(col)){
33258             col++;
33259         }
33260
33261         var el = this.getCell(row, col);
33262         if(!el){
33263             return;
33264         }
33265         var c = this.scroller.dom;
33266
33267         var ctop = parseInt(el.offsetTop, 10);
33268         var cleft = parseInt(el.offsetLeft, 10);
33269         var cbot = ctop + el.offsetHeight;
33270         var cright = cleft + el.offsetWidth;
33271
33272         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33273         var stop = parseInt(c.scrollTop, 10);
33274         var sleft = parseInt(c.scrollLeft, 10);
33275         var sbot = stop + ch;
33276         var sright = sleft + c.clientWidth;
33277
33278         if(ctop < stop){
33279                 c.scrollTop = ctop;
33280         }else if(cbot > sbot){
33281             c.scrollTop = cbot-ch;
33282         }
33283
33284         if(hscroll !== false){
33285             if(cleft < sleft){
33286                 c.scrollLeft = cleft;
33287             }else if(cright > sright){
33288                 c.scrollLeft = cright-c.clientWidth;
33289             }
33290         }
33291         return el;
33292     },
33293
33294     updateColumns : function(){
33295         this.grid.stopEditing();
33296         var cm = this.grid.colModel, colIds = this.getColumnIds();
33297         //var totalWidth = cm.getTotalWidth();
33298         var pos = 0;
33299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33300             //if(cm.isHidden(i)) continue;
33301             var w = cm.getColumnWidth(i);
33302             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33303             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33304         }
33305         this.updateSplitters();
33306     },
33307
33308     generateRules : function(cm){
33309         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33310         Roo.util.CSS.removeStyleSheet(rulesId);
33311         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33312             var cid = cm.getColumnId(i);
33313             var align = '';
33314             if(cm.config[i].align){
33315                 align = 'text-align:'+cm.config[i].align+';';
33316             }
33317             var hidden = '';
33318             if(cm.isHidden(i)){
33319                 hidden = 'display:none;';
33320             }
33321             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33322             ruleBuf.push(
33323                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33324                     this.hdSelector, cid, " {\n", align, width, "}\n",
33325                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33326                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33327         }
33328         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33329     },
33330
33331     updateSplitters : function(){
33332         var cm = this.cm, s = this.getSplitters();
33333         if(s){ // splitters not created yet
33334             var pos = 0, locked = true;
33335             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33336                 if(cm.isHidden(i)) continue;
33337                 var w = cm.getColumnWidth(i);
33338                 if(!cm.isLocked(i) && locked){
33339                     pos = 0;
33340                     locked = false;
33341                 }
33342                 pos += w;
33343                 s[i].style.left = (pos-this.splitOffset) + "px";
33344             }
33345         }
33346     },
33347
33348     handleHiddenChange : function(colModel, colIndex, hidden){
33349         if(hidden){
33350             this.hideColumn(colIndex);
33351         }else{
33352             this.unhideColumn(colIndex);
33353         }
33354     },
33355
33356     hideColumn : function(colIndex){
33357         var cid = this.getColumnId(colIndex);
33358         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33359         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33360         if(Roo.isSafari){
33361             this.updateHeaders();
33362         }
33363         this.updateSplitters();
33364         this.layout();
33365     },
33366
33367     unhideColumn : function(colIndex){
33368         var cid = this.getColumnId(colIndex);
33369         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33370         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33371
33372         if(Roo.isSafari){
33373             this.updateHeaders();
33374         }
33375         this.updateSplitters();
33376         this.layout();
33377     },
33378
33379     insertRows : function(dm, firstRow, lastRow, isUpdate){
33380         if(firstRow == 0 && lastRow == dm.getCount()-1){
33381             this.refresh();
33382         }else{
33383             if(!isUpdate){
33384                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33385             }
33386             var s = this.getScrollState();
33387             var markup = this.renderRows(firstRow, lastRow);
33388             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33389             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33390             this.restoreScroll(s);
33391             if(!isUpdate){
33392                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33393                 this.syncRowHeights(firstRow, lastRow);
33394                 this.stripeRows(firstRow);
33395                 this.layout();
33396             }
33397         }
33398     },
33399
33400     bufferRows : function(markup, target, index){
33401         var before = null, trows = target.rows, tbody = target.tBodies[0];
33402         if(index < trows.length){
33403             before = trows[index];
33404         }
33405         var b = document.createElement("div");
33406         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33407         var rows = b.firstChild.rows;
33408         for(var i = 0, len = rows.length; i < len; i++){
33409             if(before){
33410                 tbody.insertBefore(rows[0], before);
33411             }else{
33412                 tbody.appendChild(rows[0]);
33413             }
33414         }
33415         b.innerHTML = "";
33416         b = null;
33417     },
33418
33419     deleteRows : function(dm, firstRow, lastRow){
33420         if(dm.getRowCount()<1){
33421             this.fireEvent("beforerefresh", this);
33422             this.mainBody.update("");
33423             this.lockedBody.update("");
33424             this.fireEvent("refresh", this);
33425         }else{
33426             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33427             var bt = this.getBodyTable();
33428             var tbody = bt.firstChild;
33429             var rows = bt.rows;
33430             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33431                 tbody.removeChild(rows[firstRow]);
33432             }
33433             this.stripeRows(firstRow);
33434             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33435         }
33436     },
33437
33438     updateRows : function(dataSource, firstRow, lastRow){
33439         var s = this.getScrollState();
33440         this.refresh();
33441         this.restoreScroll(s);
33442     },
33443
33444     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33445         if(!noRefresh){
33446            this.refresh();
33447         }
33448         this.updateHeaderSortState();
33449     },
33450
33451     getScrollState : function(){
33452         var sb = this.scroller.dom;
33453         return {left: sb.scrollLeft, top: sb.scrollTop};
33454     },
33455
33456     stripeRows : function(startRow){
33457         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33458             return;
33459         }
33460         startRow = startRow || 0;
33461         var rows = this.getBodyTable().rows;
33462         var lrows = this.getLockedTable().rows;
33463         var cls = ' x-grid-row-alt ';
33464         for(var i = startRow, len = rows.length; i < len; i++){
33465             var row = rows[i], lrow = lrows[i];
33466             var isAlt = ((i+1) % 2 == 0);
33467             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33468             if(isAlt == hasAlt){
33469                 continue;
33470             }
33471             if(isAlt){
33472                 row.className += " x-grid-row-alt";
33473             }else{
33474                 row.className = row.className.replace("x-grid-row-alt", "");
33475             }
33476             if(lrow){
33477                 lrow.className = row.className;
33478             }
33479         }
33480     },
33481
33482     restoreScroll : function(state){
33483         var sb = this.scroller.dom;
33484         sb.scrollLeft = state.left;
33485         sb.scrollTop = state.top;
33486         this.syncScroll();
33487     },
33488
33489     syncScroll : function(){
33490         var sb = this.scroller.dom;
33491         var sh = this.mainHd.dom;
33492         var bs = this.mainBody.dom;
33493         var lv = this.lockedBody.dom;
33494         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33495         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33496     },
33497
33498     handleScroll : function(e){
33499         this.syncScroll();
33500         var sb = this.scroller.dom;
33501         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33502         e.stopEvent();
33503     },
33504
33505     handleWheel : function(e){
33506         var d = e.getWheelDelta();
33507         this.scroller.dom.scrollTop -= d*22;
33508         // set this here to prevent jumpy scrolling on large tables
33509         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33510         e.stopEvent();
33511     },
33512
33513     renderRows : function(startRow, endRow){
33514         // pull in all the crap needed to render rows
33515         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33516         var colCount = cm.getColumnCount();
33517
33518         if(ds.getCount() < 1){
33519             return ["", ""];
33520         }
33521
33522         // build a map for all the columns
33523         var cs = [];
33524         for(var i = 0; i < colCount; i++){
33525             var name = cm.getDataIndex(i);
33526             cs[i] = {
33527                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33528                 renderer : cm.getRenderer(i),
33529                 id : cm.getColumnId(i),
33530                 locked : cm.isLocked(i)
33531             };
33532         }
33533
33534         startRow = startRow || 0;
33535         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33536
33537         // records to render
33538         var rs = ds.getRange(startRow, endRow);
33539
33540         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33541     },
33542
33543     // As much as I hate to duplicate code, this was branched because FireFox really hates
33544     // [].join("") on strings. The performance difference was substantial enough to
33545     // branch this function
33546     doRender : Roo.isGecko ?
33547             function(cs, rs, ds, startRow, colCount, stripe){
33548                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33549                 // buffers
33550                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33551                 for(var j = 0, len = rs.length; j < len; j++){
33552                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33553                     for(var i = 0; i < colCount; i++){
33554                         c = cs[i];
33555                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33556                         p.id = c.id;
33557                         p.css = p.attr = "";
33558                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33559                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33560                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33561                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33562                         }
33563                         var markup = ct.apply(p);
33564                         if(!c.locked){
33565                             cb+= markup;
33566                         }else{
33567                             lcb+= markup;
33568                         }
33569                     }
33570                     var alt = [];
33571                     if(stripe && ((rowIndex+1) % 2 == 0)){
33572                         alt[0] = "x-grid-row-alt";
33573                     }
33574                     if(r.dirty){
33575                         alt[1] = " x-grid-dirty-row";
33576                     }
33577                     rp.cells = lcb;
33578                     if(this.getRowClass){
33579                         alt[2] = this.getRowClass(r, rowIndex);
33580                     }
33581                     rp.alt = alt.join(" ");
33582                     lbuf+= rt.apply(rp);
33583                     rp.cells = cb;
33584                     buf+=  rt.apply(rp);
33585                 }
33586                 return [lbuf, buf];
33587             } :
33588             function(cs, rs, ds, startRow, colCount, stripe){
33589                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33590                 // buffers
33591                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33592                 for(var j = 0, len = rs.length; j < len; j++){
33593                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33594                     for(var i = 0; i < colCount; i++){
33595                         c = cs[i];
33596                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33597                         p.id = c.id;
33598                         p.css = p.attr = "";
33599                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33600                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33601                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33602                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33603                         }
33604                         var markup = ct.apply(p);
33605                         if(!c.locked){
33606                             cb[cb.length] = markup;
33607                         }else{
33608                             lcb[lcb.length] = markup;
33609                         }
33610                     }
33611                     var alt = [];
33612                     if(stripe && ((rowIndex+1) % 2 == 0)){
33613                         alt[0] = "x-grid-row-alt";
33614                     }
33615                     if(r.dirty){
33616                         alt[1] = " x-grid-dirty-row";
33617                     }
33618                     rp.cells = lcb;
33619                     if(this.getRowClass){
33620                         alt[2] = this.getRowClass(r, rowIndex);
33621                     }
33622                     rp.alt = alt.join(" ");
33623                     rp.cells = lcb.join("");
33624                     lbuf[lbuf.length] = rt.apply(rp);
33625                     rp.cells = cb.join("");
33626                     buf[buf.length] =  rt.apply(rp);
33627                 }
33628                 return [lbuf.join(""), buf.join("")];
33629             },
33630
33631     renderBody : function(){
33632         var markup = this.renderRows();
33633         var bt = this.templates.body;
33634         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33635     },
33636
33637     /**
33638      * Refreshes the grid
33639      * @param {Boolean} headersToo
33640      */
33641     refresh : function(headersToo){
33642         this.fireEvent("beforerefresh", this);
33643         this.grid.stopEditing();
33644         var result = this.renderBody();
33645         this.lockedBody.update(result[0]);
33646         this.mainBody.update(result[1]);
33647         if(headersToo === true){
33648             this.updateHeaders();
33649             this.updateColumns();
33650             this.updateSplitters();
33651             this.updateHeaderSortState();
33652         }
33653         this.syncRowHeights();
33654         this.layout();
33655         this.fireEvent("refresh", this);
33656     },
33657
33658     handleColumnMove : function(cm, oldIndex, newIndex){
33659         this.indexMap = null;
33660         var s = this.getScrollState();
33661         this.refresh(true);
33662         this.restoreScroll(s);
33663         this.afterMove(newIndex);
33664     },
33665
33666     afterMove : function(colIndex){
33667         if(this.enableMoveAnim && Roo.enableFx){
33668             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33669         }
33670     },
33671
33672     updateCell : function(dm, rowIndex, dataIndex){
33673         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33674         if(typeof colIndex == "undefined"){ // not present in grid
33675             return;
33676         }
33677         var cm = this.grid.colModel;
33678         var cell = this.getCell(rowIndex, colIndex);
33679         var cellText = this.getCellText(rowIndex, colIndex);
33680
33681         var p = {
33682             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33683             id : cm.getColumnId(colIndex),
33684             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33685         };
33686         var renderer = cm.getRenderer(colIndex);
33687         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33688         if(typeof val == "undefined" || val === "") val = "&#160;";
33689         cellText.innerHTML = val;
33690         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33691         this.syncRowHeights(rowIndex, rowIndex);
33692     },
33693
33694     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33695         var maxWidth = 0;
33696         if(this.grid.autoSizeHeaders){
33697             var h = this.getHeaderCellMeasure(colIndex);
33698             maxWidth = Math.max(maxWidth, h.scrollWidth);
33699         }
33700         var tb, index;
33701         if(this.cm.isLocked(colIndex)){
33702             tb = this.getLockedTable();
33703             index = colIndex;
33704         }else{
33705             tb = this.getBodyTable();
33706             index = colIndex - this.cm.getLockedCount();
33707         }
33708         if(tb && tb.rows){
33709             var rows = tb.rows;
33710             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33711             for(var i = 0; i < stopIndex; i++){
33712                 var cell = rows[i].childNodes[index].firstChild;
33713                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33714             }
33715         }
33716         return maxWidth + /*margin for error in IE*/ 5;
33717     },
33718     /**
33719      * Autofit a column to its content.
33720      * @param {Number} colIndex
33721      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33722      */
33723      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33724          if(this.cm.isHidden(colIndex)){
33725              return; // can't calc a hidden column
33726          }
33727         if(forceMinSize){
33728             var cid = this.cm.getColumnId(colIndex);
33729             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33730            if(this.grid.autoSizeHeaders){
33731                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33732            }
33733         }
33734         var newWidth = this.calcColumnWidth(colIndex);
33735         this.cm.setColumnWidth(colIndex,
33736             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33737         if(!suppressEvent){
33738             this.grid.fireEvent("columnresize", colIndex, newWidth);
33739         }
33740     },
33741
33742     /**
33743      * Autofits all columns to their content and then expands to fit any extra space in the grid
33744      */
33745      autoSizeColumns : function(){
33746         var cm = this.grid.colModel;
33747         var colCount = cm.getColumnCount();
33748         for(var i = 0; i < colCount; i++){
33749             this.autoSizeColumn(i, true, true);
33750         }
33751         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33752             this.fitColumns();
33753         }else{
33754             this.updateColumns();
33755             this.layout();
33756         }
33757     },
33758
33759     /**
33760      * Autofits all columns to the grid's width proportionate with their current size
33761      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33762      */
33763     fitColumns : function(reserveScrollSpace){
33764         var cm = this.grid.colModel;
33765         var colCount = cm.getColumnCount();
33766         var cols = [];
33767         var width = 0;
33768         var i, w;
33769         for (i = 0; i < colCount; i++){
33770             if(!cm.isHidden(i) && !cm.isFixed(i)){
33771                 w = cm.getColumnWidth(i);
33772                 cols.push(i);
33773                 cols.push(w);
33774                 width += w;
33775             }
33776         }
33777         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33778         if(reserveScrollSpace){
33779             avail -= 17;
33780         }
33781         var frac = (avail - cm.getTotalWidth())/width;
33782         while (cols.length){
33783             w = cols.pop();
33784             i = cols.pop();
33785             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33786         }
33787         this.updateColumns();
33788         this.layout();
33789     },
33790
33791     onRowSelect : function(rowIndex){
33792         var row = this.getRowComposite(rowIndex);
33793         row.addClass("x-grid-row-selected");
33794     },
33795
33796     onRowDeselect : function(rowIndex){
33797         var row = this.getRowComposite(rowIndex);
33798         row.removeClass("x-grid-row-selected");
33799     },
33800
33801     onCellSelect : function(row, col){
33802         var cell = this.getCell(row, col);
33803         if(cell){
33804             Roo.fly(cell).addClass("x-grid-cell-selected");
33805         }
33806     },
33807
33808     onCellDeselect : function(row, col){
33809         var cell = this.getCell(row, col);
33810         if(cell){
33811             Roo.fly(cell).removeClass("x-grid-cell-selected");
33812         }
33813     },
33814
33815     updateHeaderSortState : function(){
33816         var state = this.ds.getSortState();
33817         if(!state){
33818             return;
33819         }
33820         this.sortState = state;
33821         var sortColumn = this.cm.findColumnIndex(state.field);
33822         if(sortColumn != -1){
33823             var sortDir = state.direction;
33824             var sc = this.sortClasses;
33825             var hds = this.el.select(this.headerSelector).removeClass(sc);
33826             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33827         }
33828     },
33829
33830     handleHeaderClick : function(g, index){
33831         if(this.headersDisabled){
33832             return;
33833         }
33834         var dm = g.dataSource, cm = g.colModel;
33835             if(!cm.isSortable(index)){
33836             return;
33837         }
33838             g.stopEditing();
33839         dm.sort(cm.getDataIndex(index));
33840     },
33841
33842
33843     destroy : function(){
33844         if(this.colMenu){
33845             this.colMenu.removeAll();
33846             Roo.menu.MenuMgr.unregister(this.colMenu);
33847             this.colMenu.getEl().remove();
33848             delete this.colMenu;
33849         }
33850         if(this.hmenu){
33851             this.hmenu.removeAll();
33852             Roo.menu.MenuMgr.unregister(this.hmenu);
33853             this.hmenu.getEl().remove();
33854             delete this.hmenu;
33855         }
33856         if(this.grid.enableColumnMove){
33857             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33858             if(dds){
33859                 for(var dd in dds){
33860                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33861                         var elid = dds[dd].dragElId;
33862                         dds[dd].unreg();
33863                         Roo.get(elid).remove();
33864                     } else if(dds[dd].config.isTarget){
33865                         dds[dd].proxyTop.remove();
33866                         dds[dd].proxyBottom.remove();
33867                         dds[dd].unreg();
33868                     }
33869                     if(Roo.dd.DDM.locationCache[dd]){
33870                         delete Roo.dd.DDM.locationCache[dd];
33871                     }
33872                 }
33873                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33874             }
33875         }
33876         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33877         this.bind(null, null);
33878         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33879     },
33880
33881     handleLockChange : function(){
33882         this.refresh(true);
33883     },
33884
33885     onDenyColumnLock : function(){
33886
33887     },
33888
33889     onDenyColumnHide : function(){
33890
33891     },
33892
33893     handleHdMenuClick : function(item){
33894         var index = this.hdCtxIndex;
33895         var cm = this.cm, ds = this.ds;
33896         switch(item.id){
33897             case "asc":
33898                 ds.sort(cm.getDataIndex(index), "ASC");
33899                 break;
33900             case "desc":
33901                 ds.sort(cm.getDataIndex(index), "DESC");
33902                 break;
33903             case "lock":
33904                 var lc = cm.getLockedCount();
33905                 if(cm.getColumnCount(true) <= lc+1){
33906                     this.onDenyColumnLock();
33907                     return;
33908                 }
33909                 if(lc != index){
33910                     cm.setLocked(index, true, true);
33911                     cm.moveColumn(index, lc);
33912                     this.grid.fireEvent("columnmove", index, lc);
33913                 }else{
33914                     cm.setLocked(index, true);
33915                 }
33916             break;
33917             case "unlock":
33918                 var lc = cm.getLockedCount();
33919                 if((lc-1) != index){
33920                     cm.setLocked(index, false, true);
33921                     cm.moveColumn(index, lc-1);
33922                     this.grid.fireEvent("columnmove", index, lc-1);
33923                 }else{
33924                     cm.setLocked(index, false);
33925                 }
33926             break;
33927             default:
33928                 index = cm.getIndexById(item.id.substr(4));
33929                 if(index != -1){
33930                     if(item.checked && cm.getColumnCount(true) <= 1){
33931                         this.onDenyColumnHide();
33932                         return false;
33933                     }
33934                     cm.setHidden(index, item.checked);
33935                 }
33936         }
33937         return true;
33938     },
33939
33940     beforeColMenuShow : function(){
33941         var cm = this.cm,  colCount = cm.getColumnCount();
33942         this.colMenu.removeAll();
33943         for(var i = 0; i < colCount; i++){
33944             this.colMenu.add(new Roo.menu.CheckItem({
33945                 id: "col-"+cm.getColumnId(i),
33946                 text: cm.getColumnHeader(i),
33947                 checked: !cm.isHidden(i),
33948                 hideOnClick:false
33949             }));
33950         }
33951     },
33952
33953     handleHdCtx : function(g, index, e){
33954         e.stopEvent();
33955         var hd = this.getHeaderCell(index);
33956         this.hdCtxIndex = index;
33957         var ms = this.hmenu.items, cm = this.cm;
33958         ms.get("asc").setDisabled(!cm.isSortable(index));
33959         ms.get("desc").setDisabled(!cm.isSortable(index));
33960         if(this.grid.enableColLock !== false){
33961             ms.get("lock").setDisabled(cm.isLocked(index));
33962             ms.get("unlock").setDisabled(!cm.isLocked(index));
33963         }
33964         this.hmenu.show(hd, "tl-bl");
33965     },
33966
33967     handleHdOver : function(e){
33968         var hd = this.findHeaderCell(e.getTarget());
33969         if(hd && !this.headersDisabled){
33970             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33971                this.fly(hd).addClass("x-grid-hd-over");
33972             }
33973         }
33974     },
33975
33976     handleHdOut : function(e){
33977         var hd = this.findHeaderCell(e.getTarget());
33978         if(hd){
33979             this.fly(hd).removeClass("x-grid-hd-over");
33980         }
33981     },
33982
33983     handleSplitDblClick : function(e, t){
33984         var i = this.getCellIndex(t);
33985         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33986             this.autoSizeColumn(i, true);
33987             this.layout();
33988         }
33989     },
33990
33991     render : function(){
33992
33993         var cm = this.cm;
33994         var colCount = cm.getColumnCount();
33995
33996         if(this.grid.monitorWindowResize === true){
33997             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33998         }
33999         var header = this.renderHeaders();
34000         var body = this.templates.body.apply({rows:""});
34001         var html = this.templates.master.apply({
34002             lockedBody: body,
34003             body: body,
34004             lockedHeader: header[0],
34005             header: header[1]
34006         });
34007
34008         //this.updateColumns();
34009
34010         this.grid.getGridEl().dom.innerHTML = html;
34011
34012         this.initElements();
34013         
34014         // a kludge to fix the random scolling effect in webkit
34015         this.el.on("scroll", function() {
34016             this.el.dom.scrollTop=0; // hopefully not recursive..
34017         },this);
34018
34019         this.scroller.on("scroll", this.handleScroll, this);
34020         this.lockedBody.on("mousewheel", this.handleWheel, this);
34021         this.mainBody.on("mousewheel", this.handleWheel, this);
34022
34023         this.mainHd.on("mouseover", this.handleHdOver, this);
34024         this.mainHd.on("mouseout", this.handleHdOut, this);
34025         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34026                 {delegate: "."+this.splitClass});
34027
34028         this.lockedHd.on("mouseover", this.handleHdOver, this);
34029         this.lockedHd.on("mouseout", this.handleHdOut, this);
34030         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34031                 {delegate: "."+this.splitClass});
34032
34033         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34034             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34035         }
34036
34037         this.updateSplitters();
34038
34039         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34040             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34041             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34042         }
34043
34044         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34045             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34046             this.hmenu.add(
34047                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34048                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34049             );
34050             if(this.grid.enableColLock !== false){
34051                 this.hmenu.add('-',
34052                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34053                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34054                 );
34055             }
34056             if(this.grid.enableColumnHide !== false){
34057
34058                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34059                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34060                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34061
34062                 this.hmenu.add('-',
34063                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34064                 );
34065             }
34066             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34067
34068             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34069         }
34070
34071         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34072             this.dd = new Roo.grid.GridDragZone(this.grid, {
34073                 ddGroup : this.grid.ddGroup || 'GridDD'
34074             });
34075         }
34076
34077         /*
34078         for(var i = 0; i < colCount; i++){
34079             if(cm.isHidden(i)){
34080                 this.hideColumn(i);
34081             }
34082             if(cm.config[i].align){
34083                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34084                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34085             }
34086         }*/
34087         
34088         this.updateHeaderSortState();
34089
34090         this.beforeInitialResize();
34091         this.layout(true);
34092
34093         // two part rendering gives faster view to the user
34094         this.renderPhase2.defer(1, this);
34095     },
34096
34097     renderPhase2 : function(){
34098         // render the rows now
34099         this.refresh();
34100         if(this.grid.autoSizeColumns){
34101             this.autoSizeColumns();
34102         }
34103     },
34104
34105     beforeInitialResize : function(){
34106
34107     },
34108
34109     onColumnSplitterMoved : function(i, w){
34110         this.userResized = true;
34111         var cm = this.grid.colModel;
34112         cm.setColumnWidth(i, w, true);
34113         var cid = cm.getColumnId(i);
34114         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34115         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34116         this.updateSplitters();
34117         this.layout();
34118         this.grid.fireEvent("columnresize", i, w);
34119     },
34120
34121     syncRowHeights : function(startIndex, endIndex){
34122         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34123             startIndex = startIndex || 0;
34124             var mrows = this.getBodyTable().rows;
34125             var lrows = this.getLockedTable().rows;
34126             var len = mrows.length-1;
34127             endIndex = Math.min(endIndex || len, len);
34128             for(var i = startIndex; i <= endIndex; i++){
34129                 var m = mrows[i], l = lrows[i];
34130                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34131                 m.style.height = l.style.height = h + "px";
34132             }
34133         }
34134     },
34135
34136     layout : function(initialRender, is2ndPass){
34137         var g = this.grid;
34138         var auto = g.autoHeight;
34139         var scrollOffset = 16;
34140         var c = g.getGridEl(), cm = this.cm,
34141                 expandCol = g.autoExpandColumn,
34142                 gv = this;
34143         //c.beginMeasure();
34144
34145         if(!c.dom.offsetWidth){ // display:none?
34146             if(initialRender){
34147                 this.lockedWrap.show();
34148                 this.mainWrap.show();
34149             }
34150             return;
34151         }
34152
34153         var hasLock = this.cm.isLocked(0);
34154
34155         var tbh = this.headerPanel.getHeight();
34156         var bbh = this.footerPanel.getHeight();
34157
34158         if(auto){
34159             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34160             var newHeight = ch + c.getBorderWidth("tb");
34161             if(g.maxHeight){
34162                 newHeight = Math.min(g.maxHeight, newHeight);
34163             }
34164             c.setHeight(newHeight);
34165         }
34166
34167         if(g.autoWidth){
34168             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34169         }
34170
34171         var s = this.scroller;
34172
34173         var csize = c.getSize(true);
34174
34175         this.el.setSize(csize.width, csize.height);
34176
34177         this.headerPanel.setWidth(csize.width);
34178         this.footerPanel.setWidth(csize.width);
34179
34180         var hdHeight = this.mainHd.getHeight();
34181         var vw = csize.width;
34182         var vh = csize.height - (tbh + bbh);
34183
34184         s.setSize(vw, vh);
34185
34186         var bt = this.getBodyTable();
34187         var ltWidth = hasLock ?
34188                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34189
34190         var scrollHeight = bt.offsetHeight;
34191         var scrollWidth = ltWidth + bt.offsetWidth;
34192         var vscroll = false, hscroll = false;
34193
34194         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34195
34196         var lw = this.lockedWrap, mw = this.mainWrap;
34197         var lb = this.lockedBody, mb = this.mainBody;
34198
34199         setTimeout(function(){
34200             var t = s.dom.offsetTop;
34201             var w = s.dom.clientWidth,
34202                 h = s.dom.clientHeight;
34203
34204             lw.setTop(t);
34205             lw.setSize(ltWidth, h);
34206
34207             mw.setLeftTop(ltWidth, t);
34208             mw.setSize(w-ltWidth, h);
34209
34210             lb.setHeight(h-hdHeight);
34211             mb.setHeight(h-hdHeight);
34212
34213             if(is2ndPass !== true && !gv.userResized && expandCol){
34214                 // high speed resize without full column calculation
34215                 
34216                 var ci = cm.getIndexById(expandCol);
34217                 if (ci < 0) {
34218                     ci = cm.findColumnIndex(expandCol);
34219                 }
34220                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34221                 var expandId = cm.getColumnId(ci);
34222                 var  tw = cm.getTotalWidth(false);
34223                 var currentWidth = cm.getColumnWidth(ci);
34224                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34225                 if(currentWidth != cw){
34226                     cm.setColumnWidth(ci, cw, true);
34227                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34228                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34229                     gv.updateSplitters();
34230                     gv.layout(false, true);
34231                 }
34232             }
34233
34234             if(initialRender){
34235                 lw.show();
34236                 mw.show();
34237             }
34238             //c.endMeasure();
34239         }, 10);
34240     },
34241
34242     onWindowResize : function(){
34243         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34244             return;
34245         }
34246         this.layout();
34247     },
34248
34249     appendFooter : function(parentEl){
34250         return null;
34251     },
34252
34253     sortAscText : "Sort Ascending",
34254     sortDescText : "Sort Descending",
34255     lockText : "Lock Column",
34256     unlockText : "Unlock Column",
34257     columnsText : "Columns"
34258 });
34259
34260
34261 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34262     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34263     this.proxy.el.addClass('x-grid3-col-dd');
34264 };
34265
34266 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34267     handleMouseDown : function(e){
34268
34269     },
34270
34271     callHandleMouseDown : function(e){
34272         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34273     }
34274 });
34275 /*
34276  * Based on:
34277  * Ext JS Library 1.1.1
34278  * Copyright(c) 2006-2007, Ext JS, LLC.
34279  *
34280  * Originally Released Under LGPL - original licence link has changed is not relivant.
34281  *
34282  * Fork - LGPL
34283  * <script type="text/javascript">
34284  */
34285  
34286 // private
34287 // This is a support class used internally by the Grid components
34288 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34289     this.grid = grid;
34290     this.view = grid.getView();
34291     this.proxy = this.view.resizeProxy;
34292     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34293         "gridSplitters" + this.grid.getGridEl().id, {
34294         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34295     });
34296     this.setHandleElId(Roo.id(hd));
34297     this.setOuterHandleElId(Roo.id(hd2));
34298     this.scroll = false;
34299 };
34300 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34301     fly: Roo.Element.fly,
34302
34303     b4StartDrag : function(x, y){
34304         this.view.headersDisabled = true;
34305         this.proxy.setHeight(this.view.mainWrap.getHeight());
34306         var w = this.cm.getColumnWidth(this.cellIndex);
34307         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34308         this.resetConstraints();
34309         this.setXConstraint(minw, 1000);
34310         this.setYConstraint(0, 0);
34311         this.minX = x - minw;
34312         this.maxX = x + 1000;
34313         this.startPos = x;
34314         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34315     },
34316
34317
34318     handleMouseDown : function(e){
34319         ev = Roo.EventObject.setEvent(e);
34320         var t = this.fly(ev.getTarget());
34321         if(t.hasClass("x-grid-split")){
34322             this.cellIndex = this.view.getCellIndex(t.dom);
34323             this.split = t.dom;
34324             this.cm = this.grid.colModel;
34325             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34326                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34327             }
34328         }
34329     },
34330
34331     endDrag : function(e){
34332         this.view.headersDisabled = false;
34333         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34334         var diff = endX - this.startPos;
34335         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34336     },
34337
34338     autoOffset : function(){
34339         this.setDelta(0,0);
34340     }
34341 });/*
34342  * Based on:
34343  * Ext JS Library 1.1.1
34344  * Copyright(c) 2006-2007, Ext JS, LLC.
34345  *
34346  * Originally Released Under LGPL - original licence link has changed is not relivant.
34347  *
34348  * Fork - LGPL
34349  * <script type="text/javascript">
34350  */
34351  
34352 // private
34353 // This is a support class used internally by the Grid components
34354 Roo.grid.GridDragZone = function(grid, config){
34355     this.view = grid.getView();
34356     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34357     if(this.view.lockedBody){
34358         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34359         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34360     }
34361     this.scroll = false;
34362     this.grid = grid;
34363     this.ddel = document.createElement('div');
34364     this.ddel.className = 'x-grid-dd-wrap';
34365 };
34366
34367 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34368     ddGroup : "GridDD",
34369
34370     getDragData : function(e){
34371         var t = Roo.lib.Event.getTarget(e);
34372         var rowIndex = this.view.findRowIndex(t);
34373         if(rowIndex !== false){
34374             var sm = this.grid.selModel;
34375             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34376               //  sm.mouseDown(e, t);
34377             //}
34378             if (e.hasModifier()){
34379                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34380             }
34381             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34382         }
34383         return false;
34384     },
34385
34386     onInitDrag : function(e){
34387         var data = this.dragData;
34388         this.ddel.innerHTML = this.grid.getDragDropText();
34389         this.proxy.update(this.ddel);
34390         // fire start drag?
34391     },
34392
34393     afterRepair : function(){
34394         this.dragging = false;
34395     },
34396
34397     getRepairXY : function(e, data){
34398         return false;
34399     },
34400
34401     onEndDrag : function(data, e){
34402         // fire end drag?
34403     },
34404
34405     onValidDrop : function(dd, e, id){
34406         // fire drag drop?
34407         this.hideProxy();
34408     },
34409
34410     beforeInvalidDrop : function(e, id){
34411
34412     }
34413 });/*
34414  * Based on:
34415  * Ext JS Library 1.1.1
34416  * Copyright(c) 2006-2007, Ext JS, LLC.
34417  *
34418  * Originally Released Under LGPL - original licence link has changed is not relivant.
34419  *
34420  * Fork - LGPL
34421  * <script type="text/javascript">
34422  */
34423  
34424
34425 /**
34426  * @class Roo.grid.ColumnModel
34427  * @extends Roo.util.Observable
34428  * This is the default implementation of a ColumnModel used by the Grid. It defines
34429  * the columns in the grid.
34430  * <br>Usage:<br>
34431  <pre><code>
34432  var colModel = new Roo.grid.ColumnModel([
34433         {header: "Ticker", width: 60, sortable: true, locked: true},
34434         {header: "Company Name", width: 150, sortable: true},
34435         {header: "Market Cap.", width: 100, sortable: true},
34436         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34437         {header: "Employees", width: 100, sortable: true, resizable: false}
34438  ]);
34439  </code></pre>
34440  * <p>
34441  
34442  * The config options listed for this class are options which may appear in each
34443  * individual column definition.
34444  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34445  * @constructor
34446  * @param {Object} config An Array of column config objects. See this class's
34447  * config objects for details.
34448 */
34449 Roo.grid.ColumnModel = function(config){
34450         /**
34451      * The config passed into the constructor
34452      */
34453     this.config = config;
34454     this.lookup = {};
34455
34456     // if no id, create one
34457     // if the column does not have a dataIndex mapping,
34458     // map it to the order it is in the config
34459     for(var i = 0, len = config.length; i < len; i++){
34460         var c = config[i];
34461         if(typeof c.dataIndex == "undefined"){
34462             c.dataIndex = i;
34463         }
34464         if(typeof c.renderer == "string"){
34465             c.renderer = Roo.util.Format[c.renderer];
34466         }
34467         if(typeof c.id == "undefined"){
34468             c.id = Roo.id();
34469         }
34470         if(c.editor && c.editor.xtype){
34471             c.editor  = Roo.factory(c.editor, Roo.grid);
34472         }
34473         if(c.editor && c.editor.isFormField){
34474             c.editor = new Roo.grid.GridEditor(c.editor);
34475         }
34476         this.lookup[c.id] = c;
34477     }
34478
34479     /**
34480      * The width of columns which have no width specified (defaults to 100)
34481      * @type Number
34482      */
34483     this.defaultWidth = 100;
34484
34485     /**
34486      * Default sortable of columns which have no sortable specified (defaults to false)
34487      * @type Boolean
34488      */
34489     this.defaultSortable = false;
34490
34491     this.addEvents({
34492         /**
34493              * @event widthchange
34494              * Fires when the width of a column changes.
34495              * @param {ColumnModel} this
34496              * @param {Number} columnIndex The column index
34497              * @param {Number} newWidth The new width
34498              */
34499             "widthchange": true,
34500         /**
34501              * @event headerchange
34502              * Fires when the text of a header changes.
34503              * @param {ColumnModel} this
34504              * @param {Number} columnIndex The column index
34505              * @param {Number} newText The new header text
34506              */
34507             "headerchange": true,
34508         /**
34509              * @event hiddenchange
34510              * Fires when a column is hidden or "unhidden".
34511              * @param {ColumnModel} this
34512              * @param {Number} columnIndex The column index
34513              * @param {Boolean} hidden true if hidden, false otherwise
34514              */
34515             "hiddenchange": true,
34516             /**
34517          * @event columnmoved
34518          * Fires when a column is moved.
34519          * @param {ColumnModel} this
34520          * @param {Number} oldIndex
34521          * @param {Number} newIndex
34522          */
34523         "columnmoved" : true,
34524         /**
34525          * @event columlockchange
34526          * Fires when a column's locked state is changed
34527          * @param {ColumnModel} this
34528          * @param {Number} colIndex
34529          * @param {Boolean} locked true if locked
34530          */
34531         "columnlockchange" : true
34532     });
34533     Roo.grid.ColumnModel.superclass.constructor.call(this);
34534 };
34535 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34536     /**
34537      * @cfg {String} header The header text to display in the Grid view.
34538      */
34539     /**
34540      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34541      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34542      * specified, the column's index is used as an index into the Record's data Array.
34543      */
34544     /**
34545      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34546      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34547      */
34548     /**
34549      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34550      * Defaults to the value of the {@link #defaultSortable} property.
34551      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34552      */
34553     /**
34554      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34555      */
34556     /**
34557      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34558      */
34559     /**
34560      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34561      */
34562     /**
34563      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34564      */
34565     /**
34566      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34567      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34568      * default renderer uses the raw data value.
34569      */
34570        /**
34571      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34572      */
34573     /**
34574      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34575      */
34576
34577     /**
34578      * Returns the id of the column at the specified index.
34579      * @param {Number} index The column index
34580      * @return {String} the id
34581      */
34582     getColumnId : function(index){
34583         return this.config[index].id;
34584     },
34585
34586     /**
34587      * Returns the column for a specified id.
34588      * @param {String} id The column id
34589      * @return {Object} the column
34590      */
34591     getColumnById : function(id){
34592         return this.lookup[id];
34593     },
34594
34595     
34596     /**
34597      * Returns the column for a specified dataIndex.
34598      * @param {String} dataIndex The column dataIndex
34599      * @return {Object|Boolean} the column or false if not found
34600      */
34601     getColumnByDataIndex: function(dataIndex){
34602         var index = this.findColumnIndex(dataIndex);
34603         return index > -1 ? this.config[index] : false;
34604     },
34605     
34606     /**
34607      * Returns the index for a specified column id.
34608      * @param {String} id The column id
34609      * @return {Number} the index, or -1 if not found
34610      */
34611     getIndexById : function(id){
34612         for(var i = 0, len = this.config.length; i < len; i++){
34613             if(this.config[i].id == id){
34614                 return i;
34615             }
34616         }
34617         return -1;
34618     },
34619     
34620     /**
34621      * Returns the index for a specified column dataIndex.
34622      * @param {String} dataIndex The column dataIndex
34623      * @return {Number} the index, or -1 if not found
34624      */
34625     
34626     findColumnIndex : function(dataIndex){
34627         for(var i = 0, len = this.config.length; i < len; i++){
34628             if(this.config[i].dataIndex == dataIndex){
34629                 return i;
34630             }
34631         }
34632         return -1;
34633     },
34634     
34635     
34636     moveColumn : function(oldIndex, newIndex){
34637         var c = this.config[oldIndex];
34638         this.config.splice(oldIndex, 1);
34639         this.config.splice(newIndex, 0, c);
34640         this.dataMap = null;
34641         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34642     },
34643
34644     isLocked : function(colIndex){
34645         return this.config[colIndex].locked === true;
34646     },
34647
34648     setLocked : function(colIndex, value, suppressEvent){
34649         if(this.isLocked(colIndex) == value){
34650             return;
34651         }
34652         this.config[colIndex].locked = value;
34653         if(!suppressEvent){
34654             this.fireEvent("columnlockchange", this, colIndex, value);
34655         }
34656     },
34657
34658     getTotalLockedWidth : function(){
34659         var totalWidth = 0;
34660         for(var i = 0; i < this.config.length; i++){
34661             if(this.isLocked(i) && !this.isHidden(i)){
34662                 this.totalWidth += this.getColumnWidth(i);
34663             }
34664         }
34665         return totalWidth;
34666     },
34667
34668     getLockedCount : function(){
34669         for(var i = 0, len = this.config.length; i < len; i++){
34670             if(!this.isLocked(i)){
34671                 return i;
34672             }
34673         }
34674     },
34675
34676     /**
34677      * Returns the number of columns.
34678      * @return {Number}
34679      */
34680     getColumnCount : function(visibleOnly){
34681         if(visibleOnly === true){
34682             var c = 0;
34683             for(var i = 0, len = this.config.length; i < len; i++){
34684                 if(!this.isHidden(i)){
34685                     c++;
34686                 }
34687             }
34688             return c;
34689         }
34690         return this.config.length;
34691     },
34692
34693     /**
34694      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34695      * @param {Function} fn
34696      * @param {Object} scope (optional)
34697      * @return {Array} result
34698      */
34699     getColumnsBy : function(fn, scope){
34700         var r = [];
34701         for(var i = 0, len = this.config.length; i < len; i++){
34702             var c = this.config[i];
34703             if(fn.call(scope||this, c, i) === true){
34704                 r[r.length] = c;
34705             }
34706         }
34707         return r;
34708     },
34709
34710     /**
34711      * Returns true if the specified column is sortable.
34712      * @param {Number} col The column index
34713      * @return {Boolean}
34714      */
34715     isSortable : function(col){
34716         if(typeof this.config[col].sortable == "undefined"){
34717             return this.defaultSortable;
34718         }
34719         return this.config[col].sortable;
34720     },
34721
34722     /**
34723      * Returns the rendering (formatting) function defined for the column.
34724      * @param {Number} col The column index.
34725      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34726      */
34727     getRenderer : function(col){
34728         if(!this.config[col].renderer){
34729             return Roo.grid.ColumnModel.defaultRenderer;
34730         }
34731         return this.config[col].renderer;
34732     },
34733
34734     /**
34735      * Sets the rendering (formatting) function for a column.
34736      * @param {Number} col The column index
34737      * @param {Function} fn The function to use to process the cell's raw data
34738      * to return HTML markup for the grid view. The render function is called with
34739      * the following parameters:<ul>
34740      * <li>Data value.</li>
34741      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34742      * <li>css A CSS style string to apply to the table cell.</li>
34743      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34744      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34745      * <li>Row index</li>
34746      * <li>Column index</li>
34747      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34748      */
34749     setRenderer : function(col, fn){
34750         this.config[col].renderer = fn;
34751     },
34752
34753     /**
34754      * Returns the width for the specified column.
34755      * @param {Number} col The column index
34756      * @return {Number}
34757      */
34758     getColumnWidth : function(col){
34759         return this.config[col].width || this.defaultWidth;
34760     },
34761
34762     /**
34763      * Sets the width for a column.
34764      * @param {Number} col The column index
34765      * @param {Number} width The new width
34766      */
34767     setColumnWidth : function(col, width, suppressEvent){
34768         this.config[col].width = width;
34769         this.totalWidth = null;
34770         if(!suppressEvent){
34771              this.fireEvent("widthchange", this, col, width);
34772         }
34773     },
34774
34775     /**
34776      * Returns the total width of all columns.
34777      * @param {Boolean} includeHidden True to include hidden column widths
34778      * @return {Number}
34779      */
34780     getTotalWidth : function(includeHidden){
34781         if(!this.totalWidth){
34782             this.totalWidth = 0;
34783             for(var i = 0, len = this.config.length; i < len; i++){
34784                 if(includeHidden || !this.isHidden(i)){
34785                     this.totalWidth += this.getColumnWidth(i);
34786                 }
34787             }
34788         }
34789         return this.totalWidth;
34790     },
34791
34792     /**
34793      * Returns the header for the specified column.
34794      * @param {Number} col The column index
34795      * @return {String}
34796      */
34797     getColumnHeader : function(col){
34798         return this.config[col].header;
34799     },
34800
34801     /**
34802      * Sets the header for a column.
34803      * @param {Number} col The column index
34804      * @param {String} header The new header
34805      */
34806     setColumnHeader : function(col, header){
34807         this.config[col].header = header;
34808         this.fireEvent("headerchange", this, col, header);
34809     },
34810
34811     /**
34812      * Returns the tooltip for the specified column.
34813      * @param {Number} col The column index
34814      * @return {String}
34815      */
34816     getColumnTooltip : function(col){
34817             return this.config[col].tooltip;
34818     },
34819     /**
34820      * Sets the tooltip for a column.
34821      * @param {Number} col The column index
34822      * @param {String} tooltip The new tooltip
34823      */
34824     setColumnTooltip : function(col, tooltip){
34825             this.config[col].tooltip = tooltip;
34826     },
34827
34828     /**
34829      * Returns the dataIndex for the specified column.
34830      * @param {Number} col The column index
34831      * @return {Number}
34832      */
34833     getDataIndex : function(col){
34834         return this.config[col].dataIndex;
34835     },
34836
34837     /**
34838      * Sets the dataIndex for a column.
34839      * @param {Number} col The column index
34840      * @param {Number} dataIndex The new dataIndex
34841      */
34842     setDataIndex : function(col, dataIndex){
34843         this.config[col].dataIndex = dataIndex;
34844     },
34845
34846     
34847     
34848     /**
34849      * Returns true if the cell is editable.
34850      * @param {Number} colIndex The column index
34851      * @param {Number} rowIndex The row index
34852      * @return {Boolean}
34853      */
34854     isCellEditable : function(colIndex, rowIndex){
34855         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34856     },
34857
34858     /**
34859      * Returns the editor defined for the cell/column.
34860      * return false or null to disable editing.
34861      * @param {Number} colIndex The column index
34862      * @param {Number} rowIndex The row index
34863      * @return {Object}
34864      */
34865     getCellEditor : function(colIndex, rowIndex){
34866         return this.config[colIndex].editor;
34867     },
34868
34869     /**
34870      * Sets if a column is editable.
34871      * @param {Number} col The column index
34872      * @param {Boolean} editable True if the column is editable
34873      */
34874     setEditable : function(col, editable){
34875         this.config[col].editable = editable;
34876     },
34877
34878
34879     /**
34880      * Returns true if the column is hidden.
34881      * @param {Number} colIndex The column index
34882      * @return {Boolean}
34883      */
34884     isHidden : function(colIndex){
34885         return this.config[colIndex].hidden;
34886     },
34887
34888
34889     /**
34890      * Returns true if the column width cannot be changed
34891      */
34892     isFixed : function(colIndex){
34893         return this.config[colIndex].fixed;
34894     },
34895
34896     /**
34897      * Returns true if the column can be resized
34898      * @return {Boolean}
34899      */
34900     isResizable : function(colIndex){
34901         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34902     },
34903     /**
34904      * Sets if a column is hidden.
34905      * @param {Number} colIndex The column index
34906      * @param {Boolean} hidden True if the column is hidden
34907      */
34908     setHidden : function(colIndex, hidden){
34909         this.config[colIndex].hidden = hidden;
34910         this.totalWidth = null;
34911         this.fireEvent("hiddenchange", this, colIndex, hidden);
34912     },
34913
34914     /**
34915      * Sets the editor for a column.
34916      * @param {Number} col The column index
34917      * @param {Object} editor The editor object
34918      */
34919     setEditor : function(col, editor){
34920         this.config[col].editor = editor;
34921     }
34922 });
34923
34924 Roo.grid.ColumnModel.defaultRenderer = function(value){
34925         if(typeof value == "string" && value.length < 1){
34926             return "&#160;";
34927         }
34928         return value;
34929 };
34930
34931 // Alias for backwards compatibility
34932 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34933 /*
34934  * Based on:
34935  * Ext JS Library 1.1.1
34936  * Copyright(c) 2006-2007, Ext JS, LLC.
34937  *
34938  * Originally Released Under LGPL - original licence link has changed is not relivant.
34939  *
34940  * Fork - LGPL
34941  * <script type="text/javascript">
34942  */
34943
34944 /**
34945  * @class Roo.grid.AbstractSelectionModel
34946  * @extends Roo.util.Observable
34947  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34948  * implemented by descendant classes.  This class should not be directly instantiated.
34949  * @constructor
34950  */
34951 Roo.grid.AbstractSelectionModel = function(){
34952     this.locked = false;
34953     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34954 };
34955
34956 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34957     /** @ignore Called by the grid automatically. Do not call directly. */
34958     init : function(grid){
34959         this.grid = grid;
34960         this.initEvents();
34961     },
34962
34963     /**
34964      * Locks the selections.
34965      */
34966     lock : function(){
34967         this.locked = true;
34968     },
34969
34970     /**
34971      * Unlocks the selections.
34972      */
34973     unlock : function(){
34974         this.locked = false;
34975     },
34976
34977     /**
34978      * Returns true if the selections are locked.
34979      * @return {Boolean}
34980      */
34981     isLocked : function(){
34982         return this.locked;
34983     }
34984 });/*
34985  * Based on:
34986  * Ext JS Library 1.1.1
34987  * Copyright(c) 2006-2007, Ext JS, LLC.
34988  *
34989  * Originally Released Under LGPL - original licence link has changed is not relivant.
34990  *
34991  * Fork - LGPL
34992  * <script type="text/javascript">
34993  */
34994 /**
34995  * @extends Roo.grid.AbstractSelectionModel
34996  * @class Roo.grid.RowSelectionModel
34997  * The default SelectionModel used by {@link Roo.grid.Grid}.
34998  * It supports multiple selections and keyboard selection/navigation. 
34999  * @constructor
35000  * @param {Object} config
35001  */
35002 Roo.grid.RowSelectionModel = function(config){
35003     Roo.apply(this, config);
35004     this.selections = new Roo.util.MixedCollection(false, function(o){
35005         return o.id;
35006     });
35007
35008     this.last = false;
35009     this.lastActive = false;
35010
35011     this.addEvents({
35012         /**
35013              * @event selectionchange
35014              * Fires when the selection changes
35015              * @param {SelectionModel} this
35016              */
35017             "selectionchange" : true,
35018         /**
35019              * @event afterselectionchange
35020              * Fires after the selection changes (eg. by key press or clicking)
35021              * @param {SelectionModel} this
35022              */
35023             "afterselectionchange" : true,
35024         /**
35025              * @event beforerowselect
35026              * Fires when a row is selected being selected, return false to cancel.
35027              * @param {SelectionModel} this
35028              * @param {Number} rowIndex The selected index
35029              * @param {Boolean} keepExisting False if other selections will be cleared
35030              */
35031             "beforerowselect" : true,
35032         /**
35033              * @event rowselect
35034              * Fires when a row is selected.
35035              * @param {SelectionModel} this
35036              * @param {Number} rowIndex The selected index
35037              * @param {Roo.data.Record} r The record
35038              */
35039             "rowselect" : true,
35040         /**
35041              * @event rowdeselect
35042              * Fires when a row is deselected.
35043              * @param {SelectionModel} this
35044              * @param {Number} rowIndex The selected index
35045              */
35046         "rowdeselect" : true
35047     });
35048     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35049     this.locked = false;
35050 };
35051
35052 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35053     /**
35054      * @cfg {Boolean} singleSelect
35055      * True to allow selection of only one row at a time (defaults to false)
35056      */
35057     singleSelect : false,
35058
35059     // private
35060     initEvents : function(){
35061
35062         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35063             this.grid.on("mousedown", this.handleMouseDown, this);
35064         }else{ // allow click to work like normal
35065             this.grid.on("rowclick", this.handleDragableRowClick, this);
35066         }
35067
35068         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35069             "up" : function(e){
35070                 if(!e.shiftKey){
35071                     this.selectPrevious(e.shiftKey);
35072                 }else if(this.last !== false && this.lastActive !== false){
35073                     var last = this.last;
35074                     this.selectRange(this.last,  this.lastActive-1);
35075                     this.grid.getView().focusRow(this.lastActive);
35076                     if(last !== false){
35077                         this.last = last;
35078                     }
35079                 }else{
35080                     this.selectFirstRow();
35081                 }
35082                 this.fireEvent("afterselectionchange", this);
35083             },
35084             "down" : function(e){
35085                 if(!e.shiftKey){
35086                     this.selectNext(e.shiftKey);
35087                 }else if(this.last !== false && this.lastActive !== false){
35088                     var last = this.last;
35089                     this.selectRange(this.last,  this.lastActive+1);
35090                     this.grid.getView().focusRow(this.lastActive);
35091                     if(last !== false){
35092                         this.last = last;
35093                     }
35094                 }else{
35095                     this.selectFirstRow();
35096                 }
35097                 this.fireEvent("afterselectionchange", this);
35098             },
35099             scope: this
35100         });
35101
35102         var view = this.grid.view;
35103         view.on("refresh", this.onRefresh, this);
35104         view.on("rowupdated", this.onRowUpdated, this);
35105         view.on("rowremoved", this.onRemove, this);
35106     },
35107
35108     // private
35109     onRefresh : function(){
35110         var ds = this.grid.dataSource, i, v = this.grid.view;
35111         var s = this.selections;
35112         s.each(function(r){
35113             if((i = ds.indexOfId(r.id)) != -1){
35114                 v.onRowSelect(i);
35115             }else{
35116                 s.remove(r);
35117             }
35118         });
35119     },
35120
35121     // private
35122     onRemove : function(v, index, r){
35123         this.selections.remove(r);
35124     },
35125
35126     // private
35127     onRowUpdated : function(v, index, r){
35128         if(this.isSelected(r)){
35129             v.onRowSelect(index);
35130         }
35131     },
35132
35133     /**
35134      * Select records.
35135      * @param {Array} records The records to select
35136      * @param {Boolean} keepExisting (optional) True to keep existing selections
35137      */
35138     selectRecords : function(records, keepExisting){
35139         if(!keepExisting){
35140             this.clearSelections();
35141         }
35142         var ds = this.grid.dataSource;
35143         for(var i = 0, len = records.length; i < len; i++){
35144             this.selectRow(ds.indexOf(records[i]), true);
35145         }
35146     },
35147
35148     /**
35149      * Gets the number of selected rows.
35150      * @return {Number}
35151      */
35152     getCount : function(){
35153         return this.selections.length;
35154     },
35155
35156     /**
35157      * Selects the first row in the grid.
35158      */
35159     selectFirstRow : function(){
35160         this.selectRow(0);
35161     },
35162
35163     /**
35164      * Select the last row.
35165      * @param {Boolean} keepExisting (optional) True to keep existing selections
35166      */
35167     selectLastRow : function(keepExisting){
35168         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35169     },
35170
35171     /**
35172      * Selects the row immediately following the last selected row.
35173      * @param {Boolean} keepExisting (optional) True to keep existing selections
35174      */
35175     selectNext : function(keepExisting){
35176         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35177             this.selectRow(this.last+1, keepExisting);
35178             this.grid.getView().focusRow(this.last);
35179         }
35180     },
35181
35182     /**
35183      * Selects the row that precedes the last selected row.
35184      * @param {Boolean} keepExisting (optional) True to keep existing selections
35185      */
35186     selectPrevious : function(keepExisting){
35187         if(this.last){
35188             this.selectRow(this.last-1, keepExisting);
35189             this.grid.getView().focusRow(this.last);
35190         }
35191     },
35192
35193     /**
35194      * Returns the selected records
35195      * @return {Array} Array of selected records
35196      */
35197     getSelections : function(){
35198         return [].concat(this.selections.items);
35199     },
35200
35201     /**
35202      * Returns the first selected record.
35203      * @return {Record}
35204      */
35205     getSelected : function(){
35206         return this.selections.itemAt(0);
35207     },
35208
35209
35210     /**
35211      * Clears all selections.
35212      */
35213     clearSelections : function(fast){
35214         if(this.locked) return;
35215         if(fast !== true){
35216             var ds = this.grid.dataSource;
35217             var s = this.selections;
35218             s.each(function(r){
35219                 this.deselectRow(ds.indexOfId(r.id));
35220             }, this);
35221             s.clear();
35222         }else{
35223             this.selections.clear();
35224         }
35225         this.last = false;
35226     },
35227
35228
35229     /**
35230      * Selects all rows.
35231      */
35232     selectAll : function(){
35233         if(this.locked) return;
35234         this.selections.clear();
35235         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35236             this.selectRow(i, true);
35237         }
35238     },
35239
35240     /**
35241      * Returns True if there is a selection.
35242      * @return {Boolean}
35243      */
35244     hasSelection : function(){
35245         return this.selections.length > 0;
35246     },
35247
35248     /**
35249      * Returns True if the specified row is selected.
35250      * @param {Number/Record} record The record or index of the record to check
35251      * @return {Boolean}
35252      */
35253     isSelected : function(index){
35254         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35255         return (r && this.selections.key(r.id) ? true : false);
35256     },
35257
35258     /**
35259      * Returns True if the specified record id is selected.
35260      * @param {String} id The id of record to check
35261      * @return {Boolean}
35262      */
35263     isIdSelected : function(id){
35264         return (this.selections.key(id) ? true : false);
35265     },
35266
35267     // private
35268     handleMouseDown : function(e, t){
35269         var view = this.grid.getView(), rowIndex;
35270         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35271             return;
35272         };
35273         if(e.shiftKey && this.last !== false){
35274             var last = this.last;
35275             this.selectRange(last, rowIndex, e.ctrlKey);
35276             this.last = last; // reset the last
35277             view.focusRow(rowIndex);
35278         }else{
35279             var isSelected = this.isSelected(rowIndex);
35280             if(e.button !== 0 && isSelected){
35281                 view.focusRow(rowIndex);
35282             }else if(e.ctrlKey && isSelected){
35283                 this.deselectRow(rowIndex);
35284             }else if(!isSelected){
35285                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35286                 view.focusRow(rowIndex);
35287             }
35288         }
35289         this.fireEvent("afterselectionchange", this);
35290     },
35291     // private
35292     handleDragableRowClick :  function(grid, rowIndex, e) 
35293     {
35294         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35295             this.selectRow(rowIndex, false);
35296             grid.view.focusRow(rowIndex);
35297              this.fireEvent("afterselectionchange", this);
35298         }
35299     },
35300     
35301     /**
35302      * Selects multiple rows.
35303      * @param {Array} rows Array of the indexes of the row to select
35304      * @param {Boolean} keepExisting (optional) True to keep existing selections
35305      */
35306     selectRows : function(rows, keepExisting){
35307         if(!keepExisting){
35308             this.clearSelections();
35309         }
35310         for(var i = 0, len = rows.length; i < len; i++){
35311             this.selectRow(rows[i], true);
35312         }
35313     },
35314
35315     /**
35316      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35317      * @param {Number} startRow The index of the first row in the range
35318      * @param {Number} endRow The index of the last row in the range
35319      * @param {Boolean} keepExisting (optional) True to retain existing selections
35320      */
35321     selectRange : function(startRow, endRow, keepExisting){
35322         if(this.locked) return;
35323         if(!keepExisting){
35324             this.clearSelections();
35325         }
35326         if(startRow <= endRow){
35327             for(var i = startRow; i <= endRow; i++){
35328                 this.selectRow(i, true);
35329             }
35330         }else{
35331             for(var i = startRow; i >= endRow; i--){
35332                 this.selectRow(i, true);
35333             }
35334         }
35335     },
35336
35337     /**
35338      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35339      * @param {Number} startRow The index of the first row in the range
35340      * @param {Number} endRow The index of the last row in the range
35341      */
35342     deselectRange : function(startRow, endRow, preventViewNotify){
35343         if(this.locked) return;
35344         for(var i = startRow; i <= endRow; i++){
35345             this.deselectRow(i, preventViewNotify);
35346         }
35347     },
35348
35349     /**
35350      * Selects a row.
35351      * @param {Number} row The index of the row to select
35352      * @param {Boolean} keepExisting (optional) True to keep existing selections
35353      */
35354     selectRow : function(index, keepExisting, preventViewNotify){
35355         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35356         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35357             if(!keepExisting || this.singleSelect){
35358                 this.clearSelections();
35359             }
35360             var r = this.grid.dataSource.getAt(index);
35361             this.selections.add(r);
35362             this.last = this.lastActive = index;
35363             if(!preventViewNotify){
35364                 this.grid.getView().onRowSelect(index);
35365             }
35366             this.fireEvent("rowselect", this, index, r);
35367             this.fireEvent("selectionchange", this);
35368         }
35369     },
35370
35371     /**
35372      * Deselects a row.
35373      * @param {Number} row The index of the row to deselect
35374      */
35375     deselectRow : function(index, preventViewNotify){
35376         if(this.locked) return;
35377         if(this.last == index){
35378             this.last = false;
35379         }
35380         if(this.lastActive == index){
35381             this.lastActive = false;
35382         }
35383         var r = this.grid.dataSource.getAt(index);
35384         this.selections.remove(r);
35385         if(!preventViewNotify){
35386             this.grid.getView().onRowDeselect(index);
35387         }
35388         this.fireEvent("rowdeselect", this, index);
35389         this.fireEvent("selectionchange", this);
35390     },
35391
35392     // private
35393     restoreLast : function(){
35394         if(this._last){
35395             this.last = this._last;
35396         }
35397     },
35398
35399     // private
35400     acceptsNav : function(row, col, cm){
35401         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35402     },
35403
35404     // private
35405     onEditorKey : function(field, e){
35406         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35407         if(k == e.TAB){
35408             e.stopEvent();
35409             ed.completeEdit();
35410             if(e.shiftKey){
35411                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35412             }else{
35413                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35414             }
35415         }else if(k == e.ENTER && !e.ctrlKey){
35416             e.stopEvent();
35417             ed.completeEdit();
35418             if(e.shiftKey){
35419                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35420             }else{
35421                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35422             }
35423         }else if(k == e.ESC){
35424             ed.cancelEdit();
35425         }
35426         if(newCell){
35427             g.startEditing(newCell[0], newCell[1]);
35428         }
35429     }
35430 });/*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440 /**
35441  * @class Roo.grid.CellSelectionModel
35442  * @extends Roo.grid.AbstractSelectionModel
35443  * This class provides the basic implementation for cell selection in a grid.
35444  * @constructor
35445  * @param {Object} config The object containing the configuration of this model.
35446  */
35447 Roo.grid.CellSelectionModel = function(config){
35448     Roo.apply(this, config);
35449
35450     this.selection = null;
35451
35452     this.addEvents({
35453         /**
35454              * @event beforerowselect
35455              * Fires before a cell is selected.
35456              * @param {SelectionModel} this
35457              * @param {Number} rowIndex The selected row index
35458              * @param {Number} colIndex The selected cell index
35459              */
35460             "beforecellselect" : true,
35461         /**
35462              * @event cellselect
35463              * Fires when a cell is selected.
35464              * @param {SelectionModel} this
35465              * @param {Number} rowIndex The selected row index
35466              * @param {Number} colIndex The selected cell index
35467              */
35468             "cellselect" : true,
35469         /**
35470              * @event selectionchange
35471              * Fires when the active selection changes.
35472              * @param {SelectionModel} this
35473              * @param {Object} selection null for no selection or an object (o) with two properties
35474                 <ul>
35475                 <li>o.record: the record object for the row the selection is in</li>
35476                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35477                 </ul>
35478              */
35479             "selectionchange" : true
35480     });
35481     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35482 };
35483
35484 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35485
35486     /** @ignore */
35487     initEvents : function(){
35488         this.grid.on("mousedown", this.handleMouseDown, this);
35489         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35490         var view = this.grid.view;
35491         view.on("refresh", this.onViewChange, this);
35492         view.on("rowupdated", this.onRowUpdated, this);
35493         view.on("beforerowremoved", this.clearSelections, this);
35494         view.on("beforerowsinserted", this.clearSelections, this);
35495         if(this.grid.isEditor){
35496             this.grid.on("beforeedit", this.beforeEdit,  this);
35497         }
35498     },
35499
35500         //private
35501     beforeEdit : function(e){
35502         this.select(e.row, e.column, false, true, e.record);
35503     },
35504
35505         //private
35506     onRowUpdated : function(v, index, r){
35507         if(this.selection && this.selection.record == r){
35508             v.onCellSelect(index, this.selection.cell[1]);
35509         }
35510     },
35511
35512         //private
35513     onViewChange : function(){
35514         this.clearSelections(true);
35515     },
35516
35517         /**
35518          * Returns the currently selected cell,.
35519          * @return {Array} The selected cell (row, column) or null if none selected.
35520          */
35521     getSelectedCell : function(){
35522         return this.selection ? this.selection.cell : null;
35523     },
35524
35525     /**
35526      * Clears all selections.
35527      * @param {Boolean} true to prevent the gridview from being notified about the change.
35528      */
35529     clearSelections : function(preventNotify){
35530         var s = this.selection;
35531         if(s){
35532             if(preventNotify !== true){
35533                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35534             }
35535             this.selection = null;
35536             this.fireEvent("selectionchange", this, null);
35537         }
35538     },
35539
35540     /**
35541      * Returns true if there is a selection.
35542      * @return {Boolean}
35543      */
35544     hasSelection : function(){
35545         return this.selection ? true : false;
35546     },
35547
35548     /** @ignore */
35549     handleMouseDown : function(e, t){
35550         var v = this.grid.getView();
35551         if(this.isLocked()){
35552             return;
35553         };
35554         var row = v.findRowIndex(t);
35555         var cell = v.findCellIndex(t);
35556         if(row !== false && cell !== false){
35557             this.select(row, cell);
35558         }
35559     },
35560
35561     /**
35562      * Selects a cell.
35563      * @param {Number} rowIndex
35564      * @param {Number} collIndex
35565      */
35566     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35567         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35568             this.clearSelections();
35569             r = r || this.grid.dataSource.getAt(rowIndex);
35570             this.selection = {
35571                 record : r,
35572                 cell : [rowIndex, colIndex]
35573             };
35574             if(!preventViewNotify){
35575                 var v = this.grid.getView();
35576                 v.onCellSelect(rowIndex, colIndex);
35577                 if(preventFocus !== true){
35578                     v.focusCell(rowIndex, colIndex);
35579                 }
35580             }
35581             this.fireEvent("cellselect", this, rowIndex, colIndex);
35582             this.fireEvent("selectionchange", this, this.selection);
35583         }
35584     },
35585
35586         //private
35587     isSelectable : function(rowIndex, colIndex, cm){
35588         return !cm.isHidden(colIndex);
35589     },
35590
35591     /** @ignore */
35592     handleKeyDown : function(e){
35593         Roo.log('Cell Sel Model handleKeyDown');
35594         if(!e.isNavKeyPress()){
35595             return;
35596         }
35597         var g = this.grid, s = this.selection;
35598         if(!s){
35599             e.stopEvent();
35600             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35601             if(cell){
35602                 this.select(cell[0], cell[1]);
35603             }
35604             return;
35605         }
35606         var sm = this;
35607         var walk = function(row, col, step){
35608             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35609         };
35610         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35611         var newCell;
35612
35613         switch(k){
35614             case e.TAB:
35615                 // handled by onEditorKey
35616                 if (g.isEditor && g.editing) {
35617                     return;
35618                 }
35619                 if(e.shiftKey){
35620                      newCell = walk(r, c-1, -1);
35621                 }else{
35622                      newCell = walk(r, c+1, 1);
35623                 }
35624              break;
35625              case e.DOWN:
35626                  newCell = walk(r+1, c, 1);
35627              break;
35628              case e.UP:
35629                  newCell = walk(r-1, c, -1);
35630              break;
35631              case e.RIGHT:
35632                  newCell = walk(r, c+1, 1);
35633              break;
35634              case e.LEFT:
35635                  newCell = walk(r, c-1, -1);
35636              break;
35637              case e.ENTER:
35638                  if(g.isEditor && !g.editing){
35639                     g.startEditing(r, c);
35640                     e.stopEvent();
35641                     return;
35642                 }
35643              break;
35644         };
35645         if(newCell){
35646             this.select(newCell[0], newCell[1]);
35647             e.stopEvent();
35648         }
35649     },
35650
35651     acceptsNav : function(row, col, cm){
35652         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35653     },
35654
35655     onEditorKey : function(field, e){
35656         
35657         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35658         ///Roo.log('onEditorKey' + k);
35659         
35660         if(k == e.TAB){
35661             if(e.shiftKey){
35662                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35663             }else{
35664                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35665             }
35666             e.stopEvent();
35667         }else if(k == e.ENTER && !e.ctrlKey){
35668             ed.completeEdit();
35669             e.stopEvent();
35670             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35671         }else if(k == e.ESC){
35672             ed.cancelEdit();
35673         }
35674         
35675         
35676         if(newCell){
35677             //Roo.log('next cell after edit');
35678             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35679         }
35680     }
35681 });/*
35682  * Based on:
35683  * Ext JS Library 1.1.1
35684  * Copyright(c) 2006-2007, Ext JS, LLC.
35685  *
35686  * Originally Released Under LGPL - original licence link has changed is not relivant.
35687  *
35688  * Fork - LGPL
35689  * <script type="text/javascript">
35690  */
35691  
35692 /**
35693  * @class Roo.grid.EditorGrid
35694  * @extends Roo.grid.Grid
35695  * Class for creating and editable grid.
35696  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35697  * The container MUST have some type of size defined for the grid to fill. The container will be 
35698  * automatically set to position relative if it isn't already.
35699  * @param {Object} dataSource The data model to bind to
35700  * @param {Object} colModel The column model with info about this grid's columns
35701  */
35702 Roo.grid.EditorGrid = function(container, config){
35703     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35704     this.getGridEl().addClass("xedit-grid");
35705
35706     if(!this.selModel){
35707         this.selModel = new Roo.grid.CellSelectionModel();
35708     }
35709
35710     this.activeEditor = null;
35711
35712         this.addEvents({
35713             /**
35714              * @event beforeedit
35715              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35716              * <ul style="padding:5px;padding-left:16px;">
35717              * <li>grid - This grid</li>
35718              * <li>record - The record being edited</li>
35719              * <li>field - The field name being edited</li>
35720              * <li>value - The value for the field being edited.</li>
35721              * <li>row - The grid row index</li>
35722              * <li>column - The grid column index</li>
35723              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35724              * </ul>
35725              * @param {Object} e An edit event (see above for description)
35726              */
35727             "beforeedit" : true,
35728             /**
35729              * @event afteredit
35730              * Fires after a cell is edited. <br />
35731              * <ul style="padding:5px;padding-left:16px;">
35732              * <li>grid - This grid</li>
35733              * <li>record - The record being edited</li>
35734              * <li>field - The field name being edited</li>
35735              * <li>value - The value being set</li>
35736              * <li>originalValue - The original value for the field, before the edit.</li>
35737              * <li>row - The grid row index</li>
35738              * <li>column - The grid column index</li>
35739              * </ul>
35740              * @param {Object} e An edit event (see above for description)
35741              */
35742             "afteredit" : true,
35743             /**
35744              * @event validateedit
35745              * Fires after a cell is edited, but before the value is set in the record. 
35746          * You can use this to modify the value being set in the field, Return false
35747              * to cancel the change. The edit event object has the following properties <br />
35748              * <ul style="padding:5px;padding-left:16px;">
35749          * <li>editor - This editor</li>
35750              * <li>grid - This grid</li>
35751              * <li>record - The record being edited</li>
35752              * <li>field - The field name being edited</li>
35753              * <li>value - The value being set</li>
35754              * <li>originalValue - The original value for the field, before the edit.</li>
35755              * <li>row - The grid row index</li>
35756              * <li>column - The grid column index</li>
35757              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35758              * </ul>
35759              * @param {Object} e An edit event (see above for description)
35760              */
35761             "validateedit" : true
35762         });
35763     this.on("bodyscroll", this.stopEditing,  this);
35764     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35765 };
35766
35767 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35768     /**
35769      * @cfg {Number} clicksToEdit
35770      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35771      */
35772     clicksToEdit: 2,
35773
35774     // private
35775     isEditor : true,
35776     // private
35777     trackMouseOver: false, // causes very odd FF errors
35778
35779     onCellDblClick : function(g, row, col){
35780         this.startEditing(row, col);
35781     },
35782
35783     onEditComplete : function(ed, value, startValue){
35784         this.editing = false;
35785         this.activeEditor = null;
35786         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35787         var r = ed.record;
35788         var field = this.colModel.getDataIndex(ed.col);
35789         var e = {
35790             grid: this,
35791             record: r,
35792             field: field,
35793             originalValue: startValue,
35794             value: value,
35795             row: ed.row,
35796             column: ed.col,
35797             cancel:false,
35798             editor: ed
35799         };
35800         if(String(value) !== String(startValue)){
35801             
35802             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35803                 r.set(field, e.value);
35804                 // if we are dealing with a combo box..
35805                 // then we also set the 'name' colum to be the displayField
35806                 if (ed.field.displayField && ed.field.name) {
35807                     r.set(ed.field.name, ed.field.el.dom.value);
35808                 }
35809                 
35810                 delete e.cancel; //?? why!!!
35811                 this.fireEvent("afteredit", e);
35812             }
35813         } else {
35814             this.fireEvent("afteredit", e); // always fire it!
35815         }
35816         this.view.focusCell(ed.row, ed.col);
35817     },
35818
35819     /**
35820      * Starts editing the specified for the specified row/column
35821      * @param {Number} rowIndex
35822      * @param {Number} colIndex
35823      */
35824     startEditing : function(row, col){
35825         this.stopEditing();
35826         if(this.colModel.isCellEditable(col, row)){
35827             this.view.ensureVisible(row, col, true);
35828             var r = this.dataSource.getAt(row);
35829             var field = this.colModel.getDataIndex(col);
35830             var e = {
35831                 grid: this,
35832                 record: r,
35833                 field: field,
35834                 value: r.data[field],
35835                 row: row,
35836                 column: col,
35837                 cancel:false
35838             };
35839             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35840                 this.editing = true;
35841                 var ed = this.colModel.getCellEditor(col, row);
35842                 
35843                 if (!ed) {
35844                     return;
35845                 }
35846                 if(!ed.rendered){
35847                     ed.render(ed.parentEl || document.body);
35848                 }
35849                 ed.field.reset();
35850                 (function(){ // complex but required for focus issues in safari, ie and opera
35851                     ed.row = row;
35852                     ed.col = col;
35853                     ed.record = r;
35854                     ed.on("complete", this.onEditComplete, this, {single: true});
35855                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35856                     this.activeEditor = ed;
35857                     var v = r.data[field];
35858                     ed.startEdit(this.view.getCell(row, col), v);
35859                     // combo's with 'displayField and name set
35860                     if (ed.field.displayField && ed.field.name) {
35861                         ed.field.el.dom.value = r.data[ed.field.name];
35862                     }
35863                     
35864                     
35865                 }).defer(50, this);
35866             }
35867         }
35868     },
35869         
35870     /**
35871      * Stops any active editing
35872      */
35873     stopEditing : function(){
35874         if(this.activeEditor){
35875             this.activeEditor.completeEdit();
35876         }
35877         this.activeEditor = null;
35878     }
35879 });/*
35880  * Based on:
35881  * Ext JS Library 1.1.1
35882  * Copyright(c) 2006-2007, Ext JS, LLC.
35883  *
35884  * Originally Released Under LGPL - original licence link has changed is not relivant.
35885  *
35886  * Fork - LGPL
35887  * <script type="text/javascript">
35888  */
35889
35890 // private - not really -- you end up using it !
35891 // This is a support class used internally by the Grid components
35892
35893 /**
35894  * @class Roo.grid.GridEditor
35895  * @extends Roo.Editor
35896  * Class for creating and editable grid elements.
35897  * @param {Object} config any settings (must include field)
35898  */
35899 Roo.grid.GridEditor = function(field, config){
35900     if (!config && field.field) {
35901         config = field;
35902         field = Roo.factory(config.field, Roo.form);
35903     }
35904     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35905     field.monitorTab = false;
35906 };
35907
35908 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35909     
35910     /**
35911      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35912      */
35913     
35914     alignment: "tl-tl",
35915     autoSize: "width",
35916     hideEl : false,
35917     cls: "x-small-editor x-grid-editor",
35918     shim:false,
35919     shadow:"frame"
35920 });/*
35921  * Based on:
35922  * Ext JS Library 1.1.1
35923  * Copyright(c) 2006-2007, Ext JS, LLC.
35924  *
35925  * Originally Released Under LGPL - original licence link has changed is not relivant.
35926  *
35927  * Fork - LGPL
35928  * <script type="text/javascript">
35929  */
35930   
35931
35932   
35933 Roo.grid.PropertyRecord = Roo.data.Record.create([
35934     {name:'name',type:'string'},  'value'
35935 ]);
35936
35937
35938 Roo.grid.PropertyStore = function(grid, source){
35939     this.grid = grid;
35940     this.store = new Roo.data.Store({
35941         recordType : Roo.grid.PropertyRecord
35942     });
35943     this.store.on('update', this.onUpdate,  this);
35944     if(source){
35945         this.setSource(source);
35946     }
35947     Roo.grid.PropertyStore.superclass.constructor.call(this);
35948 };
35949
35950
35951
35952 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35953     setSource : function(o){
35954         this.source = o;
35955         this.store.removeAll();
35956         var data = [];
35957         for(var k in o){
35958             if(this.isEditableValue(o[k])){
35959                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35960             }
35961         }
35962         this.store.loadRecords({records: data}, {}, true);
35963     },
35964
35965     onUpdate : function(ds, record, type){
35966         if(type == Roo.data.Record.EDIT){
35967             var v = record.data['value'];
35968             var oldValue = record.modified['value'];
35969             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35970                 this.source[record.id] = v;
35971                 record.commit();
35972                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35973             }else{
35974                 record.reject();
35975             }
35976         }
35977     },
35978
35979     getProperty : function(row){
35980        return this.store.getAt(row);
35981     },
35982
35983     isEditableValue: function(val){
35984         if(val && val instanceof Date){
35985             return true;
35986         }else if(typeof val == 'object' || typeof val == 'function'){
35987             return false;
35988         }
35989         return true;
35990     },
35991
35992     setValue : function(prop, value){
35993         this.source[prop] = value;
35994         this.store.getById(prop).set('value', value);
35995     },
35996
35997     getSource : function(){
35998         return this.source;
35999     }
36000 });
36001
36002 Roo.grid.PropertyColumnModel = function(grid, store){
36003     this.grid = grid;
36004     var g = Roo.grid;
36005     g.PropertyColumnModel.superclass.constructor.call(this, [
36006         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36007         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36008     ]);
36009     this.store = store;
36010     this.bselect = Roo.DomHelper.append(document.body, {
36011         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36012             {tag: 'option', value: 'true', html: 'true'},
36013             {tag: 'option', value: 'false', html: 'false'}
36014         ]
36015     });
36016     Roo.id(this.bselect);
36017     var f = Roo.form;
36018     this.editors = {
36019         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36020         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36021         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36022         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36023         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36024     };
36025     this.renderCellDelegate = this.renderCell.createDelegate(this);
36026     this.renderPropDelegate = this.renderProp.createDelegate(this);
36027 };
36028
36029 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36030     
36031     
36032     nameText : 'Name',
36033     valueText : 'Value',
36034     
36035     dateFormat : 'm/j/Y',
36036     
36037     
36038     renderDate : function(dateVal){
36039         return dateVal.dateFormat(this.dateFormat);
36040     },
36041
36042     renderBool : function(bVal){
36043         return bVal ? 'true' : 'false';
36044     },
36045
36046     isCellEditable : function(colIndex, rowIndex){
36047         return colIndex == 1;
36048     },
36049
36050     getRenderer : function(col){
36051         return col == 1 ?
36052             this.renderCellDelegate : this.renderPropDelegate;
36053     },
36054
36055     renderProp : function(v){
36056         return this.getPropertyName(v);
36057     },
36058
36059     renderCell : function(val){
36060         var rv = val;
36061         if(val instanceof Date){
36062             rv = this.renderDate(val);
36063         }else if(typeof val == 'boolean'){
36064             rv = this.renderBool(val);
36065         }
36066         return Roo.util.Format.htmlEncode(rv);
36067     },
36068
36069     getPropertyName : function(name){
36070         var pn = this.grid.propertyNames;
36071         return pn && pn[name] ? pn[name] : name;
36072     },
36073
36074     getCellEditor : function(colIndex, rowIndex){
36075         var p = this.store.getProperty(rowIndex);
36076         var n = p.data['name'], val = p.data['value'];
36077         
36078         if(typeof(this.grid.customEditors[n]) == 'string'){
36079             return this.editors[this.grid.customEditors[n]];
36080         }
36081         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36082             return this.grid.customEditors[n];
36083         }
36084         if(val instanceof Date){
36085             return this.editors['date'];
36086         }else if(typeof val == 'number'){
36087             return this.editors['number'];
36088         }else if(typeof val == 'boolean'){
36089             return this.editors['boolean'];
36090         }else{
36091             return this.editors['string'];
36092         }
36093     }
36094 });
36095
36096 /**
36097  * @class Roo.grid.PropertyGrid
36098  * @extends Roo.grid.EditorGrid
36099  * This class represents the  interface of a component based property grid control.
36100  * <br><br>Usage:<pre><code>
36101  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36102       
36103  });
36104  // set any options
36105  grid.render();
36106  * </code></pre>
36107   
36108  * @constructor
36109  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36110  * The container MUST have some type of size defined for the grid to fill. The container will be
36111  * automatically set to position relative if it isn't already.
36112  * @param {Object} config A config object that sets properties on this grid.
36113  */
36114 Roo.grid.PropertyGrid = function(container, config){
36115     config = config || {};
36116     var store = new Roo.grid.PropertyStore(this);
36117     this.store = store;
36118     var cm = new Roo.grid.PropertyColumnModel(this, store);
36119     store.store.sort('name', 'ASC');
36120     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36121         ds: store.store,
36122         cm: cm,
36123         enableColLock:false,
36124         enableColumnMove:false,
36125         stripeRows:false,
36126         trackMouseOver: false,
36127         clicksToEdit:1
36128     }, config));
36129     this.getGridEl().addClass('x-props-grid');
36130     this.lastEditRow = null;
36131     this.on('columnresize', this.onColumnResize, this);
36132     this.addEvents({
36133          /**
36134              * @event beforepropertychange
36135              * Fires before a property changes (return false to stop?)
36136              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36137              * @param {String} id Record Id
36138              * @param {String} newval New Value
36139          * @param {String} oldval Old Value
36140              */
36141         "beforepropertychange": true,
36142         /**
36143              * @event propertychange
36144              * Fires after a property changes
36145              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36146              * @param {String} id Record Id
36147              * @param {String} newval New Value
36148          * @param {String} oldval Old Value
36149              */
36150         "propertychange": true
36151     });
36152     this.customEditors = this.customEditors || {};
36153 };
36154 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36155     
36156      /**
36157      * @cfg {Object} customEditors map of colnames=> custom editors.
36158      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36159      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36160      * false disables editing of the field.
36161          */
36162     
36163       /**
36164      * @cfg {Object} propertyNames map of property Names to their displayed value
36165          */
36166     
36167     render : function(){
36168         Roo.grid.PropertyGrid.superclass.render.call(this);
36169         this.autoSize.defer(100, this);
36170     },
36171
36172     autoSize : function(){
36173         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36174         if(this.view){
36175             this.view.fitColumns();
36176         }
36177     },
36178
36179     onColumnResize : function(){
36180         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36181         this.autoSize();
36182     },
36183     /**
36184      * Sets the data for the Grid
36185      * accepts a Key => Value object of all the elements avaiable.
36186      * @param {Object} data  to appear in grid.
36187      */
36188     setSource : function(source){
36189         this.store.setSource(source);
36190         //this.autoSize();
36191     },
36192     /**
36193      * Gets all the data from the grid.
36194      * @return {Object} data  data stored in grid
36195      */
36196     getSource : function(){
36197         return this.store.getSource();
36198     }
36199 });/*
36200  * Based on:
36201  * Ext JS Library 1.1.1
36202  * Copyright(c) 2006-2007, Ext JS, LLC.
36203  *
36204  * Originally Released Under LGPL - original licence link has changed is not relivant.
36205  *
36206  * Fork - LGPL
36207  * <script type="text/javascript">
36208  */
36209  
36210 /**
36211  * @class Roo.LoadMask
36212  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36213  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36214  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36215  * element's UpdateManager load indicator and will be destroyed after the initial load.
36216  * @constructor
36217  * Create a new LoadMask
36218  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36219  * @param {Object} config The config object
36220  */
36221 Roo.LoadMask = function(el, config){
36222     this.el = Roo.get(el);
36223     Roo.apply(this, config);
36224     if(this.store){
36225         this.store.on('beforeload', this.onBeforeLoad, this);
36226         this.store.on('load', this.onLoad, this);
36227         this.store.on('loadexception', this.onLoad, this);
36228         this.removeMask = false;
36229     }else{
36230         var um = this.el.getUpdateManager();
36231         um.showLoadIndicator = false; // disable the default indicator
36232         um.on('beforeupdate', this.onBeforeLoad, this);
36233         um.on('update', this.onLoad, this);
36234         um.on('failure', this.onLoad, this);
36235         this.removeMask = true;
36236     }
36237 };
36238
36239 Roo.LoadMask.prototype = {
36240     /**
36241      * @cfg {Boolean} removeMask
36242      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36243      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36244      */
36245     /**
36246      * @cfg {String} msg
36247      * The text to display in a centered loading message box (defaults to 'Loading...')
36248      */
36249     msg : 'Loading...',
36250     /**
36251      * @cfg {String} msgCls
36252      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36253      */
36254     msgCls : 'x-mask-loading',
36255
36256     /**
36257      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36258      * @type Boolean
36259      */
36260     disabled: false,
36261
36262     /**
36263      * Disables the mask to prevent it from being displayed
36264      */
36265     disable : function(){
36266        this.disabled = true;
36267     },
36268
36269     /**
36270      * Enables the mask so that it can be displayed
36271      */
36272     enable : function(){
36273         this.disabled = false;
36274     },
36275
36276     // private
36277     onLoad : function(){
36278         this.el.unmask(this.removeMask);
36279     },
36280
36281     // private
36282     onBeforeLoad : function(){
36283         if(!this.disabled){
36284             this.el.mask(this.msg, this.msgCls);
36285         }
36286     },
36287
36288     // private
36289     destroy : function(){
36290         if(this.store){
36291             this.store.un('beforeload', this.onBeforeLoad, this);
36292             this.store.un('load', this.onLoad, this);
36293             this.store.un('loadexception', this.onLoad, this);
36294         }else{
36295             var um = this.el.getUpdateManager();
36296             um.un('beforeupdate', this.onBeforeLoad, this);
36297             um.un('update', this.onLoad, this);
36298             um.un('failure', this.onLoad, this);
36299         }
36300     }
36301 };/*
36302  * Based on:
36303  * Ext JS Library 1.1.1
36304  * Copyright(c) 2006-2007, Ext JS, LLC.
36305  *
36306  * Originally Released Under LGPL - original licence link has changed is not relivant.
36307  *
36308  * Fork - LGPL
36309  * <script type="text/javascript">
36310  */
36311 Roo.XTemplate = function(){
36312     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36313     var s = this.html;
36314
36315     s = ['<tpl>', s, '</tpl>'].join('');
36316
36317     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36318
36319     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36320     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36321     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36322     var m, id = 0;
36323     var tpls = [];
36324
36325     while(m = s.match(re)){
36326        var m2 = m[0].match(nameRe);
36327        var m3 = m[0].match(ifRe);
36328        var m4 = m[0].match(execRe);
36329        var exp = null, fn = null, exec = null;
36330        var name = m2 && m2[1] ? m2[1] : '';
36331        if(m3){
36332            exp = m3 && m3[1] ? m3[1] : null;
36333            if(exp){
36334                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36335            }
36336        }
36337        if(m4){
36338            exp = m4 && m4[1] ? m4[1] : null;
36339            if(exp){
36340                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36341            }
36342        }
36343        if(name){
36344            switch(name){
36345                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36346                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36347                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36348            }
36349        }
36350        tpls.push({
36351             id: id,
36352             target: name,
36353             exec: exec,
36354             test: fn,
36355             body: m[1]||''
36356         });
36357        s = s.replace(m[0], '{xtpl'+ id + '}');
36358        ++id;
36359     }
36360     for(var i = tpls.length-1; i >= 0; --i){
36361         this.compileTpl(tpls[i]);
36362     }
36363     this.master = tpls[tpls.length-1];
36364     this.tpls = tpls;
36365 };
36366 Roo.extend(Roo.XTemplate, Roo.Template, {
36367
36368     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36369
36370     applySubTemplate : function(id, values, parent){
36371         var t = this.tpls[id];
36372         if(t.test && !t.test.call(this, values, parent)){
36373             return '';
36374         }
36375         if(t.exec && t.exec.call(this, values, parent)){
36376             return '';
36377         }
36378         var vs = t.target ? t.target.call(this, values, parent) : values;
36379         parent = t.target ? values : parent;
36380         if(t.target && vs instanceof Array){
36381             var buf = [];
36382             for(var i = 0, len = vs.length; i < len; i++){
36383                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36384             }
36385             return buf.join('');
36386         }
36387         return t.compiled.call(this, vs, parent);
36388     },
36389
36390     compileTpl : function(tpl){
36391         var fm = Roo.util.Format;
36392         var useF = this.disableFormats !== true;
36393         var sep = Roo.isGecko ? "+" : ",";
36394         var fn = function(m, name, format, args){
36395             if(name.substr(0, 4) == 'xtpl'){
36396                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36397             }
36398             var v;
36399             if(name.indexOf('.') != -1){
36400                 v = name;
36401             }else{
36402                 v = "values['" + name + "']";
36403             }
36404             if(format && useF){
36405                 args = args ? ',' + args : "";
36406                 if(format.substr(0, 5) != "this."){
36407                     format = "fm." + format + '(';
36408                 }else{
36409                     format = 'this.call("'+ format.substr(5) + '", ';
36410                     args = ", values";
36411                 }
36412             }else{
36413                 args= ''; format = "("+v+" === undefined ? '' : ";
36414             }
36415             return "'"+ sep + format + v + args + ")"+sep+"'";
36416         };
36417         var body;
36418         // branched to use + in gecko and [].join() in others
36419         if(Roo.isGecko){
36420             body = "tpl.compiled = function(values, parent){ return '" +
36421                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36422                     "';};";
36423         }else{
36424             body = ["tpl.compiled = function(values, parent){ return ['"];
36425             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36426             body.push("'].join('');};");
36427             body = body.join('');
36428         }
36429         /** eval:var:zzzzzzz */
36430         eval(body);
36431         return this;
36432     },
36433
36434     applyTemplate : function(values){
36435         return this.master.compiled.call(this, values, {});
36436         var s = this.subs;
36437     },
36438
36439     apply : function(){
36440         return this.applyTemplate.apply(this, arguments);
36441     },
36442
36443     compile : function(){return this;}
36444 });
36445
36446 Roo.XTemplate.from = function(el){
36447     el = Roo.getDom(el);
36448     return new Roo.XTemplate(el.value || el.innerHTML);
36449 };/*
36450  * Original code for Roojs - LGPL
36451  * <script type="text/javascript">
36452  */
36453  
36454 /**
36455  * @class Roo.XComponent
36456  * A delayed Element creator...
36457  * 
36458  * Mypart.xyx = new Roo.XComponent({
36459
36460     parent : 'Mypart.xyz', // empty == document.element.!!
36461     order : '001',
36462     name : 'xxxx'
36463     region : 'xxxx'
36464     disabled : function() {} 
36465      
36466     tree : function() { // return an tree of xtype declared components
36467         var MODULE = this;
36468         return 
36469         {
36470             xtype : 'NestedLayoutPanel',
36471             // technicall
36472         }
36473      ]
36474  *})
36475  * @extends Roo.util.Observable
36476  * @constructor
36477  * @param cfg {Object} configuration of component
36478  * 
36479  */
36480 Roo.XComponent = function(cfg) {
36481     Roo.apply(this, cfg);
36482     this.addEvents({ 
36483         /**
36484              * @event built
36485              * Fires when this the componnt is built
36486              * @param {Roo.XComponent} c the component
36487              */
36488         'built' : true,
36489         /**
36490              * @event buildcomplete
36491              * Fires on the top level element when all elements have been built
36492              * @param {Roo.XComponent} c the top level component.
36493          */
36494         'buildcomplete' : true
36495         
36496     });
36497     
36498     Roo.XComponent.register(this);
36499     this.modules = false;
36500     this.el = false; // where the layout goes..
36501     
36502     
36503 }
36504 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36505     /**
36506      * @property el
36507      * The created element (with Roo.factory())
36508      * @type {Roo.Layout}
36509      */
36510     el  : false,
36511     
36512     /**
36513      * @property el
36514      * for BC  - use el in new code
36515      * @type {Roo.Layout}
36516      */
36517     panel : false,
36518     
36519     /**
36520      * @property layout
36521      * for BC  - use el in new code
36522      * @type {Roo.Layout}
36523      */
36524     layout : false,
36525     
36526      /**
36527      * @cfg {Function|boolean} disabled
36528      * If this module is disabled by some rule, return true from the funtion
36529      */
36530     disabled : false,
36531     
36532     /**
36533      * @cfg {String} parent 
36534      * Name of parent element which it get xtype added to..
36535      */
36536     parent: false,
36537     
36538     /**
36539      * @cfg {String} order
36540      * Used to set the order in which elements are created (usefull for multiple tabs)
36541      */
36542     
36543     order : false,
36544     /**
36545      * @cfg {String} name
36546      * String to display while loading.
36547      */
36548     name : false,
36549     /**
36550      * @cfg {Array} items
36551      * A single item array - the first element is the root of the tree..
36552      * It's done this way to stay compatible with the Xtype system...
36553      */
36554     items : false
36555      
36556      
36557     
36558 });
36559
36560 Roo.apply(Roo.XComponent, {
36561     
36562     /**
36563      * @property  buildCompleted
36564      * True when the builder has completed building the interface.
36565      * @type Boolean
36566      */
36567     buildCompleted : false,
36568      
36569     /**
36570      * @property  topModule
36571      * the upper most module - uses document.element as it's constructor.
36572      * @type Object
36573      */
36574      
36575     topModule  : false,
36576       
36577     /**
36578      * @property  modules
36579      * array of modules to be created by registration system.
36580      * @type Roo.XComponent
36581      */
36582     
36583     modules : [],
36584       
36585     
36586     /**
36587      * Register components to be built later.
36588      *
36589      * This solves the following issues
36590      * - Building is not done on page load, but after an authentication process has occured.
36591      * - Interface elements are registered on page load
36592      * - Parent Interface elements may not be loaded before child, so this handles that..
36593      * 
36594      *
36595      * example:
36596      * 
36597      * MyApp.register({
36598           order : '000001',
36599           module : 'Pman.Tab.projectMgr',
36600           region : 'center',
36601           parent : 'Pman.layout',
36602           disabled : false,  // or use a function..
36603         })
36604      
36605      * * @param {Object} details about module
36606      */
36607     register : function(obj) {
36608         this.modules.push(obj);
36609          
36610     },
36611     /**
36612      * convert a string to an object..
36613      * 
36614      */
36615     
36616     toObject : function(str)
36617     {
36618         if (!str || typeof(str) == 'object') {
36619             return str;
36620         }
36621         var ar = str.split('.');
36622         var rt, o;
36623         rt = ar.shift();
36624             /** eval:var:o */
36625         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36626         if (o === false) {
36627             throw "Module not found : " + str;
36628         }
36629         Roo.each(ar, function(e) {
36630             if (typeof(o[e]) == 'undefined') {
36631                 throw "Module not found : " + str;
36632             }
36633             o = o[e];
36634         });
36635         return o;
36636         
36637     },
36638     
36639     
36640     /**
36641      * move modules into their correct place in the tree..
36642      * 
36643      */
36644     preBuild : function ()
36645     {
36646         
36647         Roo.each(this.modules , function (obj)
36648         {
36649             obj.parent = this.toObject(obj.parent);
36650             
36651             if (!obj.parent) {
36652                 this.topModule = obj;
36653                 return;
36654             }
36655             
36656             if (!obj.parent.modules) {
36657                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36658                     function(o) { return o.order + '' }
36659                 );
36660             }
36661             
36662             obj.parent.modules.add(obj);
36663         }, this);
36664     },
36665     
36666      /**
36667      * make a list of modules to build.
36668      * @return {Array} list of modules. 
36669      */ 
36670     
36671     buildOrder : function()
36672     {
36673         var _this = this;
36674         var cmp = function(a,b) {   
36675             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36676         };
36677         
36678         if (!this.topModule || !this.topModule.modules) {
36679             throw "No top level modules to build";
36680         }
36681        
36682         // make a flat list in order of modules to build.
36683         var mods = [ this.topModule ];
36684         
36685         
36686         // add modules to their parents..
36687         var addMod = function(m) {
36688            // Roo.debug && Roo.log(m.modKey);
36689             
36690             mods.push(m);
36691             if (m.modules) {
36692                 m.modules.keySort('ASC',  cmp );
36693                 m.modules.each(addMod);
36694             }
36695             // not sure if this is used any more..
36696             if (m.finalize) {
36697                 m.finalize.name = m.name + " (clean up) ";
36698                 mods.push(m.finalize);
36699             }
36700             
36701         }
36702         this.topModule.modules.keySort('ASC',  cmp );
36703         this.topModule.modules.each(addMod);
36704         return mods;
36705     },
36706     
36707      /**
36708      * Build the registered modules.
36709      * @param {Object} parent element.
36710      * @param {Function} optional method to call after module has been added.
36711      * 
36712      */ 
36713    
36714     build : function() 
36715     {
36716         
36717         this.preBuild();
36718         var mods = this.buildOrder();
36719       
36720         //this.allmods = mods;
36721         //Roo.debug && Roo.log(mods);
36722         //return;
36723         if (!mods.length) { // should not happen
36724             throw "NO modules!!!";
36725         }
36726         
36727         
36728         
36729         // flash it up as modal - so we store the mask!?
36730         Roo.MessageBox.show({ title: 'loading' });
36731         Roo.MessageBox.show({
36732            title: "Please wait...",
36733            msg: "Building Interface...",
36734            width:450,
36735            progress:true,
36736            closable:false,
36737            modal: false
36738           
36739         });
36740         var total = mods.length;
36741         
36742         var _this = this;
36743         var progressRun = function() {
36744             if (!mods.length) {
36745                 Roo.debug && Roo.log('hide?');
36746                 Roo.MessageBox.hide();
36747                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36748                 return;    
36749             }
36750             
36751             var m = mods.shift();
36752             Roo.debug && Roo.log(m);
36753             if (typeof(m) == 'function') { // not sure if this is supported any more..
36754                 m.call(this);
36755                 return progressRun.defer(10, _this);
36756             } 
36757             
36758             Roo.MessageBox.updateProgress(
36759                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36760                     " of " + total + 
36761                     (m.name ? (' - ' + m.name) : '')
36762                     );
36763             
36764          
36765             
36766             var disabled = (typeof(m.disabled) == 'function') ?
36767                 m.disabled.call(m.module.disabled) : m.disabled;    
36768             
36769             
36770             if (disabled) {
36771                 return progressRun(); // we do not update the display!
36772             }
36773             
36774             if (!m.parent) {
36775                 // it's a top level one..
36776                 var layoutbase = new Ext.BorderLayout(document.body, {
36777                
36778                     center: {
36779                          titlebar: false,
36780                          autoScroll:false,
36781                          closeOnTab: true,
36782                          tabPosition: 'top',
36783                          //resizeTabs: true,
36784                          alwaysShowTabs: true,
36785                          minTabWidth: 140
36786                     }
36787                 });
36788                 var tree = m.tree();
36789                 tree.region = 'center';
36790                 m.el = layoutbase.addxtype(tree);
36791                 m.panel = m.el;
36792                 m.layout = m.panel.layout;    
36793                 return progressRun.defer(10, _this);
36794             }
36795             
36796             var tree = m.tree();
36797             tree.region = tree.region || m.region;
36798             m.el = m.parent.el.addxtype(tree);
36799             m.fireEvent('built', m);
36800             m.panel = m.el;
36801             m.layout = m.panel.layout;    
36802             progressRun.defer(10, _this); 
36803             
36804         }
36805         progressRun.defer(1, _this);
36806      
36807         
36808         
36809     }
36810      
36811    
36812     
36813     
36814 });
36815  //<script type="text/javascript">
36816
36817
36818 /**
36819  * @class Roo.Login
36820  * @extends Roo.LayoutDialog
36821  * A generic Login Dialog..... - only one needed in theory!?!?
36822  *
36823  * Fires XComponent builder on success...
36824  * 
36825  * Sends 
36826  *    username,password, lang = for login actions.
36827  *    check = 1 for periodic checking that sesion is valid.
36828  *    passwordRequest = email request password
36829  *    logout = 1 = to logout
36830  * 
36831  * Affects: (this id="????" elements)
36832  *   loading  (removed) (used to indicate application is loading)
36833  *   loading-mask (hides) (used to hide application when it's building loading)
36834  *   
36835  * 
36836  * Usage: 
36837  *    
36838  * 
36839  * Myapp.login = Roo.Login({
36840      url: xxxx,
36841    
36842      realm : 'Myapp', 
36843      
36844      
36845      method : 'POST',
36846      
36847      
36848      * 
36849  })
36850  * 
36851  * 
36852  * 
36853  **/
36854  
36855 Roo.Login = function(cfg)
36856 {
36857     this.addEvents({
36858         'refreshed' : true
36859     });
36860     
36861     Roo.apply(this,cfg);
36862     
36863     Roo.onReady(function() {
36864         this.onLoad();
36865     }, this);
36866     // call parent..
36867     
36868    
36869     Roo.Login.superclass.constructor.call(this, this);
36870     //this.addxtype(this.items[0]);
36871     
36872     
36873 }
36874
36875
36876 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36877     
36878     /**
36879      * @cfg {String} method
36880      * Method used to query for login details.
36881      */
36882     
36883     method : 'POST',
36884     /**
36885      * @cfg {String} url
36886      * URL to query login data. - eg. baseURL + '/Login.php'
36887      */
36888     url : '',
36889     
36890     /**
36891      * @property user
36892      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36893      * @type {Object} 
36894      */
36895     user : false,
36896     /**
36897      * @property checkFails
36898      * Number of times we have attempted to get authentication check, and failed.
36899      * @type {Number} 
36900      */
36901     checkFails : 0,
36902       /**
36903      * @property intervalID
36904      * The window interval that does the constant login checking.
36905      * @type {Number} 
36906      */
36907     intervalID : 0,
36908     
36909     
36910     onLoad : function() // called on page load...
36911     {
36912         // load 
36913          
36914         if (Roo.get('loading')) { // clear any loading indicator..
36915             Roo.get('loading').remove();
36916         }
36917         
36918         //this.switchLang('en'); // set the language to english..
36919        
36920         this.check({
36921             success:  function(response, opts)  {  // check successfull...
36922             
36923                 var res = this.processResponse(response);
36924                 this.checkFails =0;
36925                 if (!res.success) { // error!
36926                     this.checkFails = 5;
36927                     //console.log('call failure');
36928                     return this.failure(response,opts);
36929                 }
36930                 
36931                 if (!res.data.id) { // id=0 == login failure.
36932                     return this.show();
36933                 }
36934                 
36935                               
36936                         //console.log(success);
36937                 this.fillAuth(res.data);   
36938                 this.checkFails =0;
36939                 Roo.XComponent.build();
36940             },
36941             failure : this.show
36942         });
36943         
36944     }, 
36945     
36946     
36947     check: function(cfg) // called every so often to refresh cookie etc..
36948     {
36949         if (cfg.again) { // could be undefined..
36950             this.checkFails++;
36951         } else {
36952             this.checkFails = 0;
36953         }
36954         var _this = this;
36955         if (this.sending) {
36956             if ( this.checkFails > 4) {
36957                 Roo.MessageBox.alert("Error",  
36958                     "Error getting authentication status. - try reloading, or wait a while", function() {
36959                         _this.sending = false;
36960                     }); 
36961                 return;
36962             }
36963             cfg.again = true;
36964             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36965             return;
36966         }
36967         this.sending = true;
36968         
36969         Roo.Ajax.request({  
36970             url: this.url,
36971             params: {
36972                 getAuthUser: true
36973             },  
36974             method: this.method,
36975             success:  cfg.success || this.success,
36976             failure : cfg.failure || this.failure,
36977             scope : this,
36978             callCfg : cfg
36979               
36980         });  
36981     }, 
36982     
36983     
36984     logout: function()
36985     {
36986         window.onbeforeunload = function() { }; // false does not work for IE..
36987         this.user = false;
36988         var _this = this;
36989         
36990         Roo.Ajax.request({  
36991             url: this.url,
36992             params: {
36993                 logout: 1
36994             },  
36995             method: 'GET',
36996             failure : function() {
36997                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36998                     document.location = document.location.toString() + '?ts=' + Math.random();
36999                 });
37000                 
37001             },
37002             success : function() {
37003                 _this.user = false;
37004                 this.checkFails =0;
37005                 // fixme..
37006                 document.location = document.location.toString() + '?ts=' + Math.random();
37007             }
37008               
37009               
37010         }); 
37011     },
37012     
37013     processResponse : function (response)
37014     {
37015         var res = '';
37016         try {
37017             res = Roo.decode(response.responseText);
37018             // oops...
37019             if (typeof(res) != 'object') {
37020                 res = { success : false, errorMsg : res, errors : true };
37021             }
37022             if (typeof(res.success) == 'undefined') {
37023                 res.success = false;
37024             }
37025             
37026         } catch(e) {
37027             res = { success : false,  errorMsg : response.responseText, errors : true };
37028         }
37029         return res;
37030     },
37031     
37032     success : function(response, opts)  // check successfull...
37033     {  
37034         this.sending = false;
37035         var res = this.processResponse(response);
37036         if (!res.success) {
37037             return this.failure(response, opts);
37038         }
37039         if (!res.data || !res.data.id) {
37040             return this.failure(response,opts);
37041         }
37042         //console.log(res);
37043         this.fillAuth(res.data);
37044         
37045         this.checkFails =0;
37046         
37047     },
37048     
37049     
37050     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37051     {
37052         this.authUser = -1;
37053         this.sending = false;
37054         var res = this.processResponse(response);
37055         //console.log(res);
37056         if ( this.checkFails > 2) {
37057         
37058             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37059                 "Error getting authentication status. - try reloading"); 
37060             return;
37061         }
37062         opts.callCfg.again = true;
37063         this.check.defer(1000, this, [ opts.callCfg ]);
37064         return;  
37065     },
37066     
37067     
37068     
37069     fillAuth: function(au) {
37070         this.startAuthCheck();
37071         this.authUserId = au.id;
37072         this.authUser = au;
37073         this.lastChecked = new Date();
37074         this.fireEvent('refreshed', au);
37075         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37076         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37077         au.lang = au.lang || 'en';
37078         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37079         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37080         this.switchLang(au.lang );
37081         
37082      
37083         // open system... - -on setyp..
37084         if (this.authUserId  < 0) {
37085             Roo.MessageBox.alert("Warning", 
37086                 "This is an open system - please set up a admin user with a password.");  
37087         }
37088          
37089         //Pman.onload(); // which should do nothing if it's a re-auth result...
37090         
37091              
37092     },
37093     
37094     startAuthCheck : function() // starter for timeout checking..
37095     {
37096         if (this.intervalID) { // timer already in place...
37097             return false;
37098         }
37099         var _this = this;
37100         this.intervalID =  window.setInterval(function() {
37101               _this.check(false);
37102             }, 120000); // every 120 secs = 2mins..
37103         
37104         
37105     },
37106          
37107     
37108     switchLang : function (lang) 
37109     {
37110         _T = typeof(_T) == 'undefined' ? false : _T;
37111           if (!_T || !lang.length) {
37112             return;
37113         }
37114         
37115         if (!_T && lang != 'en') {
37116             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37117             return;
37118         }
37119         
37120         if (typeof(_T.en) == 'undefined') {
37121             _T.en = {};
37122             Roo.apply(_T.en, _T);
37123         }
37124         
37125         if (typeof(_T[lang]) == 'undefined') {
37126             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37127             return;
37128         }
37129         
37130         
37131         Roo.apply(_T, _T[lang]);
37132         // just need to set the text values for everything...
37133         var _this = this;
37134         /* this will not work ...
37135         if (this.form) { 
37136             
37137                
37138             function formLabel(name, val) {
37139                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37140             }
37141             
37142             formLabel('password', "Password"+':');
37143             formLabel('username', "Email Address"+':');
37144             formLabel('lang', "Language"+':');
37145             this.dialog.setTitle("Login");
37146             this.dialog.buttons[0].setText("Forgot Password");
37147             this.dialog.buttons[1].setText("Login");
37148         }
37149         */
37150         
37151         
37152     },
37153     
37154     
37155     title: "Login",
37156     modal: true,
37157     width:  350,
37158     //height: 230,
37159     height: 180,
37160     shadow: true,
37161     minWidth:200,
37162     minHeight:180,
37163     //proxyDrag: true,
37164     closable: false,
37165     draggable: false,
37166     collapsible: false,
37167     resizable: false,
37168     center: {  // needed??
37169         autoScroll:false,
37170         titlebar: false,
37171        // tabPosition: 'top',
37172         hideTabs: true,
37173         closeOnTab: true,
37174         alwaysShowTabs: false
37175     } ,
37176     listeners : {
37177         
37178         show  : function(dlg)
37179         {
37180             //console.log(this);
37181             this.form = this.layout.getRegion('center').activePanel.form;
37182             this.form.dialog = dlg;
37183             this.buttons[0].form = this.form;
37184             this.buttons[0].dialog = dlg;
37185             this.buttons[1].form = this.form;
37186             this.buttons[1].dialog = dlg;
37187            
37188            //this.resizeToLogo.defer(1000,this);
37189             // this is all related to resizing for logos..
37190             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37191            //// if (!sz) {
37192              //   this.resizeToLogo.defer(1000,this);
37193              //   return;
37194            // }
37195             //var w = Ext.lib.Dom.getViewWidth() - 100;
37196             //var h = Ext.lib.Dom.getViewHeight() - 100;
37197             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37198             //this.center();
37199             if (this.disabled) {
37200                 this.hide();
37201                 return;
37202             }
37203             
37204             if (this.user.id < 0) { // used for inital setup situations.
37205                 return;
37206             }
37207             
37208             if (this.intervalID) {
37209                 // remove the timer
37210                 window.clearInterval(this.intervalID);
37211                 this.intervalID = false;
37212             }
37213             
37214             
37215             if (Roo.get('loading')) {
37216                 Roo.get('loading').remove();
37217             }
37218             if (Roo.get('loading-mask')) {
37219                 Roo.get('loading-mask').hide();
37220             }
37221             
37222             //incomming._node = tnode;
37223             this.form.reset();
37224             //this.dialog.modal = !modal;
37225             //this.dialog.show();
37226             this.el.unmask(); 
37227             
37228             
37229             this.form.setValues({
37230                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37231                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37232             });
37233             
37234             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37235             if (this.form.findField('username').getValue().length > 0 ){
37236                 this.form.findField('password').focus();
37237             } else {
37238                this.form.findField('username').focus();
37239             }
37240     
37241         }
37242     },
37243     items : [
37244          {
37245        
37246             xtype : 'ContentPanel',
37247             xns : Roo,
37248             region: 'center',
37249             fitToFrame : true,
37250             
37251             items : [
37252     
37253                 {
37254                
37255                     xtype : 'Form',
37256                     xns : Roo.form,
37257                     labelWidth: 100,
37258                     style : 'margin: 10px;',
37259                     
37260                     listeners : {
37261                         actionfailed : function(f, act) {
37262                             // form can return { errors: .... }
37263                                 
37264                             //act.result.errors // invalid form element list...
37265                             //act.result.errorMsg// invalid form element list...
37266                             
37267                             this.dialog.el.unmask();
37268                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37269                                         "Login failed - communication error - try again.");
37270                                       
37271                         },
37272                         actioncomplete: function(re, act) {
37273                              
37274                             Roo.state.Manager.set(
37275                                 this.dialog.realm + '.username',  
37276                                     this.findField('username').getValue()
37277                             );
37278                             Roo.state.Manager.set(
37279                                 this.dialog.realm + '.lang',  
37280                                 this.findField('lang').getValue() 
37281                             );
37282                             
37283                             this.dialog.fillAuth(act.result.data);
37284                               
37285                             this.dialog.hide();
37286                             
37287                             if (Roo.get('loading-mask')) {
37288                                 Roo.get('loading-mask').show();
37289                             }
37290                             Roo.XComponent.build();
37291                             
37292                              
37293                             
37294                         }
37295                     },
37296                     items : [
37297                         {
37298                             xtype : 'TextField',
37299                             xns : Roo.form,
37300                             fieldLabel: "Email Address",
37301                             name: 'username',
37302                             width:200,
37303                             autoCreate : {tag: "input", type: "text", size: "20"}
37304                         },
37305                         {
37306                             xtype : 'TextField',
37307                             xns : Roo.form,
37308                             fieldLabel: "Password",
37309                             inputType: 'password',
37310                             name: 'password',
37311                             width:200,
37312                             autoCreate : {tag: "input", type: "text", size: "20"},
37313                             listeners : {
37314                                 specialkey : function(e,ev) {
37315                                     if (ev.keyCode == 13) {
37316                                         this.form.dialog.el.mask("Logging in");
37317                                         this.form.doAction('submit', {
37318                                             url: this.form.dialog.url,
37319                                             method: this.form.dialog.method
37320                                         });
37321                                     }
37322                                 }
37323                             }  
37324                         },
37325                         {
37326                             xtype : 'ComboBox',
37327                             xns : Roo.form,
37328                             fieldLabel: "Language",
37329                             name : 'langdisp',
37330                             store: {
37331                                 xtype : 'SimpleStore',
37332                                 fields: ['lang', 'ldisp'],
37333                                 data : [
37334                                     [ 'en', 'English' ],
37335                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37336                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37337                                 ]
37338                             },
37339                             
37340                             valueField : 'lang',
37341                             hiddenName:  'lang',
37342                             width: 200,
37343                             displayField:'ldisp',
37344                             typeAhead: false,
37345                             editable: false,
37346                             mode: 'local',
37347                             triggerAction: 'all',
37348                             emptyText:'Select a Language...',
37349                             selectOnFocus:true,
37350                             listeners : {
37351                                 select :  function(cb, rec, ix) {
37352                                     this.form.switchLang(rec.data.lang);
37353                                 }
37354                             }
37355                         
37356                         }
37357                     ]
37358                 }
37359                   
37360                 
37361             ]
37362         }
37363     ],
37364     buttons : [
37365         {
37366             xtype : 'Button',
37367             xns : 'Roo',
37368             text : "Forgot Password",
37369             listeners : {
37370                 click : function() {
37371                     //console.log(this);
37372                     var n = this.form.findField('username').getValue();
37373                     if (!n.length) {
37374                         Roo.MessageBox.alert("Error", "Fill in your email address");
37375                         return;
37376                     }
37377                     Roo.Ajax.request({
37378                         url: this.dialog.url,
37379                         params: {
37380                             passwordRequest: n
37381                         },
37382                         method: this.dialog.method,
37383                         success:  function(response, opts)  {  // check successfull...
37384                         
37385                             var res = this.dialog.processResponse(response);
37386                             if (!res.success) { // error!
37387                                Roo.MessageBox.alert("Error" ,
37388                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37389                                return;
37390                             }
37391                             Roo.MessageBox.alert("Notice" ,
37392                                 "Please check you email for the Password Reset message");
37393                         },
37394                         failure : function() {
37395                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37396                         }
37397                         
37398                     });
37399                 }
37400             }
37401         },
37402         {
37403             xtype : 'Button',
37404             xns : 'Roo',
37405             text : "Login",
37406             listeners : {
37407                 
37408                 click : function () {
37409                         
37410                     this.dialog.el.mask("Logging in");
37411                     this.form.doAction('submit', {
37412                             url: this.dialog.url,
37413                             method: this.dialog.method
37414                     });
37415                 }
37416             }
37417         }
37418     ]
37419   
37420   
37421 })
37422  
37423
37424
37425