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 = config.listeners;
3895     if (listeners) {
3896         delete config.listeners;
3897     }
3898     Roo.apply(this, config);
3899     
3900     if(this.containerScroll){
3901         Roo.dd.ScrollManager.register(this.el);
3902     }
3903     this.addEvents( {
3904          /**
3905          * @scope Roo.dd.DropTarget
3906          */
3907          
3908          /**
3909          * @event enter
3910          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3911          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3912          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3913          * 
3914          * IMPORTANT : it should set this.overClass and this.dropAllowed
3915          * 
3916          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3917          * @param {Event} e The event
3918          * @param {Object} data An object containing arbitrary data supplied by the drag source
3919          */
3920         "enter" : true,
3921         
3922          /**
3923          * @event over
3924          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3925          * This method will be called on every mouse movement while the drag source is over the drop target.
3926          * This default implementation simply returns the dropAllowed config value.
3927          * 
3928          * IMPORTANT : it should set this.dropAllowed
3929          * 
3930          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3931          * @param {Event} e The event
3932          * @param {Object} data An object containing arbitrary data supplied by the drag source
3933          
3934          */
3935         "over" : true,
3936         /**
3937          * @event out
3938          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3939          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3940          * overClass (if any) from the drop element.
3941          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942          * @param {Event} e The event
3943          * @param {Object} data An object containing arbitrary data supplied by the drag source
3944          */
3945          "out" : true,
3946          
3947         /**
3948          * @event drop
3949          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3950          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3951          * implementation that does something to process the drop event and returns true so that the drag source's
3952          * repair action does not run.
3953          * 
3954          * IMPORTANT : it should set this.success
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959         */
3960          "drop" : true
3961     });
3962             
3963      
3964     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3965         this.el.dom, 
3966         this.ddGroup || this.group,
3967         {
3968             isTarget: true,
3969             listeners : listeners || {} 
3970            
3971         
3972         }
3973     );
3974
3975 };
3976
3977 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3978     /**
3979      * @cfg {String} overClass
3980      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3981      */
3982      /**
3983      * @cfg {String} ddGroup
3984      * The drag drop group to handle drop events for
3985      */
3986      
3987     /**
3988      * @cfg {String} dropAllowed
3989      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3990      */
3991     dropAllowed : "x-dd-drop-ok",
3992     /**
3993      * @cfg {String} dropNotAllowed
3994      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3995      */
3996     dropNotAllowed : "x-dd-drop-nodrop",
3997     /**
3998      * @cfg {boolean} success
3999      * set this after drop listener.. 
4000      */
4001     success : false,
4002     /**
4003      * @cfg {boolean} valid
4004      * if the drop point is valid for over/enter..
4005      */
4006     valid : false,
4007     // private
4008     isTarget : true,
4009
4010     // private
4011     isNotifyTarget : true,
4012     
4013     /**
4014      * @hide
4015      */
4016     notifyEnter : function(dd, e, data){
4017         this.valid = true;
4018         this.fireEvent('enter', this, dd, e, data);
4019         if(this.overClass){
4020             this.el.addClass(this.overClass);
4021         }
4022         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4023     },
4024
4025     /**
4026      * @hide
4027      */
4028     notifyOver : function(dd, e, data){
4029         this.valid = true;
4030         this.fireEvent('over', this, dd, e, data);
4031         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4032     },
4033
4034     /**
4035      * @hide
4036      */
4037     notifyOut : function(dd, e, data){
4038         this.fireEvent('out', this, dd, e, data);
4039         if(this.overClass){
4040             this.el.removeClass(this.overClass);
4041         }
4042     },
4043
4044     /**
4045      * @hide
4046      */
4047     notifyDrop : function(dd, e, data){
4048         this.success = false;
4049         this.fireEvent('drop', this, dd, e, data);
4050         return this.success;
4051     }
4052 });/*
4053  * Based on:
4054  * Ext JS Library 1.1.1
4055  * Copyright(c) 2006-2007, Ext JS, LLC.
4056  *
4057  * Originally Released Under LGPL - original licence link has changed is not relivant.
4058  *
4059  * Fork - LGPL
4060  * <script type="text/javascript">
4061  */
4062
4063
4064 /**
4065  * @class Roo.dd.DragZone
4066  * @extends Roo.dd.DragSource
4067  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4068  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4069  * @constructor
4070  * @param {String/HTMLElement/Element} el The container element
4071  * @param {Object} config
4072  */
4073 Roo.dd.DragZone = function(el, config){
4074     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4075     if(this.containerScroll){
4076         Roo.dd.ScrollManager.register(this.el);
4077     }
4078 };
4079
4080 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4081     /**
4082      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4083      * for auto scrolling during drag operations.
4084      */
4085     /**
4086      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4087      * method after a failed drop (defaults to "c3daf9" - light blue)
4088      */
4089
4090     /**
4091      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4092      * for a valid target to drag based on the mouse down. Override this method
4093      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4094      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4095      * @param {EventObject} e The mouse down event
4096      * @return {Object} The dragData
4097      */
4098     getDragData : function(e){
4099         return Roo.dd.Registry.getHandleFromEvent(e);
4100     },
4101     
4102     /**
4103      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4104      * this.dragData.ddel
4105      * @param {Number} x The x position of the click on the dragged object
4106      * @param {Number} y The y position of the click on the dragged object
4107      * @return {Boolean} true to continue the drag, false to cancel
4108      */
4109     onInitDrag : function(x, y){
4110         this.proxy.update(this.dragData.ddel.cloneNode(true));
4111         this.onStartDrag(x, y);
4112         return true;
4113     },
4114     
4115     /**
4116      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4117      */
4118     afterRepair : function(){
4119         if(Roo.enableFx){
4120             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4121         }
4122         this.dragging = false;
4123     },
4124
4125     /**
4126      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4127      * the XY of this.dragData.ddel
4128      * @param {EventObject} e The mouse up event
4129      * @return {Array} The xy location (e.g. [100, 200])
4130      */
4131     getRepairXY : function(e){
4132         return Roo.Element.fly(this.dragData.ddel).getXY();  
4133     }
4134 });/*
4135  * Based on:
4136  * Ext JS Library 1.1.1
4137  * Copyright(c) 2006-2007, Ext JS, LLC.
4138  *
4139  * Originally Released Under LGPL - original licence link has changed is not relivant.
4140  *
4141  * Fork - LGPL
4142  * <script type="text/javascript">
4143  */
4144 /**
4145  * @class Roo.dd.DropZone
4146  * @extends Roo.dd.DropTarget
4147  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4148  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4149  * @constructor
4150  * @param {String/HTMLElement/Element} el The container element
4151  * @param {Object} config
4152  */
4153 Roo.dd.DropZone = function(el, config){
4154     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4155 };
4156
4157 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4158     /**
4159      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4160      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4161      * provide your own custom lookup.
4162      * @param {Event} e The event
4163      * @return {Object} data The custom data
4164      */
4165     getTargetFromEvent : function(e){
4166         return Roo.dd.Registry.getTargetFromEvent(e);
4167     },
4168
4169     /**
4170      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4171      * that it has registered.  This method has no default implementation and should be overridden to provide
4172      * node-specific processing if necessary.
4173      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4174      * {@link #getTargetFromEvent} for this node)
4175      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4176      * @param {Event} e The event
4177      * @param {Object} data An object containing arbitrary data supplied by the drag source
4178      */
4179     onNodeEnter : function(n, dd, e, data){
4180         
4181     },
4182
4183     /**
4184      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4185      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4186      * overridden to provide the proper feedback.
4187      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4188      * {@link #getTargetFromEvent} for this node)
4189      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4190      * @param {Event} e The event
4191      * @param {Object} data An object containing arbitrary data supplied by the drag source
4192      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4193      * underlying {@link Roo.dd.StatusProxy} can be updated
4194      */
4195     onNodeOver : function(n, dd, e, data){
4196         return this.dropAllowed;
4197     },
4198
4199     /**
4200      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4201      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4202      * node-specific processing if necessary.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      */
4209     onNodeOut : function(n, dd, e, data){
4210         
4211     },
4212
4213     /**
4214      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4215      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4216      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4217      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4218      * {@link #getTargetFromEvent} for this node)
4219      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4220      * @param {Event} e The event
4221      * @param {Object} data An object containing arbitrary data supplied by the drag source
4222      * @return {Boolean} True if the drop was valid, else false
4223      */
4224     onNodeDrop : function(n, dd, e, data){
4225         return false;
4226     },
4227
4228     /**
4229      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4230      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4231      * it should be overridden to provide the proper feedback if necessary.
4232      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4233      * @param {Event} e The event
4234      * @param {Object} data An object containing arbitrary data supplied by the drag source
4235      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4236      * underlying {@link Roo.dd.StatusProxy} can be updated
4237      */
4238     onContainerOver : function(dd, e, data){
4239         return this.dropNotAllowed;
4240     },
4241
4242     /**
4243      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4244      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4245      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4246      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4247      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4248      * @param {Event} e The event
4249      * @param {Object} data An object containing arbitrary data supplied by the drag source
4250      * @return {Boolean} True if the drop was valid, else false
4251      */
4252     onContainerDrop : function(dd, e, data){
4253         return false;
4254     },
4255
4256     /**
4257      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4258      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4259      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4260      * you should override this method and provide a custom implementation.
4261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4262      * @param {Event} e The event
4263      * @param {Object} data An object containing arbitrary data supplied by the drag source
4264      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4265      * underlying {@link Roo.dd.StatusProxy} can be updated
4266      */
4267     notifyEnter : function(dd, e, data){
4268         return this.dropNotAllowed;
4269     },
4270
4271     /**
4272      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4273      * This method will be called on every mouse movement while the drag source is over the drop zone.
4274      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4275      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4276      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4277      * registered node, it will call {@link #onContainerOver}.
4278      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4279      * @param {Event} e The event
4280      * @param {Object} data An object containing arbitrary data supplied by the drag source
4281      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4282      * underlying {@link Roo.dd.StatusProxy} can be updated
4283      */
4284     notifyOver : function(dd, e, data){
4285         var n = this.getTargetFromEvent(e);
4286         if(!n){ // not over valid drop target
4287             if(this.lastOverNode){
4288                 this.onNodeOut(this.lastOverNode, dd, e, data);
4289                 this.lastOverNode = null;
4290             }
4291             return this.onContainerOver(dd, e, data);
4292         }
4293         if(this.lastOverNode != n){
4294             if(this.lastOverNode){
4295                 this.onNodeOut(this.lastOverNode, dd, e, data);
4296             }
4297             this.onNodeEnter(n, dd, e, data);
4298             this.lastOverNode = n;
4299         }
4300         return this.onNodeOver(n, dd, e, data);
4301     },
4302
4303     /**
4304      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4305      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4306      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4307      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4308      * @param {Event} e The event
4309      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4310      */
4311     notifyOut : function(dd, e, data){
4312         if(this.lastOverNode){
4313             this.onNodeOut(this.lastOverNode, dd, e, data);
4314             this.lastOverNode = null;
4315         }
4316     },
4317
4318     /**
4319      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4320      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4321      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4322      * otherwise it will call {@link #onContainerDrop}.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag source
4326      * @return {Boolean} True if the drop was valid, else false
4327      */
4328     notifyDrop : function(dd, e, data){
4329         if(this.lastOverNode){
4330             this.onNodeOut(this.lastOverNode, dd, e, data);
4331             this.lastOverNode = null;
4332         }
4333         var n = this.getTargetFromEvent(e);
4334         return n ?
4335             this.onNodeDrop(n, dd, e, data) :
4336             this.onContainerDrop(dd, e, data);
4337     },
4338
4339     // private
4340     triggerCacheRefresh : function(){
4341         Roo.dd.DDM.refreshCache(this.groups);
4342     }  
4343 });/*
4344  * Based on:
4345  * Ext JS Library 1.1.1
4346  * Copyright(c) 2006-2007, Ext JS, LLC.
4347  *
4348  * Originally Released Under LGPL - original licence link has changed is not relivant.
4349  *
4350  * Fork - LGPL
4351  * <script type="text/javascript">
4352  */
4353
4354
4355 /**
4356  * @class Roo.data.SortTypes
4357  * @singleton
4358  * Defines the default sorting (casting?) comparison functions used when sorting data.
4359  */
4360 Roo.data.SortTypes = {
4361     /**
4362      * Default sort that does nothing
4363      * @param {Mixed} s The value being converted
4364      * @return {Mixed} The comparison value
4365      */
4366     none : function(s){
4367         return s;
4368     },
4369     
4370     /**
4371      * The regular expression used to strip tags
4372      * @type {RegExp}
4373      * @property
4374      */
4375     stripTagsRE : /<\/?[^>]+>/gi,
4376     
4377     /**
4378      * Strips all HTML tags to sort on text only
4379      * @param {Mixed} s The value being converted
4380      * @return {String} The comparison value
4381      */
4382     asText : function(s){
4383         return String(s).replace(this.stripTagsRE, "");
4384     },
4385     
4386     /**
4387      * Strips all HTML tags to sort on text only - Case insensitive
4388      * @param {Mixed} s The value being converted
4389      * @return {String} The comparison value
4390      */
4391     asUCText : function(s){
4392         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4393     },
4394     
4395     /**
4396      * Case insensitive string
4397      * @param {Mixed} s The value being converted
4398      * @return {String} The comparison value
4399      */
4400     asUCString : function(s) {
4401         return String(s).toUpperCase();
4402     },
4403     
4404     /**
4405      * Date sorting
4406      * @param {Mixed} s The value being converted
4407      * @return {Number} The comparison value
4408      */
4409     asDate : function(s) {
4410         if(!s){
4411             return 0;
4412         }
4413         if(s instanceof Date){
4414             return s.getTime();
4415         }
4416         return Date.parse(String(s));
4417     },
4418     
4419     /**
4420      * Float sorting
4421      * @param {Mixed} s The value being converted
4422      * @return {Float} The comparison value
4423      */
4424     asFloat : function(s) {
4425         var val = parseFloat(String(s).replace(/,/g, ""));
4426         if(isNaN(val)) val = 0;
4427         return val;
4428     },
4429     
4430     /**
4431      * Integer sorting
4432      * @param {Mixed} s The value being converted
4433      * @return {Number} The comparison value
4434      */
4435     asInt : function(s) {
4436         var val = parseInt(String(s).replace(/,/g, ""));
4437         if(isNaN(val)) val = 0;
4438         return val;
4439     }
4440 };/*
4441  * Based on:
4442  * Ext JS Library 1.1.1
4443  * Copyright(c) 2006-2007, Ext JS, LLC.
4444  *
4445  * Originally Released Under LGPL - original licence link has changed is not relivant.
4446  *
4447  * Fork - LGPL
4448  * <script type="text/javascript">
4449  */
4450
4451 /**
4452 * @class Roo.data.Record
4453  * Instances of this class encapsulate both record <em>definition</em> information, and record
4454  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4455  * to access Records cached in an {@link Roo.data.Store} object.<br>
4456  * <p>
4457  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4458  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4459  * objects.<br>
4460  * <p>
4461  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4462  * @constructor
4463  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4464  * {@link #create}. The parameters are the same.
4465  * @param {Array} data An associative Array of data values keyed by the field name.
4466  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4467  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4468  * not specified an integer id is generated.
4469  */
4470 Roo.data.Record = function(data, id){
4471     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4472     this.data = data;
4473 };
4474
4475 /**
4476  * Generate a constructor for a specific record layout.
4477  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4478  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4479  * Each field definition object may contain the following properties: <ul>
4480  * <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,
4481  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4482  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4483  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4484  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4485  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4486  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4487  * this may be omitted.</p></li>
4488  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4489  * <ul><li>auto (Default, implies no conversion)</li>
4490  * <li>string</li>
4491  * <li>int</li>
4492  * <li>float</li>
4493  * <li>boolean</li>
4494  * <li>date</li></ul></p></li>
4495  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4496  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4497  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4498  * by the Reader into an object that will be stored in the Record. It is passed the
4499  * following parameters:<ul>
4500  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4501  * </ul></p></li>
4502  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4503  * </ul>
4504  * <br>usage:<br><pre><code>
4505 var TopicRecord = Roo.data.Record.create(
4506     {name: 'title', mapping: 'topic_title'},
4507     {name: 'author', mapping: 'username'},
4508     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4509     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4510     {name: 'lastPoster', mapping: 'user2'},
4511     {name: 'excerpt', mapping: 'post_text'}
4512 );
4513
4514 var myNewRecord = new TopicRecord({
4515     title: 'Do my job please',
4516     author: 'noobie',
4517     totalPosts: 1,
4518     lastPost: new Date(),
4519     lastPoster: 'Animal',
4520     excerpt: 'No way dude!'
4521 });
4522 myStore.add(myNewRecord);
4523 </code></pre>
4524  * @method create
4525  * @static
4526  */
4527 Roo.data.Record.create = function(o){
4528     var f = function(){
4529         f.superclass.constructor.apply(this, arguments);
4530     };
4531     Roo.extend(f, Roo.data.Record);
4532     var p = f.prototype;
4533     p.fields = new Roo.util.MixedCollection(false, function(field){
4534         return field.name;
4535     });
4536     for(var i = 0, len = o.length; i < len; i++){
4537         p.fields.add(new Roo.data.Field(o[i]));
4538     }
4539     f.getField = function(name){
4540         return p.fields.get(name);  
4541     };
4542     return f;
4543 };
4544
4545 Roo.data.Record.AUTO_ID = 1000;
4546 Roo.data.Record.EDIT = 'edit';
4547 Roo.data.Record.REJECT = 'reject';
4548 Roo.data.Record.COMMIT = 'commit';
4549
4550 Roo.data.Record.prototype = {
4551     /**
4552      * Readonly flag - true if this record has been modified.
4553      * @type Boolean
4554      */
4555     dirty : false,
4556     editing : false,
4557     error: null,
4558     modified: null,
4559
4560     // private
4561     join : function(store){
4562         this.store = store;
4563     },
4564
4565     /**
4566      * Set the named field to the specified value.
4567      * @param {String} name The name of the field to set.
4568      * @param {Object} value The value to set the field to.
4569      */
4570     set : function(name, value){
4571         if(this.data[name] == value){
4572             return;
4573         }
4574         this.dirty = true;
4575         if(!this.modified){
4576             this.modified = {};
4577         }
4578         if(typeof this.modified[name] == 'undefined'){
4579             this.modified[name] = this.data[name];
4580         }
4581         this.data[name] = value;
4582         if(!this.editing){
4583             this.store.afterEdit(this);
4584         }       
4585     },
4586
4587     /**
4588      * Get the value of the named field.
4589      * @param {String} name The name of the field to get the value of.
4590      * @return {Object} The value of the field.
4591      */
4592     get : function(name){
4593         return this.data[name]; 
4594     },
4595
4596     // private
4597     beginEdit : function(){
4598         this.editing = true;
4599         this.modified = {}; 
4600     },
4601
4602     // private
4603     cancelEdit : function(){
4604         this.editing = false;
4605         delete this.modified;
4606     },
4607
4608     // private
4609     endEdit : function(){
4610         this.editing = false;
4611         if(this.dirty && this.store){
4612             this.store.afterEdit(this);
4613         }
4614     },
4615
4616     /**
4617      * Usually called by the {@link Roo.data.Store} which owns the Record.
4618      * Rejects all changes made to the Record since either creation, or the last commit operation.
4619      * Modified fields are reverted to their original values.
4620      * <p>
4621      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4622      * of reject operations.
4623      */
4624     reject : function(){
4625         var m = this.modified;
4626         for(var n in m){
4627             if(typeof m[n] != "function"){
4628                 this.data[n] = m[n];
4629             }
4630         }
4631         this.dirty = false;
4632         delete this.modified;
4633         this.editing = false;
4634         if(this.store){
4635             this.store.afterReject(this);
4636         }
4637     },
4638
4639     /**
4640      * Usually called by the {@link Roo.data.Store} which owns the Record.
4641      * Commits all changes made to the Record since either creation, or the last commit operation.
4642      * <p>
4643      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4644      * of commit operations.
4645      */
4646     commit : function(){
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterCommit(this);
4652         }
4653     },
4654
4655     // private
4656     hasError : function(){
4657         return this.error != null;
4658     },
4659
4660     // private
4661     clearError : function(){
4662         this.error = null;
4663     },
4664
4665     /**
4666      * Creates a copy of this record.
4667      * @param {String} id (optional) A new record id if you don't want to use this record's id
4668      * @return {Record}
4669      */
4670     copy : function(newId) {
4671         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4672     }
4673 };/*
4674  * Based on:
4675  * Ext JS Library 1.1.1
4676  * Copyright(c) 2006-2007, Ext JS, LLC.
4677  *
4678  * Originally Released Under LGPL - original licence link has changed is not relivant.
4679  *
4680  * Fork - LGPL
4681  * <script type="text/javascript">
4682  */
4683
4684
4685
4686 /**
4687  * @class Roo.data.Store
4688  * @extends Roo.util.Observable
4689  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4690  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4691  * <p>
4692  * 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
4693  * has no knowledge of the format of the data returned by the Proxy.<br>
4694  * <p>
4695  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4696  * instances from the data object. These records are cached and made available through accessor functions.
4697  * @constructor
4698  * Creates a new Store.
4699  * @param {Object} config A config object containing the objects needed for the Store to access data,
4700  * and read the data into Records.
4701  */
4702 Roo.data.Store = function(config){
4703     this.data = new Roo.util.MixedCollection(false);
4704     this.data.getKey = function(o){
4705         return o.id;
4706     };
4707     this.baseParams = {};
4708     // private
4709     this.paramNames = {
4710         "start" : "start",
4711         "limit" : "limit",
4712         "sort" : "sort",
4713         "dir" : "dir"
4714     };
4715
4716     if(config && config.data){
4717         this.inlineData = config.data;
4718         delete config.data;
4719     }
4720
4721     Roo.apply(this, config);
4722     
4723     if(this.reader){ // reader passed
4724         this.reader = Roo.factory(this.reader, Roo.data);
4725         this.reader.xmodule = this.xmodule || false;
4726         if(!this.recordType){
4727             this.recordType = this.reader.recordType;
4728         }
4729         if(this.reader.onMetaChange){
4730             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4731         }
4732     }
4733
4734     if(this.recordType){
4735         this.fields = this.recordType.prototype.fields;
4736     }
4737     this.modified = [];
4738
4739     this.addEvents({
4740         /**
4741          * @event datachanged
4742          * Fires when the data cache has changed, and a widget which is using this Store
4743          * as a Record cache should refresh its view.
4744          * @param {Store} this
4745          */
4746         datachanged : true,
4747         /**
4748          * @event metachange
4749          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4750          * @param {Store} this
4751          * @param {Object} meta The JSON metadata
4752          */
4753         metachange : true,
4754         /**
4755          * @event add
4756          * Fires when Records have been added to the Store
4757          * @param {Store} this
4758          * @param {Roo.data.Record[]} records The array of Records added
4759          * @param {Number} index The index at which the record(s) were added
4760          */
4761         add : true,
4762         /**
4763          * @event remove
4764          * Fires when a Record has been removed from the Store
4765          * @param {Store} this
4766          * @param {Roo.data.Record} record The Record that was removed
4767          * @param {Number} index The index at which the record was removed
4768          */
4769         remove : true,
4770         /**
4771          * @event update
4772          * Fires when a Record has been updated
4773          * @param {Store} this
4774          * @param {Roo.data.Record} record The Record that was updated
4775          * @param {String} operation The update operation being performed.  Value may be one of:
4776          * <pre><code>
4777  Roo.data.Record.EDIT
4778  Roo.data.Record.REJECT
4779  Roo.data.Record.COMMIT
4780          * </code></pre>
4781          */
4782         update : true,
4783         /**
4784          * @event clear
4785          * Fires when the data cache has been cleared.
4786          * @param {Store} this
4787          */
4788         clear : true,
4789         /**
4790          * @event beforeload
4791          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4792          * the load action will be canceled.
4793          * @param {Store} this
4794          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4795          */
4796         beforeload : true,
4797         /**
4798          * @event load
4799          * Fires after a new set of Records has been loaded.
4800          * @param {Store} this
4801          * @param {Roo.data.Record[]} records The Records that were loaded
4802          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4803          */
4804         load : true,
4805         /**
4806          * @event loadexception
4807          * Fires if an exception occurs in the Proxy during loading.
4808          * Called with the signature of the Proxy's "loadexception" event.
4809          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4810          * 
4811          * @param {Proxy} 
4812          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4813          * @param {Object} load options 
4814          * @param {Object} jsonData from your request (normally this contains the Exception)
4815          */
4816         loadexception : true
4817     });
4818     
4819     if(this.proxy){
4820         this.proxy = Roo.factory(this.proxy, Roo.data);
4821         this.proxy.xmodule = this.xmodule || false;
4822         this.relayEvents(this.proxy,  ["loadexception"]);
4823     }
4824     this.sortToggle = {};
4825
4826     Roo.data.Store.superclass.constructor.call(this);
4827
4828     if(this.inlineData){
4829         this.loadData(this.inlineData);
4830         delete this.inlineData;
4831     }
4832 };
4833 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4834      /**
4835     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4836     * without a remote query - used by combo/forms at present.
4837     */
4838     
4839     /**
4840     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4841     */
4842     /**
4843     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4844     */
4845     /**
4846     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4847     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4848     */
4849     /**
4850     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4851     * on any HTTP request
4852     */
4853     /**
4854     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4855     */
4856     /**
4857     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4858     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4859     */
4860     remoteSort : false,
4861
4862     /**
4863     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4864      * loaded or when a record is removed. (defaults to false).
4865     */
4866     pruneModifiedRecords : false,
4867
4868     // private
4869     lastOptions : null,
4870
4871     /**
4872      * Add Records to the Store and fires the add event.
4873      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4874      */
4875     add : function(records){
4876         records = [].concat(records);
4877         for(var i = 0, len = records.length; i < len; i++){
4878             records[i].join(this);
4879         }
4880         var index = this.data.length;
4881         this.data.addAll(records);
4882         this.fireEvent("add", this, records, index);
4883     },
4884
4885     /**
4886      * Remove a Record from the Store and fires the remove event.
4887      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4888      */
4889     remove : function(record){
4890         var index = this.data.indexOf(record);
4891         this.data.removeAt(index);
4892         if(this.pruneModifiedRecords){
4893             this.modified.remove(record);
4894         }
4895         this.fireEvent("remove", this, record, index);
4896     },
4897
4898     /**
4899      * Remove all Records from the Store and fires the clear event.
4900      */
4901     removeAll : function(){
4902         this.data.clear();
4903         if(this.pruneModifiedRecords){
4904             this.modified = [];
4905         }
4906         this.fireEvent("clear", this);
4907     },
4908
4909     /**
4910      * Inserts Records to the Store at the given index and fires the add event.
4911      * @param {Number} index The start index at which to insert the passed Records.
4912      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4913      */
4914     insert : function(index, records){
4915         records = [].concat(records);
4916         for(var i = 0, len = records.length; i < len; i++){
4917             this.data.insert(index, records[i]);
4918             records[i].join(this);
4919         }
4920         this.fireEvent("add", this, records, index);
4921     },
4922
4923     /**
4924      * Get the index within the cache of the passed Record.
4925      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4926      * @return {Number} The index of the passed Record. Returns -1 if not found.
4927      */
4928     indexOf : function(record){
4929         return this.data.indexOf(record);
4930     },
4931
4932     /**
4933      * Get the index within the cache of the Record with the passed id.
4934      * @param {String} id The id of the Record to find.
4935      * @return {Number} The index of the Record. Returns -1 if not found.
4936      */
4937     indexOfId : function(id){
4938         return this.data.indexOfKey(id);
4939     },
4940
4941     /**
4942      * Get the Record with the specified id.
4943      * @param {String} id The id of the Record to find.
4944      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4945      */
4946     getById : function(id){
4947         return this.data.key(id);
4948     },
4949
4950     /**
4951      * Get the Record at the specified index.
4952      * @param {Number} index The index of the Record to find.
4953      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4954      */
4955     getAt : function(index){
4956         return this.data.itemAt(index);
4957     },
4958
4959     /**
4960      * Returns a range of Records between specified indices.
4961      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4962      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4963      * @return {Roo.data.Record[]} An array of Records
4964      */
4965     getRange : function(start, end){
4966         return this.data.getRange(start, end);
4967     },
4968
4969     // private
4970     storeOptions : function(o){
4971         o = Roo.apply({}, o);
4972         delete o.callback;
4973         delete o.scope;
4974         this.lastOptions = o;
4975     },
4976
4977     /**
4978      * Loads the Record cache from the configured Proxy using the configured Reader.
4979      * <p>
4980      * If using remote paging, then the first load call must specify the <em>start</em>
4981      * and <em>limit</em> properties in the options.params property to establish the initial
4982      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4983      * <p>
4984      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4985      * and this call will return before the new data has been loaded. Perform any post-processing
4986      * in a callback function, or in a "load" event handler.</strong>
4987      * <p>
4988      * @param {Object} options An object containing properties which control loading options:<ul>
4989      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4990      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4991      * passed the following arguments:<ul>
4992      * <li>r : Roo.data.Record[]</li>
4993      * <li>options: Options object from the load call</li>
4994      * <li>success: Boolean success indicator</li></ul></li>
4995      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4996      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4997      * </ul>
4998      */
4999     load : function(options){
5000         options = options || {};
5001         if(this.fireEvent("beforeload", this, options) !== false){
5002             this.storeOptions(options);
5003             var p = Roo.apply(options.params || {}, this.baseParams);
5004             // if meta was not loaded from remote source.. try requesting it.
5005             if (!this.reader.metaFromRemote) {
5006                 p._requestMeta = 1;
5007             }
5008             if(this.sortInfo && this.remoteSort){
5009                 var pn = this.paramNames;
5010                 p[pn["sort"]] = this.sortInfo.field;
5011                 p[pn["dir"]] = this.sortInfo.direction;
5012             }
5013             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5014         }
5015     },
5016
5017     /**
5018      * Reloads the Record cache from the configured Proxy using the configured Reader and
5019      * the options from the last load operation performed.
5020      * @param {Object} options (optional) An object containing properties which may override the options
5021      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5022      * the most recently used options are reused).
5023      */
5024     reload : function(options){
5025         this.load(Roo.applyIf(options||{}, this.lastOptions));
5026     },
5027
5028     // private
5029     // Called as a callback by the Reader during a load operation.
5030     loadRecords : function(o, options, success){
5031         if(!o || success === false){
5032             if(success !== false){
5033                 this.fireEvent("load", this, [], options);
5034             }
5035             if(options.callback){
5036                 options.callback.call(options.scope || this, [], options, false);
5037             }
5038             return;
5039         }
5040         // if data returned failure - throw an exception.
5041         if (o.success === false) {
5042             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5043             return;
5044         }
5045         var r = o.records, t = o.totalRecords || r.length;
5046         if(!options || options.add !== true){
5047             if(this.pruneModifiedRecords){
5048                 this.modified = [];
5049             }
5050             for(var i = 0, len = r.length; i < len; i++){
5051                 r[i].join(this);
5052             }
5053             if(this.snapshot){
5054                 this.data = this.snapshot;
5055                 delete this.snapshot;
5056             }
5057             this.data.clear();
5058             this.data.addAll(r);
5059             this.totalLength = t;
5060             this.applySort();
5061             this.fireEvent("datachanged", this);
5062         }else{
5063             this.totalLength = Math.max(t, this.data.length+r.length);
5064             this.add(r);
5065         }
5066         this.fireEvent("load", this, r, options);
5067         if(options.callback){
5068             options.callback.call(options.scope || this, r, options, true);
5069         }
5070     },
5071
5072     /**
5073      * Loads data from a passed data block. A Reader which understands the format of the data
5074      * must have been configured in the constructor.
5075      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5076      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5077      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5078      */
5079     loadData : function(o, append){
5080         var r = this.reader.readRecords(o);
5081         this.loadRecords(r, {add: append}, true);
5082     },
5083
5084     /**
5085      * Gets the number of cached records.
5086      * <p>
5087      * <em>If using paging, this may not be the total size of the dataset. If the data object
5088      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5089      * the data set size</em>
5090      */
5091     getCount : function(){
5092         return this.data.length || 0;
5093     },
5094
5095     /**
5096      * Gets the total number of records in the dataset as returned by the server.
5097      * <p>
5098      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5099      * the dataset size</em>
5100      */
5101     getTotalCount : function(){
5102         return this.totalLength || 0;
5103     },
5104
5105     /**
5106      * Returns the sort state of the Store as an object with two properties:
5107      * <pre><code>
5108  field {String} The name of the field by which the Records are sorted
5109  direction {String} The sort order, "ASC" or "DESC"
5110      * </code></pre>
5111      */
5112     getSortState : function(){
5113         return this.sortInfo;
5114     },
5115
5116     // private
5117     applySort : function(){
5118         if(this.sortInfo && !this.remoteSort){
5119             var s = this.sortInfo, f = s.field;
5120             var st = this.fields.get(f).sortType;
5121             var fn = function(r1, r2){
5122                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5123                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5124             };
5125             this.data.sort(s.direction, fn);
5126             if(this.snapshot && this.snapshot != this.data){
5127                 this.snapshot.sort(s.direction, fn);
5128             }
5129         }
5130     },
5131
5132     /**
5133      * Sets the default sort column and order to be used by the next load operation.
5134      * @param {String} fieldName The name of the field to sort by.
5135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5136      */
5137     setDefaultSort : function(field, dir){
5138         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5139     },
5140
5141     /**
5142      * Sort the Records.
5143      * If remote sorting is used, the sort is performed on the server, and the cache is
5144      * reloaded. If local sorting is used, the cache is sorted internally.
5145      * @param {String} fieldName The name of the field to sort by.
5146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5147      */
5148     sort : function(fieldName, dir){
5149         var f = this.fields.get(fieldName);
5150         if(!dir){
5151             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5152                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5153             }else{
5154                 dir = f.sortDir;
5155             }
5156         }
5157         this.sortToggle[f.name] = dir;
5158         this.sortInfo = {field: f.name, direction: dir};
5159         if(!this.remoteSort){
5160             this.applySort();
5161             this.fireEvent("datachanged", this);
5162         }else{
5163             this.load(this.lastOptions);
5164         }
5165     },
5166
5167     /**
5168      * Calls the specified function for each of the Records in the cache.
5169      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5170      * Returning <em>false</em> aborts and exits the iteration.
5171      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5172      */
5173     each : function(fn, scope){
5174         this.data.each(fn, scope);
5175     },
5176
5177     /**
5178      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5179      * (e.g., during paging).
5180      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5181      */
5182     getModifiedRecords : function(){
5183         return this.modified;
5184     },
5185
5186     // private
5187     createFilterFn : function(property, value, anyMatch){
5188         if(!value.exec){ // not a regex
5189             value = String(value);
5190             if(value.length == 0){
5191                 return false;
5192             }
5193             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5194         }
5195         return function(r){
5196             return value.test(r.data[property]);
5197         };
5198     },
5199
5200     /**
5201      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5202      * @param {String} property A field on your records
5203      * @param {Number} start The record index to start at (defaults to 0)
5204      * @param {Number} end The last record index to include (defaults to length - 1)
5205      * @return {Number} The sum
5206      */
5207     sum : function(property, start, end){
5208         var rs = this.data.items, v = 0;
5209         start = start || 0;
5210         end = (end || end === 0) ? end : rs.length-1;
5211
5212         for(var i = start; i <= end; i++){
5213             v += (rs[i].data[property] || 0);
5214         }
5215         return v;
5216     },
5217
5218     /**
5219      * Filter the records by a specified property.
5220      * @param {String} field A field on your records
5221      * @param {String/RegExp} value Either a string that the field
5222      * should start with or a RegExp to test against the field
5223      * @param {Boolean} anyMatch True to match any part not just the beginning
5224      */
5225     filter : function(property, value, anyMatch){
5226         var fn = this.createFilterFn(property, value, anyMatch);
5227         return fn ? this.filterBy(fn) : this.clearFilter();
5228     },
5229
5230     /**
5231      * Filter by a function. The specified function will be called with each
5232      * record in this data source. If the function returns true the record is included,
5233      * otherwise it is filtered.
5234      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5235      * @param {Object} scope (optional) The scope of the function (defaults to this)
5236      */
5237     filterBy : function(fn, scope){
5238         this.snapshot = this.snapshot || this.data;
5239         this.data = this.queryBy(fn, scope||this);
5240         this.fireEvent("datachanged", this);
5241     },
5242
5243     /**
5244      * Query the records by a specified property.
5245      * @param {String} field A field on your records
5246      * @param {String/RegExp} value Either a string that the field
5247      * should start with or a RegExp to test against the field
5248      * @param {Boolean} anyMatch True to match any part not just the beginning
5249      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5250      */
5251     query : function(property, value, anyMatch){
5252         var fn = this.createFilterFn(property, value, anyMatch);
5253         return fn ? this.queryBy(fn) : this.data.clone();
5254     },
5255
5256     /**
5257      * Query by a function. The specified function will be called with each
5258      * record in this data source. If the function returns true the record is included
5259      * in the results.
5260      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5261      * @param {Object} scope (optional) The scope of the function (defaults to this)
5262       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5263      **/
5264     queryBy : function(fn, scope){
5265         var data = this.snapshot || this.data;
5266         return data.filterBy(fn, scope||this);
5267     },
5268
5269     /**
5270      * Collects unique values for a particular dataIndex from this store.
5271      * @param {String} dataIndex The property to collect
5272      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5273      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5274      * @return {Array} An array of the unique values
5275      **/
5276     collect : function(dataIndex, allowNull, bypassFilter){
5277         var d = (bypassFilter === true && this.snapshot) ?
5278                 this.snapshot.items : this.data.items;
5279         var v, sv, r = [], l = {};
5280         for(var i = 0, len = d.length; i < len; i++){
5281             v = d[i].data[dataIndex];
5282             sv = String(v);
5283             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5284                 l[sv] = true;
5285                 r[r.length] = v;
5286             }
5287         }
5288         return r;
5289     },
5290
5291     /**
5292      * Revert to a view of the Record cache with no filtering applied.
5293      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5294      */
5295     clearFilter : function(suppressEvent){
5296         if(this.snapshot && this.snapshot != this.data){
5297             this.data = this.snapshot;
5298             delete this.snapshot;
5299             if(suppressEvent !== true){
5300                 this.fireEvent("datachanged", this);
5301             }
5302         }
5303     },
5304
5305     // private
5306     afterEdit : function(record){
5307         if(this.modified.indexOf(record) == -1){
5308             this.modified.push(record);
5309         }
5310         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5311     },
5312
5313     // private
5314     afterReject : function(record){
5315         this.modified.remove(record);
5316         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5317     },
5318
5319     // private
5320     afterCommit : function(record){
5321         this.modified.remove(record);
5322         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5323     },
5324
5325     /**
5326      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5327      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5328      */
5329     commitChanges : function(){
5330         var m = this.modified.slice(0);
5331         this.modified = [];
5332         for(var i = 0, len = m.length; i < len; i++){
5333             m[i].commit();
5334         }
5335     },
5336
5337     /**
5338      * Cancel outstanding changes on all changed records.
5339      */
5340     rejectChanges : function(){
5341         var m = this.modified.slice(0);
5342         this.modified = [];
5343         for(var i = 0, len = m.length; i < len; i++){
5344             m[i].reject();
5345         }
5346     },
5347
5348     onMetaChange : function(meta, rtype, o){
5349         this.recordType = rtype;
5350         this.fields = rtype.prototype.fields;
5351         delete this.snapshot;
5352         this.sortInfo = meta.sortInfo || this.sortInfo;
5353         this.modified = [];
5354         this.fireEvent('metachange', this, this.reader.meta);
5355     }
5356 });/*
5357  * Based on:
5358  * Ext JS Library 1.1.1
5359  * Copyright(c) 2006-2007, Ext JS, LLC.
5360  *
5361  * Originally Released Under LGPL - original licence link has changed is not relivant.
5362  *
5363  * Fork - LGPL
5364  * <script type="text/javascript">
5365  */
5366
5367 /**
5368  * @class Roo.data.SimpleStore
5369  * @extends Roo.data.Store
5370  * Small helper class to make creating Stores from Array data easier.
5371  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5372  * @cfg {Array} fields An array of field definition objects, or field name strings.
5373  * @cfg {Array} data The multi-dimensional array of data
5374  * @constructor
5375  * @param {Object} config
5376  */
5377 Roo.data.SimpleStore = function(config){
5378     Roo.data.SimpleStore.superclass.constructor.call(this, {
5379         isLocal : true,
5380         reader: new Roo.data.ArrayReader({
5381                 id: config.id
5382             },
5383             Roo.data.Record.create(config.fields)
5384         ),
5385         proxy : new Roo.data.MemoryProxy(config.data)
5386     });
5387     this.load();
5388 };
5389 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5390  * Based on:
5391  * Ext JS Library 1.1.1
5392  * Copyright(c) 2006-2007, Ext JS, LLC.
5393  *
5394  * Originally Released Under LGPL - original licence link has changed is not relivant.
5395  *
5396  * Fork - LGPL
5397  * <script type="text/javascript">
5398  */
5399
5400 /**
5401 /**
5402  * @extends Roo.data.Store
5403  * @class Roo.data.JsonStore
5404  * Small helper class to make creating Stores for JSON data easier. <br/>
5405 <pre><code>
5406 var store = new Roo.data.JsonStore({
5407     url: 'get-images.php',
5408     root: 'images',
5409     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5410 });
5411 </code></pre>
5412  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5413  * JsonReader and HttpProxy (unless inline data is provided).</b>
5414  * @cfg {Array} fields An array of field definition objects, or field name strings.
5415  * @constructor
5416  * @param {Object} config
5417  */
5418 Roo.data.JsonStore = function(c){
5419     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5420         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5421         reader: new Roo.data.JsonReader(c, c.fields)
5422     }));
5423 };
5424 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435  
5436 Roo.data.Field = function(config){
5437     if(typeof config == "string"){
5438         config = {name: config};
5439     }
5440     Roo.apply(this, config);
5441     
5442     if(!this.type){
5443         this.type = "auto";
5444     }
5445     
5446     var st = Roo.data.SortTypes;
5447     // named sortTypes are supported, here we look them up
5448     if(typeof this.sortType == "string"){
5449         this.sortType = st[this.sortType];
5450     }
5451     
5452     // set default sortType for strings and dates
5453     if(!this.sortType){
5454         switch(this.type){
5455             case "string":
5456                 this.sortType = st.asUCString;
5457                 break;
5458             case "date":
5459                 this.sortType = st.asDate;
5460                 break;
5461             default:
5462                 this.sortType = st.none;
5463         }
5464     }
5465
5466     // define once
5467     var stripRe = /[\$,%]/g;
5468
5469     // prebuilt conversion function for this field, instead of
5470     // switching every time we're reading a value
5471     if(!this.convert){
5472         var cv, dateFormat = this.dateFormat;
5473         switch(this.type){
5474             case "":
5475             case "auto":
5476             case undefined:
5477                 cv = function(v){ return v; };
5478                 break;
5479             case "string":
5480                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5481                 break;
5482             case "int":
5483                 cv = function(v){
5484                     return v !== undefined && v !== null && v !== '' ?
5485                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5486                     };
5487                 break;
5488             case "float":
5489                 cv = function(v){
5490                     return v !== undefined && v !== null && v !== '' ?
5491                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5492                     };
5493                 break;
5494             case "bool":
5495             case "boolean":
5496                 cv = function(v){ return v === true || v === "true" || v == 1; };
5497                 break;
5498             case "date":
5499                 cv = function(v){
5500                     if(!v){
5501                         return '';
5502                     }
5503                     if(v instanceof Date){
5504                         return v;
5505                     }
5506                     if(dateFormat){
5507                         if(dateFormat == "timestamp"){
5508                             return new Date(v*1000);
5509                         }
5510                         return Date.parseDate(v, dateFormat);
5511                     }
5512                     var parsed = Date.parse(v);
5513                     return parsed ? new Date(parsed) : null;
5514                 };
5515              break;
5516             
5517         }
5518         this.convert = cv;
5519     }
5520 };
5521
5522 Roo.data.Field.prototype = {
5523     dateFormat: null,
5524     defaultValue: "",
5525     mapping: null,
5526     sortType : null,
5527     sortDir : "ASC"
5528 };/*
5529  * Based on:
5530  * Ext JS Library 1.1.1
5531  * Copyright(c) 2006-2007, Ext JS, LLC.
5532  *
5533  * Originally Released Under LGPL - original licence link has changed is not relivant.
5534  *
5535  * Fork - LGPL
5536  * <script type="text/javascript">
5537  */
5538  
5539 // Base class for reading structured data from a data source.  This class is intended to be
5540 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5541
5542 /**
5543  * @class Roo.data.DataReader
5544  * Base class for reading structured data from a data source.  This class is intended to be
5545  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5546  */
5547
5548 Roo.data.DataReader = function(meta, recordType){
5549     
5550     this.meta = meta;
5551     
5552     this.recordType = recordType instanceof Array ? 
5553         Roo.data.Record.create(recordType) : recordType;
5554 };
5555
5556 Roo.data.DataReader.prototype = {
5557      /**
5558      * Create an empty record
5559      * @param {Object} data (optional) - overlay some values
5560      * @return {Roo.data.Record} record created.
5561      */
5562     newRow :  function(d) {
5563         var da =  {};
5564         this.recordType.prototype.fields.each(function(c) {
5565             switch( c.type) {
5566                 case 'int' : da[c.name] = 0; break;
5567                 case 'date' : da[c.name] = new Date(); break;
5568                 case 'float' : da[c.name] = 0.0; break;
5569                 case 'boolean' : da[c.name] = false; break;
5570                 default : da[c.name] = ""; break;
5571             }
5572             
5573         });
5574         return new this.recordType(Roo.apply(da, d));
5575     }
5576     
5577 };/*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587
5588 /**
5589  * @class Roo.data.DataProxy
5590  * @extends Roo.data.Observable
5591  * This class is an abstract base class for implementations which provide retrieval of
5592  * unformatted data objects.<br>
5593  * <p>
5594  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5595  * (of the appropriate type which knows how to parse the data object) to provide a block of
5596  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5597  * <p>
5598  * Custom implementations must implement the load method as described in
5599  * {@link Roo.data.HttpProxy#load}.
5600  */
5601 Roo.data.DataProxy = function(){
5602     this.addEvents({
5603         /**
5604          * @event beforeload
5605          * Fires before a network request is made to retrieve a data object.
5606          * @param {Object} This DataProxy object.
5607          * @param {Object} params The params parameter to the load function.
5608          */
5609         beforeload : true,
5610         /**
5611          * @event load
5612          * Fires before the load method's callback is called.
5613          * @param {Object} This DataProxy object.
5614          * @param {Object} o The data object.
5615          * @param {Object} arg The callback argument object passed to the load function.
5616          */
5617         load : true,
5618         /**
5619          * @event loadexception
5620          * Fires if an Exception occurs during data retrieval.
5621          * @param {Object} This DataProxy object.
5622          * @param {Object} o The data object.
5623          * @param {Object} arg The callback argument object passed to the load function.
5624          * @param {Object} e The Exception.
5625          */
5626         loadexception : true
5627     });
5628     Roo.data.DataProxy.superclass.constructor.call(this);
5629 };
5630
5631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5632
5633     /**
5634      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5635      */
5636 /*
5637  * Based on:
5638  * Ext JS Library 1.1.1
5639  * Copyright(c) 2006-2007, Ext JS, LLC.
5640  *
5641  * Originally Released Under LGPL - original licence link has changed is not relivant.
5642  *
5643  * Fork - LGPL
5644  * <script type="text/javascript">
5645  */
5646 /**
5647  * @class Roo.data.MemoryProxy
5648  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5649  * to the Reader when its load method is called.
5650  * @constructor
5651  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5652  */
5653 Roo.data.MemoryProxy = function(data){
5654     if (data.data) {
5655         data = data.data;
5656     }
5657     Roo.data.MemoryProxy.superclass.constructor.call(this);
5658     this.data = data;
5659 };
5660
5661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5662     /**
5663      * Load data from the requested source (in this case an in-memory
5664      * data object passed to the constructor), read the data object into
5665      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5666      * process that block using the passed callback.
5667      * @param {Object} params This parameter is not used by the MemoryProxy class.
5668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5669      * object into a block of Roo.data.Records.
5670      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5671      * The function must be passed <ul>
5672      * <li>The Record block object</li>
5673      * <li>The "arg" argument from the load function</li>
5674      * <li>A boolean success indicator</li>
5675      * </ul>
5676      * @param {Object} scope The scope in which to call the callback
5677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5678      */
5679     load : function(params, reader, callback, scope, arg){
5680         params = params || {};
5681         var result;
5682         try {
5683             result = reader.readRecords(this.data);
5684         }catch(e){
5685             this.fireEvent("loadexception", this, arg, null, e);
5686             callback.call(scope, null, arg, false);
5687             return;
5688         }
5689         callback.call(scope, result, arg, true);
5690     },
5691     
5692     // private
5693     update : function(params, records){
5694         
5695     }
5696 });/*
5697  * Based on:
5698  * Ext JS Library 1.1.1
5699  * Copyright(c) 2006-2007, Ext JS, LLC.
5700  *
5701  * Originally Released Under LGPL - original licence link has changed is not relivant.
5702  *
5703  * Fork - LGPL
5704  * <script type="text/javascript">
5705  */
5706 /**
5707  * @class Roo.data.HttpProxy
5708  * @extends Roo.data.DataProxy
5709  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5710  * configured to reference a certain URL.<br><br>
5711  * <p>
5712  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5713  * from which the running page was served.<br><br>
5714  * <p>
5715  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5716  * <p>
5717  * Be aware that to enable the browser to parse an XML document, the server must set
5718  * the Content-Type header in the HTTP response to "text/xml".
5719  * @constructor
5720  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5721  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5722  * will be used to make the request.
5723  */
5724 Roo.data.HttpProxy = function(conn){
5725     Roo.data.HttpProxy.superclass.constructor.call(this);
5726     // is conn a conn config or a real conn?
5727     this.conn = conn;
5728     this.useAjax = !conn || !conn.events;
5729   
5730 };
5731
5732 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5733     // thse are take from connection...
5734     
5735     /**
5736      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5737      */
5738     /**
5739      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5740      * extra parameters to each request made by this object. (defaults to undefined)
5741      */
5742     /**
5743      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5744      *  to each request made by this object. (defaults to undefined)
5745      */
5746     /**
5747      * @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)
5748      */
5749     /**
5750      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5751      */
5752      /**
5753      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5754      * @type Boolean
5755      */
5756   
5757
5758     /**
5759      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5760      * @type Boolean
5761      */
5762     /**
5763      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5764      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5765      * a finer-grained basis than the DataProxy events.
5766      */
5767     getConnection : function(){
5768         return this.useAjax ? Roo.Ajax : this.conn;
5769     },
5770
5771     /**
5772      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5773      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5774      * process that block using the passed callback.
5775      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5776      * for the request to the remote server.
5777      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5778      * object into a block of Roo.data.Records.
5779      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5780      * The function must be passed <ul>
5781      * <li>The Record block object</li>
5782      * <li>The "arg" argument from the load function</li>
5783      * <li>A boolean success indicator</li>
5784      * </ul>
5785      * @param {Object} scope The scope in which to call the callback
5786      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5787      */
5788     load : function(params, reader, callback, scope, arg){
5789         if(this.fireEvent("beforeload", this, params) !== false){
5790             var  o = {
5791                 params : params || {},
5792                 request: {
5793                     callback : callback,
5794                     scope : scope,
5795                     arg : arg
5796                 },
5797                 reader: reader,
5798                 callback : this.loadResponse,
5799                 scope: this
5800             };
5801             if(this.useAjax){
5802                 Roo.applyIf(o, this.conn);
5803                 if(this.activeRequest){
5804                     Roo.Ajax.abort(this.activeRequest);
5805                 }
5806                 this.activeRequest = Roo.Ajax.request(o);
5807             }else{
5808                 this.conn.request(o);
5809             }
5810         }else{
5811             callback.call(scope||this, null, arg, false);
5812         }
5813     },
5814
5815     // private
5816     loadResponse : function(o, success, response){
5817         delete this.activeRequest;
5818         if(!success){
5819             this.fireEvent("loadexception", this, o, response);
5820             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5821             return;
5822         }
5823         var result;
5824         try {
5825             result = o.reader.read(response);
5826         }catch(e){
5827             this.fireEvent("loadexception", this, o, response, e);
5828             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5829             return;
5830         }
5831         
5832         this.fireEvent("load", this, o, o.request.arg);
5833         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5834     },
5835
5836     // private
5837     update : function(dataSet){
5838
5839     },
5840
5841     // private
5842     updateResponse : function(dataSet){
5843
5844     }
5845 });/*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855
5856 /**
5857  * @class Roo.data.ScriptTagProxy
5858  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5859  * other than the originating domain of the running page.<br><br>
5860  * <p>
5861  * <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
5862  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5863  * <p>
5864  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5865  * source code that is used as the source inside a &lt;script> tag.<br><br>
5866  * <p>
5867  * In order for the browser to process the returned data, the server must wrap the data object
5868  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5869  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5870  * depending on whether the callback name was passed:
5871  * <p>
5872  * <pre><code>
5873 boolean scriptTag = false;
5874 String cb = request.getParameter("callback");
5875 if (cb != null) {
5876     scriptTag = true;
5877     response.setContentType("text/javascript");
5878 } else {
5879     response.setContentType("application/x-json");
5880 }
5881 Writer out = response.getWriter();
5882 if (scriptTag) {
5883     out.write(cb + "(");
5884 }
5885 out.print(dataBlock.toJsonString());
5886 if (scriptTag) {
5887     out.write(");");
5888 }
5889 </pre></code>
5890  *
5891  * @constructor
5892  * @param {Object} config A configuration object.
5893  */
5894 Roo.data.ScriptTagProxy = function(config){
5895     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5896     Roo.apply(this, config);
5897     this.head = document.getElementsByTagName("head")[0];
5898 };
5899
5900 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5901
5902 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5903     /**
5904      * @cfg {String} url The URL from which to request the data object.
5905      */
5906     /**
5907      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5908      */
5909     timeout : 30000,
5910     /**
5911      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5912      * the server the name of the callback function set up by the load call to process the returned data object.
5913      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5914      * javascript output which calls this named function passing the data object as its only parameter.
5915      */
5916     callbackParam : "callback",
5917     /**
5918      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5919      * name to the request.
5920      */
5921     nocache : true,
5922
5923     /**
5924      * Load data from the configured URL, read the data object into
5925      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5926      * process that block using the passed callback.
5927      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5928      * for the request to the remote server.
5929      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5930      * object into a block of Roo.data.Records.
5931      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5932      * The function must be passed <ul>
5933      * <li>The Record block object</li>
5934      * <li>The "arg" argument from the load function</li>
5935      * <li>A boolean success indicator</li>
5936      * </ul>
5937      * @param {Object} scope The scope in which to call the callback
5938      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5939      */
5940     load : function(params, reader, callback, scope, arg){
5941         if(this.fireEvent("beforeload", this, params) !== false){
5942
5943             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5944
5945             var url = this.url;
5946             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5947             if(this.nocache){
5948                 url += "&_dc=" + (new Date().getTime());
5949             }
5950             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5951             var trans = {
5952                 id : transId,
5953                 cb : "stcCallback"+transId,
5954                 scriptId : "stcScript"+transId,
5955                 params : params,
5956                 arg : arg,
5957                 url : url,
5958                 callback : callback,
5959                 scope : scope,
5960                 reader : reader
5961             };
5962             var conn = this;
5963
5964             window[trans.cb] = function(o){
5965                 conn.handleResponse(o, trans);
5966             };
5967
5968             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5969
5970             if(this.autoAbort !== false){
5971                 this.abort();
5972             }
5973
5974             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5975
5976             var script = document.createElement("script");
5977             script.setAttribute("src", url);
5978             script.setAttribute("type", "text/javascript");
5979             script.setAttribute("id", trans.scriptId);
5980             this.head.appendChild(script);
5981
5982             this.trans = trans;
5983         }else{
5984             callback.call(scope||this, null, arg, false);
5985         }
5986     },
5987
5988     // private
5989     isLoading : function(){
5990         return this.trans ? true : false;
5991     },
5992
5993     /**
5994      * Abort the current server request.
5995      */
5996     abort : function(){
5997         if(this.isLoading()){
5998             this.destroyTrans(this.trans);
5999         }
6000     },
6001
6002     // private
6003     destroyTrans : function(trans, isLoaded){
6004         this.head.removeChild(document.getElementById(trans.scriptId));
6005         clearTimeout(trans.timeoutId);
6006         if(isLoaded){
6007             window[trans.cb] = undefined;
6008             try{
6009                 delete window[trans.cb];
6010             }catch(e){}
6011         }else{
6012             // if hasn't been loaded, wait for load to remove it to prevent script error
6013             window[trans.cb] = function(){
6014                 window[trans.cb] = undefined;
6015                 try{
6016                     delete window[trans.cb];
6017                 }catch(e){}
6018             };
6019         }
6020     },
6021
6022     // private
6023     handleResponse : function(o, trans){
6024         this.trans = false;
6025         this.destroyTrans(trans, true);
6026         var result;
6027         try {
6028             result = trans.reader.readRecords(o);
6029         }catch(e){
6030             this.fireEvent("loadexception", this, o, trans.arg, e);
6031             trans.callback.call(trans.scope||window, null, trans.arg, false);
6032             return;
6033         }
6034         this.fireEvent("load", this, o, trans.arg);
6035         trans.callback.call(trans.scope||window, result, trans.arg, true);
6036     },
6037
6038     // private
6039     handleFailure : function(trans){
6040         this.trans = false;
6041         this.destroyTrans(trans, false);
6042         this.fireEvent("loadexception", this, null, trans.arg);
6043         trans.callback.call(trans.scope||window, null, trans.arg, false);
6044     }
6045 });/*
6046  * Based on:
6047  * Ext JS Library 1.1.1
6048  * Copyright(c) 2006-2007, Ext JS, LLC.
6049  *
6050  * Originally Released Under LGPL - original licence link has changed is not relivant.
6051  *
6052  * Fork - LGPL
6053  * <script type="text/javascript">
6054  */
6055
6056 /**
6057  * @class Roo.data.JsonReader
6058  * @extends Roo.data.DataReader
6059  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6060  * based on mappings in a provided Roo.data.Record constructor.
6061  * 
6062  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6063  * in the reply previously. 
6064  * 
6065  * <p>
6066  * Example code:
6067  * <pre><code>
6068 var RecordDef = Roo.data.Record.create([
6069     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6070     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6071 ]);
6072 var myReader = new Roo.data.JsonReader({
6073     totalProperty: "results",    // The property which contains the total dataset size (optional)
6074     root: "rows",                // The property which contains an Array of row objects
6075     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6076 }, RecordDef);
6077 </code></pre>
6078  * <p>
6079  * This would consume a JSON file like this:
6080  * <pre><code>
6081 { 'results': 2, 'rows': [
6082     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6083     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6084 }
6085 </code></pre>
6086  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6087  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6088  * paged from the remote server.
6089  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6090  * @cfg {String} root name of the property which contains the Array of row objects.
6091  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6092  * @constructor
6093  * Create a new JsonReader
6094  * @param {Object} meta Metadata configuration options
6095  * @param {Object} recordType Either an Array of field definition objects,
6096  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6097  */
6098 Roo.data.JsonReader = function(meta, recordType){
6099     
6100     meta = meta || {};
6101     // set some defaults:
6102     Roo.applyIf(meta, {
6103         totalProperty: 'total',
6104         successProperty : 'success',
6105         root : 'data',
6106         id : 'id'
6107     });
6108     
6109     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6110 };
6111 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6112     
6113     /**
6114      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6115      * Used by Store query builder to append _requestMeta to params.
6116      * 
6117      */
6118     metaFromRemote : false,
6119     /**
6120      * This method is only used by a DataProxy which has retrieved data from a remote server.
6121      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6122      * @return {Object} data A data block which is used by an Roo.data.Store object as
6123      * a cache of Roo.data.Records.
6124      */
6125     read : function(response){
6126         var json = response.responseText;
6127        
6128         var o = /* eval:var:o */ eval("("+json+")");
6129         if(!o) {
6130             throw {message: "JsonReader.read: Json object not found"};
6131         }
6132         
6133         if(o.metaData){
6134             
6135             delete this.ef;
6136             this.metaFromRemote = true;
6137             this.meta = o.metaData;
6138             this.recordType = Roo.data.Record.create(o.metaData.fields);
6139             this.onMetaChange(this.meta, this.recordType, o);
6140         }
6141         return this.readRecords(o);
6142     },
6143
6144     // private function a store will implement
6145     onMetaChange : function(meta, recordType, o){
6146
6147     },
6148
6149     /**
6150          * @ignore
6151          */
6152     simpleAccess: function(obj, subsc) {
6153         return obj[subsc];
6154     },
6155
6156         /**
6157          * @ignore
6158          */
6159     getJsonAccessor: function(){
6160         var re = /[\[\.]/;
6161         return function(expr) {
6162             try {
6163                 return(re.test(expr))
6164                     ? new Function("obj", "return obj." + expr)
6165                     : function(obj){
6166                         return obj[expr];
6167                     };
6168             } catch(e){}
6169             return Roo.emptyFn;
6170         };
6171     }(),
6172
6173     /**
6174      * Create a data block containing Roo.data.Records from an XML document.
6175      * @param {Object} o An object which contains an Array of row objects in the property specified
6176      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6177      * which contains the total size of the dataset.
6178      * @return {Object} data A data block which is used by an Roo.data.Store object as
6179      * a cache of Roo.data.Records.
6180      */
6181     readRecords : function(o){
6182         /**
6183          * After any data loads, the raw JSON data is available for further custom processing.
6184          * @type Object
6185          */
6186         this.jsonData = o;
6187         var s = this.meta, Record = this.recordType,
6188             f = Record.prototype.fields, fi = f.items, fl = f.length;
6189
6190 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6191         if (!this.ef) {
6192             if(s.totalProperty) {
6193                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6194                 }
6195                 if(s.successProperty) {
6196                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6197                 }
6198                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6199                 if (s.id) {
6200                         var g = this.getJsonAccessor(s.id);
6201                         this.getId = function(rec) {
6202                                 var r = g(rec);
6203                                 return (r === undefined || r === "") ? null : r;
6204                         };
6205                 } else {
6206                         this.getId = function(){return null;};
6207                 }
6208             this.ef = [];
6209             for(var jj = 0; jj < fl; jj++){
6210                 f = fi[jj];
6211                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6212                 this.ef[jj] = this.getJsonAccessor(map);
6213             }
6214         }
6215
6216         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6217         if(s.totalProperty){
6218             var vt = parseInt(this.getTotal(o), 10);
6219             if(!isNaN(vt)){
6220                 totalRecords = vt;
6221             }
6222         }
6223         if(s.successProperty){
6224             var vs = this.getSuccess(o);
6225             if(vs === false || vs === 'false'){
6226                 success = false;
6227             }
6228         }
6229         var records = [];
6230             for(var i = 0; i < c; i++){
6231                     var n = root[i];
6232                 var values = {};
6233                 var id = this.getId(n);
6234                 for(var j = 0; j < fl; j++){
6235                     f = fi[j];
6236                 var v = this.ef[j](n);
6237                 if (!f.convert) {
6238                     Roo.log('missing convert for ' + f.name);
6239                     Roo.log(f);
6240                     continue;
6241                 }
6242                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6243                 }
6244                 var record = new Record(values, id);
6245                 record.json = n;
6246                 records[i] = record;
6247             }
6248             return {
6249                 success : success,
6250                 records : records,
6251                 totalRecords : totalRecords
6252             };
6253     }
6254 });/*
6255  * Based on:
6256  * Ext JS Library 1.1.1
6257  * Copyright(c) 2006-2007, Ext JS, LLC.
6258  *
6259  * Originally Released Under LGPL - original licence link has changed is not relivant.
6260  *
6261  * Fork - LGPL
6262  * <script type="text/javascript">
6263  */
6264
6265 /**
6266  * @class Roo.data.XmlReader
6267  * @extends Roo.data.DataReader
6268  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6269  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6270  * <p>
6271  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6272  * header in the HTTP response must be set to "text/xml".</em>
6273  * <p>
6274  * Example code:
6275  * <pre><code>
6276 var RecordDef = Roo.data.Record.create([
6277    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6278    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6279 ]);
6280 var myReader = new Roo.data.XmlReader({
6281    totalRecords: "results", // The element which contains the total dataset size (optional)
6282    record: "row",           // The repeated element which contains row information
6283    id: "id"                 // The element within the row that provides an ID for the record (optional)
6284 }, RecordDef);
6285 </code></pre>
6286  * <p>
6287  * This would consume an XML file like this:
6288  * <pre><code>
6289 &lt;?xml?>
6290 &lt;dataset>
6291  &lt;results>2&lt;/results>
6292  &lt;row>
6293    &lt;id>1&lt;/id>
6294    &lt;name>Bill&lt;/name>
6295    &lt;occupation>Gardener&lt;/occupation>
6296  &lt;/row>
6297  &lt;row>
6298    &lt;id>2&lt;/id>
6299    &lt;name>Ben&lt;/name>
6300    &lt;occupation>Horticulturalist&lt;/occupation>
6301  &lt;/row>
6302 &lt;/dataset>
6303 </code></pre>
6304  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6305  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6306  * paged from the remote server.
6307  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6308  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6309  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6310  * a record identifier value.
6311  * @constructor
6312  * Create a new XmlReader
6313  * @param {Object} meta Metadata configuration options
6314  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6315  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6316  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6317  */
6318 Roo.data.XmlReader = function(meta, recordType){
6319     meta = meta || {};
6320     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6321 };
6322 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6323     /**
6324      * This method is only used by a DataProxy which has retrieved data from a remote server.
6325          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6326          * to contain a method called 'responseXML' that returns an XML document object.
6327      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6328      * a cache of Roo.data.Records.
6329      */
6330     read : function(response){
6331         var doc = response.responseXML;
6332         if(!doc) {
6333             throw {message: "XmlReader.read: XML Document not available"};
6334         }
6335         return this.readRecords(doc);
6336     },
6337
6338     /**
6339      * Create a data block containing Roo.data.Records from an XML document.
6340          * @param {Object} doc A parsed XML document.
6341      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6342      * a cache of Roo.data.Records.
6343      */
6344     readRecords : function(doc){
6345         /**
6346          * After any data loads/reads, the raw XML Document is available for further custom processing.
6347          * @type XMLDocument
6348          */
6349         this.xmlData = doc;
6350         var root = doc.documentElement || doc;
6351         var q = Roo.DomQuery;
6352         var recordType = this.recordType, fields = recordType.prototype.fields;
6353         var sid = this.meta.id;
6354         var totalRecords = 0, success = true;
6355         if(this.meta.totalRecords){
6356             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6357         }
6358         
6359         if(this.meta.success){
6360             var sv = q.selectValue(this.meta.success, root, true);
6361             success = sv !== false && sv !== 'false';
6362         }
6363         var records = [];
6364         var ns = q.select(this.meta.record, root);
6365         for(var i = 0, len = ns.length; i < len; i++) {
6366                 var n = ns[i];
6367                 var values = {};
6368                 var id = sid ? q.selectValue(sid, n) : undefined;
6369                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6370                     var f = fields.items[j];
6371                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6372                     v = f.convert(v);
6373                     values[f.name] = v;
6374                 }
6375                 var record = new recordType(values, id);
6376                 record.node = n;
6377                 records[records.length] = record;
6378             }
6379
6380             return {
6381                 success : success,
6382                 records : records,
6383                 totalRecords : totalRecords || records.length
6384             };
6385     }
6386 });/*
6387  * Based on:
6388  * Ext JS Library 1.1.1
6389  * Copyright(c) 2006-2007, Ext JS, LLC.
6390  *
6391  * Originally Released Under LGPL - original licence link has changed is not relivant.
6392  *
6393  * Fork - LGPL
6394  * <script type="text/javascript">
6395  */
6396
6397 /**
6398  * @class Roo.data.ArrayReader
6399  * @extends Roo.data.DataReader
6400  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6401  * Each element of that Array represents a row of data fields. The
6402  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6403  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6404  * <p>
6405  * Example code:.
6406  * <pre><code>
6407 var RecordDef = Roo.data.Record.create([
6408     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6409     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6410 ]);
6411 var myReader = new Roo.data.ArrayReader({
6412     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6413 }, RecordDef);
6414 </code></pre>
6415  * <p>
6416  * This would consume an Array like this:
6417  * <pre><code>
6418 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6419   </code></pre>
6420  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6421  * @constructor
6422  * Create a new JsonReader
6423  * @param {Object} meta Metadata configuration options.
6424  * @param {Object} recordType Either an Array of field definition objects
6425  * as specified to {@link Roo.data.Record#create},
6426  * or an {@link Roo.data.Record} object
6427  * created using {@link Roo.data.Record#create}.
6428  */
6429 Roo.data.ArrayReader = function(meta, recordType){
6430     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6431 };
6432
6433 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6434     /**
6435      * Create a data block containing Roo.data.Records from an XML document.
6436      * @param {Object} o An Array of row objects which represents the dataset.
6437      * @return {Object} data A data block which is used by an Roo.data.Store object as
6438      * a cache of Roo.data.Records.
6439      */
6440     readRecords : function(o){
6441         var sid = this.meta ? this.meta.id : null;
6442         var recordType = this.recordType, fields = recordType.prototype.fields;
6443         var records = [];
6444         var root = o;
6445             for(var i = 0; i < root.length; i++){
6446                     var n = root[i];
6447                 var values = {};
6448                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                 var f = fields.items[j];
6451                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6452                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6453                 v = f.convert(v);
6454                 values[f.name] = v;
6455             }
6456                 var record = new recordType(values, id);
6457                 record.json = n;
6458                 records[records.length] = record;
6459             }
6460             return {
6461                 records : records,
6462                 totalRecords : records.length
6463             };
6464     }
6465 });/*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476
6477 /**
6478  * @class Roo.data.Tree
6479  * @extends Roo.util.Observable
6480  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6481  * in the tree have most standard DOM functionality.
6482  * @constructor
6483  * @param {Node} root (optional) The root node
6484  */
6485 Roo.data.Tree = function(root){
6486    this.nodeHash = {};
6487    /**
6488     * The root node for this tree
6489     * @type Node
6490     */
6491    this.root = null;
6492    if(root){
6493        this.setRootNode(root);
6494    }
6495    this.addEvents({
6496        /**
6497         * @event append
6498         * Fires when a new child node is appended to a node in this tree.
6499         * @param {Tree} tree The owner tree
6500         * @param {Node} parent The parent node
6501         * @param {Node} node The newly appended node
6502         * @param {Number} index The index of the newly appended node
6503         */
6504        "append" : true,
6505        /**
6506         * @event remove
6507         * Fires when a child node is removed from a node in this tree.
6508         * @param {Tree} tree The owner tree
6509         * @param {Node} parent The parent node
6510         * @param {Node} node The child node removed
6511         */
6512        "remove" : true,
6513        /**
6514         * @event move
6515         * Fires when a node is moved to a new location in the tree
6516         * @param {Tree} tree The owner tree
6517         * @param {Node} node The node moved
6518         * @param {Node} oldParent The old parent of this node
6519         * @param {Node} newParent The new parent of this node
6520         * @param {Number} index The index it was moved to
6521         */
6522        "move" : true,
6523        /**
6524         * @event insert
6525         * Fires when a new child node is inserted in a node in this tree.
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} parent The parent node
6528         * @param {Node} node The child node inserted
6529         * @param {Node} refNode The child node the node was inserted before
6530         */
6531        "insert" : true,
6532        /**
6533         * @event beforeappend
6534         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6535         * @param {Tree} tree The owner tree
6536         * @param {Node} parent The parent node
6537         * @param {Node} node The child node to be appended
6538         */
6539        "beforeappend" : true,
6540        /**
6541         * @event beforeremove
6542         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node to be removed
6546         */
6547        "beforeremove" : true,
6548        /**
6549         * @event beforemove
6550         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node being moved
6553         * @param {Node} oldParent The parent of the node
6554         * @param {Node} newParent The new parent the node is moving to
6555         * @param {Number} index The index it is being moved to
6556         */
6557        "beforemove" : true,
6558        /**
6559         * @event beforeinsert
6560         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node to be inserted
6564         * @param {Node} refNode The child node the node is being inserted before
6565         */
6566        "beforeinsert" : true
6567    });
6568
6569     Roo.data.Tree.superclass.constructor.call(this);
6570 };
6571
6572 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6573     pathSeparator: "/",
6574
6575     proxyNodeEvent : function(){
6576         return this.fireEvent.apply(this, arguments);
6577     },
6578
6579     /**
6580      * Returns the root node for this tree.
6581      * @return {Node}
6582      */
6583     getRootNode : function(){
6584         return this.root;
6585     },
6586
6587     /**
6588      * Sets the root node for this tree.
6589      * @param {Node} node
6590      * @return {Node}
6591      */
6592     setRootNode : function(node){
6593         this.root = node;
6594         node.ownerTree = this;
6595         node.isRoot = true;
6596         this.registerNode(node);
6597         return node;
6598     },
6599
6600     /**
6601      * Gets a node in this tree by its id.
6602      * @param {String} id
6603      * @return {Node}
6604      */
6605     getNodeById : function(id){
6606         return this.nodeHash[id];
6607     },
6608
6609     registerNode : function(node){
6610         this.nodeHash[node.id] = node;
6611     },
6612
6613     unregisterNode : function(node){
6614         delete this.nodeHash[node.id];
6615     },
6616
6617     toString : function(){
6618         return "[Tree"+(this.id?" "+this.id:"")+"]";
6619     }
6620 });
6621
6622 /**
6623  * @class Roo.data.Node
6624  * @extends Roo.util.Observable
6625  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6626  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6627  * @constructor
6628  * @param {Object} attributes The attributes/config for the node
6629  */
6630 Roo.data.Node = function(attributes){
6631     /**
6632      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6633      * @type {Object}
6634      */
6635     this.attributes = attributes || {};
6636     this.leaf = this.attributes.leaf;
6637     /**
6638      * The node id. @type String
6639      */
6640     this.id = this.attributes.id;
6641     if(!this.id){
6642         this.id = Roo.id(null, "ynode-");
6643         this.attributes.id = this.id;
6644     }
6645     /**
6646      * All child nodes of this node. @type Array
6647      */
6648     this.childNodes = [];
6649     if(!this.childNodes.indexOf){ // indexOf is a must
6650         this.childNodes.indexOf = function(o){
6651             for(var i = 0, len = this.length; i < len; i++){
6652                 if(this[i] == o) {
6653                     return i;
6654                 }
6655             }
6656             return -1;
6657         };
6658     }
6659     /**
6660      * The parent node for this node. @type Node
6661      */
6662     this.parentNode = null;
6663     /**
6664      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6665      */
6666     this.firstChild = null;
6667     /**
6668      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6669      */
6670     this.lastChild = null;
6671     /**
6672      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6673      */
6674     this.previousSibling = null;
6675     /**
6676      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6677      */
6678     this.nextSibling = null;
6679
6680     this.addEvents({
6681        /**
6682         * @event append
6683         * Fires when a new child node is appended
6684         * @param {Tree} tree The owner tree
6685         * @param {Node} this This node
6686         * @param {Node} node The newly appended node
6687         * @param {Number} index The index of the newly appended node
6688         */
6689        "append" : true,
6690        /**
6691         * @event remove
6692         * Fires when a child node is removed
6693         * @param {Tree} tree The owner tree
6694         * @param {Node} this This node
6695         * @param {Node} node The removed node
6696         */
6697        "remove" : true,
6698        /**
6699         * @event move
6700         * Fires when this node is moved to a new location in the tree
6701         * @param {Tree} tree The owner tree
6702         * @param {Node} this This node
6703         * @param {Node} oldParent The old parent of this node
6704         * @param {Node} newParent The new parent of this node
6705         * @param {Number} index The index it was moved to
6706         */
6707        "move" : true,
6708        /**
6709         * @event insert
6710         * Fires when a new child node is inserted.
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} node The child node inserted
6714         * @param {Node} refNode The child node the node was inserted before
6715         */
6716        "insert" : true,
6717        /**
6718         * @event beforeappend
6719         * Fires before a new child is appended, return false to cancel the append.
6720         * @param {Tree} tree The owner tree
6721         * @param {Node} this This node
6722         * @param {Node} node The child node to be appended
6723         */
6724        "beforeappend" : true,
6725        /**
6726         * @event beforeremove
6727         * Fires before a child is removed, return false to cancel the remove.
6728         * @param {Tree} tree The owner tree
6729         * @param {Node} this This node
6730         * @param {Node} node The child node to be removed
6731         */
6732        "beforeremove" : true,
6733        /**
6734         * @event beforemove
6735         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6736         * @param {Tree} tree The owner tree
6737         * @param {Node} this This node
6738         * @param {Node} oldParent The parent of this node
6739         * @param {Node} newParent The new parent this node is moving to
6740         * @param {Number} index The index it is being moved to
6741         */
6742        "beforemove" : true,
6743        /**
6744         * @event beforeinsert
6745         * Fires before a new child is inserted, return false to cancel the insert.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} node The child node to be inserted
6749         * @param {Node} refNode The child node the node is being inserted before
6750         */
6751        "beforeinsert" : true
6752    });
6753     this.listeners = this.attributes.listeners;
6754     Roo.data.Node.superclass.constructor.call(this);
6755 };
6756
6757 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6758     fireEvent : function(evtName){
6759         // first do standard event for this node
6760         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6761             return false;
6762         }
6763         // then bubble it up to the tree if the event wasn't cancelled
6764         var ot = this.getOwnerTree();
6765         if(ot){
6766             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6767                 return false;
6768             }
6769         }
6770         return true;
6771     },
6772
6773     /**
6774      * Returns true if this node is a leaf
6775      * @return {Boolean}
6776      */
6777     isLeaf : function(){
6778         return this.leaf === true;
6779     },
6780
6781     // private
6782     setFirstChild : function(node){
6783         this.firstChild = node;
6784     },
6785
6786     //private
6787     setLastChild : function(node){
6788         this.lastChild = node;
6789     },
6790
6791
6792     /**
6793      * Returns true if this node is the last child of its parent
6794      * @return {Boolean}
6795      */
6796     isLast : function(){
6797        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6798     },
6799
6800     /**
6801      * Returns true if this node is the first child of its parent
6802      * @return {Boolean}
6803      */
6804     isFirst : function(){
6805        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6806     },
6807
6808     hasChildNodes : function(){
6809         return !this.isLeaf() && this.childNodes.length > 0;
6810     },
6811
6812     /**
6813      * Insert node(s) as the last child node of this node.
6814      * @param {Node/Array} node The node or Array of nodes to append
6815      * @return {Node} The appended node if single append, or null if an array was passed
6816      */
6817     appendChild : function(node){
6818         var multi = false;
6819         if(node instanceof Array){
6820             multi = node;
6821         }else if(arguments.length > 1){
6822             multi = arguments;
6823         }
6824         // if passed an array or multiple args do them one by one
6825         if(multi){
6826             for(var i = 0, len = multi.length; i < len; i++) {
6827                 this.appendChild(multi[i]);
6828             }
6829         }else{
6830             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6831                 return false;
6832             }
6833             var index = this.childNodes.length;
6834             var oldParent = node.parentNode;
6835             // it's a move, make sure we move it cleanly
6836             if(oldParent){
6837                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6838                     return false;
6839                 }
6840                 oldParent.removeChild(node);
6841             }
6842             index = this.childNodes.length;
6843             if(index == 0){
6844                 this.setFirstChild(node);
6845             }
6846             this.childNodes.push(node);
6847             node.parentNode = this;
6848             var ps = this.childNodes[index-1];
6849             if(ps){
6850                 node.previousSibling = ps;
6851                 ps.nextSibling = node;
6852             }else{
6853                 node.previousSibling = null;
6854             }
6855             node.nextSibling = null;
6856             this.setLastChild(node);
6857             node.setOwnerTree(this.getOwnerTree());
6858             this.fireEvent("append", this.ownerTree, this, node, index);
6859             if(oldParent){
6860                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6861             }
6862             return node;
6863         }
6864     },
6865
6866     /**
6867      * Removes a child node from this node.
6868      * @param {Node} node The node to remove
6869      * @return {Node} The removed node
6870      */
6871     removeChild : function(node){
6872         var index = this.childNodes.indexOf(node);
6873         if(index == -1){
6874             return false;
6875         }
6876         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6877             return false;
6878         }
6879
6880         // remove it from childNodes collection
6881         this.childNodes.splice(index, 1);
6882
6883         // update siblings
6884         if(node.previousSibling){
6885             node.previousSibling.nextSibling = node.nextSibling;
6886         }
6887         if(node.nextSibling){
6888             node.nextSibling.previousSibling = node.previousSibling;
6889         }
6890
6891         // update child refs
6892         if(this.firstChild == node){
6893             this.setFirstChild(node.nextSibling);
6894         }
6895         if(this.lastChild == node){
6896             this.setLastChild(node.previousSibling);
6897         }
6898
6899         node.setOwnerTree(null);
6900         // clear any references from the node
6901         node.parentNode = null;
6902         node.previousSibling = null;
6903         node.nextSibling = null;
6904         this.fireEvent("remove", this.ownerTree, this, node);
6905         return node;
6906     },
6907
6908     /**
6909      * Inserts the first node before the second node in this nodes childNodes collection.
6910      * @param {Node} node The node to insert
6911      * @param {Node} refNode The node to insert before (if null the node is appended)
6912      * @return {Node} The inserted node
6913      */
6914     insertBefore : function(node, refNode){
6915         if(!refNode){ // like standard Dom, refNode can be null for append
6916             return this.appendChild(node);
6917         }
6918         // nothing to do
6919         if(node == refNode){
6920             return false;
6921         }
6922
6923         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6924             return false;
6925         }
6926         var index = this.childNodes.indexOf(refNode);
6927         var oldParent = node.parentNode;
6928         var refIndex = index;
6929
6930         // when moving internally, indexes will change after remove
6931         if(oldParent == this && this.childNodes.indexOf(node) < index){
6932             refIndex--;
6933         }
6934
6935         // it's a move, make sure we move it cleanly
6936         if(oldParent){
6937             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6938                 return false;
6939             }
6940             oldParent.removeChild(node);
6941         }
6942         if(refIndex == 0){
6943             this.setFirstChild(node);
6944         }
6945         this.childNodes.splice(refIndex, 0, node);
6946         node.parentNode = this;
6947         var ps = this.childNodes[refIndex-1];
6948         if(ps){
6949             node.previousSibling = ps;
6950             ps.nextSibling = node;
6951         }else{
6952             node.previousSibling = null;
6953         }
6954         node.nextSibling = refNode;
6955         refNode.previousSibling = node;
6956         node.setOwnerTree(this.getOwnerTree());
6957         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6958         if(oldParent){
6959             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6960         }
6961         return node;
6962     },
6963
6964     /**
6965      * Returns the child node at the specified index.
6966      * @param {Number} index
6967      * @return {Node}
6968      */
6969     item : function(index){
6970         return this.childNodes[index];
6971     },
6972
6973     /**
6974      * Replaces one child node in this node with another.
6975      * @param {Node} newChild The replacement node
6976      * @param {Node} oldChild The node to replace
6977      * @return {Node} The replaced node
6978      */
6979     replaceChild : function(newChild, oldChild){
6980         this.insertBefore(newChild, oldChild);
6981         this.removeChild(oldChild);
6982         return oldChild;
6983     },
6984
6985     /**
6986      * Returns the index of a child node
6987      * @param {Node} node
6988      * @return {Number} The index of the node or -1 if it was not found
6989      */
6990     indexOf : function(child){
6991         return this.childNodes.indexOf(child);
6992     },
6993
6994     /**
6995      * Returns the tree this node is in.
6996      * @return {Tree}
6997      */
6998     getOwnerTree : function(){
6999         // if it doesn't have one, look for one
7000         if(!this.ownerTree){
7001             var p = this;
7002             while(p){
7003                 if(p.ownerTree){
7004                     this.ownerTree = p.ownerTree;
7005                     break;
7006                 }
7007                 p = p.parentNode;
7008             }
7009         }
7010         return this.ownerTree;
7011     },
7012
7013     /**
7014      * Returns depth of this node (the root node has a depth of 0)
7015      * @return {Number}
7016      */
7017     getDepth : function(){
7018         var depth = 0;
7019         var p = this;
7020         while(p.parentNode){
7021             ++depth;
7022             p = p.parentNode;
7023         }
7024         return depth;
7025     },
7026
7027     // private
7028     setOwnerTree : function(tree){
7029         // if it's move, we need to update everyone
7030         if(tree != this.ownerTree){
7031             if(this.ownerTree){
7032                 this.ownerTree.unregisterNode(this);
7033             }
7034             this.ownerTree = tree;
7035             var cs = this.childNodes;
7036             for(var i = 0, len = cs.length; i < len; i++) {
7037                 cs[i].setOwnerTree(tree);
7038             }
7039             if(tree){
7040                 tree.registerNode(this);
7041             }
7042         }
7043     },
7044
7045     /**
7046      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7047      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7048      * @return {String} The path
7049      */
7050     getPath : function(attr){
7051         attr = attr || "id";
7052         var p = this.parentNode;
7053         var b = [this.attributes[attr]];
7054         while(p){
7055             b.unshift(p.attributes[attr]);
7056             p = p.parentNode;
7057         }
7058         var sep = this.getOwnerTree().pathSeparator;
7059         return sep + b.join(sep);
7060     },
7061
7062     /**
7063      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7064      * function call will be the scope provided or the current node. The arguments to the function
7065      * will be the args provided or the current node. If the function returns false at any point,
7066      * the bubble is stopped.
7067      * @param {Function} fn The function to call
7068      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7069      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7070      */
7071     bubble : function(fn, scope, args){
7072         var p = this;
7073         while(p){
7074             if(fn.call(scope || p, args || p) === false){
7075                 break;
7076             }
7077             p = p.parentNode;
7078         }
7079     },
7080
7081     /**
7082      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7083      * function call will be the scope provided or the current node. The arguments to the function
7084      * will be the args provided or the current node. If the function returns false at any point,
7085      * the cascade is stopped on that branch.
7086      * @param {Function} fn The function to call
7087      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7088      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7089      */
7090     cascade : function(fn, scope, args){
7091         if(fn.call(scope || this, args || this) !== false){
7092             var cs = this.childNodes;
7093             for(var i = 0, len = cs.length; i < len; i++) {
7094                 cs[i].cascade(fn, scope, args);
7095             }
7096         }
7097     },
7098
7099     /**
7100      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the iteration stops.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     eachChild : function(fn, scope, args){
7109         var cs = this.childNodes;
7110         for(var i = 0, len = cs.length; i < len; i++) {
7111                 if(fn.call(scope || this, args || cs[i]) === false){
7112                     break;
7113                 }
7114         }
7115     },
7116
7117     /**
7118      * Finds the first child that has the attribute with the specified value.
7119      * @param {String} attribute The attribute name
7120      * @param {Mixed} value The value to search for
7121      * @return {Node} The found child or null if none was found
7122      */
7123     findChild : function(attribute, value){
7124         var cs = this.childNodes;
7125         for(var i = 0, len = cs.length; i < len; i++) {
7126                 if(cs[i].attributes[attribute] == value){
7127                     return cs[i];
7128                 }
7129         }
7130         return null;
7131     },
7132
7133     /**
7134      * Finds the first child by a custom function. The child matches if the function passed
7135      * returns true.
7136      * @param {Function} fn
7137      * @param {Object} scope (optional)
7138      * @return {Node} The found child or null if none was found
7139      */
7140     findChildBy : function(fn, scope){
7141         var cs = this.childNodes;
7142         for(var i = 0, len = cs.length; i < len; i++) {
7143                 if(fn.call(scope||cs[i], cs[i]) === true){
7144                     return cs[i];
7145                 }
7146         }
7147         return null;
7148     },
7149
7150     /**
7151      * Sorts this nodes children using the supplied sort function
7152      * @param {Function} fn
7153      * @param {Object} scope (optional)
7154      */
7155     sort : function(fn, scope){
7156         var cs = this.childNodes;
7157         var len = cs.length;
7158         if(len > 0){
7159             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7160             cs.sort(sortFn);
7161             for(var i = 0; i < len; i++){
7162                 var n = cs[i];
7163                 n.previousSibling = cs[i-1];
7164                 n.nextSibling = cs[i+1];
7165                 if(i == 0){
7166                     this.setFirstChild(n);
7167                 }
7168                 if(i == len-1){
7169                     this.setLastChild(n);
7170                 }
7171             }
7172         }
7173     },
7174
7175     /**
7176      * Returns true if this node is an ancestor (at any point) of the passed node.
7177      * @param {Node} node
7178      * @return {Boolean}
7179      */
7180     contains : function(node){
7181         return node.isAncestor(this);
7182     },
7183
7184     /**
7185      * Returns true if the passed node is an ancestor (at any point) of this node.
7186      * @param {Node} node
7187      * @return {Boolean}
7188      */
7189     isAncestor : function(node){
7190         var p = this.parentNode;
7191         while(p){
7192             if(p == node){
7193                 return true;
7194             }
7195             p = p.parentNode;
7196         }
7197         return false;
7198     },
7199
7200     toString : function(){
7201         return "[Node"+(this.id?" "+this.id:"")+"]";
7202     }
7203 });/*
7204  * Based on:
7205  * Ext JS Library 1.1.1
7206  * Copyright(c) 2006-2007, Ext JS, LLC.
7207  *
7208  * Originally Released Under LGPL - original licence link has changed is not relivant.
7209  *
7210  * Fork - LGPL
7211  * <script type="text/javascript">
7212  */
7213  
7214
7215 /**
7216  * @class Roo.ComponentMgr
7217  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7218  * @singleton
7219  */
7220 Roo.ComponentMgr = function(){
7221     var all = new Roo.util.MixedCollection();
7222
7223     return {
7224         /**
7225          * Registers a component.
7226          * @param {Roo.Component} c The component
7227          */
7228         register : function(c){
7229             all.add(c);
7230         },
7231
7232         /**
7233          * Unregisters a component.
7234          * @param {Roo.Component} c The component
7235          */
7236         unregister : function(c){
7237             all.remove(c);
7238         },
7239
7240         /**
7241          * Returns a component by id
7242          * @param {String} id The component id
7243          */
7244         get : function(id){
7245             return all.get(id);
7246         },
7247
7248         /**
7249          * Registers a function that will be called when a specified component is added to ComponentMgr
7250          * @param {String} id The component id
7251          * @param {Funtction} fn The callback function
7252          * @param {Object} scope The scope of the callback
7253          */
7254         onAvailable : function(id, fn, scope){
7255             all.on("add", function(index, o){
7256                 if(o.id == id){
7257                     fn.call(scope || o, o);
7258                     all.un("add", fn, scope);
7259                 }
7260             });
7261         }
7262     };
7263 }();/*
7264  * Based on:
7265  * Ext JS Library 1.1.1
7266  * Copyright(c) 2006-2007, Ext JS, LLC.
7267  *
7268  * Originally Released Under LGPL - original licence link has changed is not relivant.
7269  *
7270  * Fork - LGPL
7271  * <script type="text/javascript">
7272  */
7273  
7274 /**
7275  * @class Roo.Component
7276  * @extends Roo.util.Observable
7277  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7278  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7279  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7280  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7281  * All visual components (widgets) that require rendering into a layout should subclass Component.
7282  * @constructor
7283  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7284  * 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
7285  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7286  */
7287 Roo.Component = function(config){
7288     config = config || {};
7289     if(config.tagName || config.dom || typeof config == "string"){ // element object
7290         config = {el: config, id: config.id || config};
7291     }
7292     this.initialConfig = config;
7293
7294     Roo.apply(this, config);
7295     this.addEvents({
7296         /**
7297          * @event disable
7298          * Fires after the component is disabled.
7299              * @param {Roo.Component} this
7300              */
7301         disable : true,
7302         /**
7303          * @event enable
7304          * Fires after the component is enabled.
7305              * @param {Roo.Component} this
7306              */
7307         enable : true,
7308         /**
7309          * @event beforeshow
7310          * Fires before the component is shown.  Return false to stop the show.
7311              * @param {Roo.Component} this
7312              */
7313         beforeshow : true,
7314         /**
7315          * @event show
7316          * Fires after the component is shown.
7317              * @param {Roo.Component} this
7318              */
7319         show : true,
7320         /**
7321          * @event beforehide
7322          * Fires before the component is hidden. Return false to stop the hide.
7323              * @param {Roo.Component} this
7324              */
7325         beforehide : true,
7326         /**
7327          * @event hide
7328          * Fires after the component is hidden.
7329              * @param {Roo.Component} this
7330              */
7331         hide : true,
7332         /**
7333          * @event beforerender
7334          * Fires before the component is rendered. Return false to stop the render.
7335              * @param {Roo.Component} this
7336              */
7337         beforerender : true,
7338         /**
7339          * @event render
7340          * Fires after the component is rendered.
7341              * @param {Roo.Component} this
7342              */
7343         render : true,
7344         /**
7345          * @event beforedestroy
7346          * Fires before the component is destroyed. Return false to stop the destroy.
7347              * @param {Roo.Component} this
7348              */
7349         beforedestroy : true,
7350         /**
7351          * @event destroy
7352          * Fires after the component is destroyed.
7353              * @param {Roo.Component} this
7354              */
7355         destroy : true
7356     });
7357     if(!this.id){
7358         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7359     }
7360     Roo.ComponentMgr.register(this);
7361     Roo.Component.superclass.constructor.call(this);
7362     this.initComponent();
7363     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7364         this.render(this.renderTo);
7365         delete this.renderTo;
7366     }
7367 };
7368
7369 // private
7370 Roo.Component.AUTO_ID = 1000;
7371
7372 Roo.extend(Roo.Component, Roo.util.Observable, {
7373     /**
7374      * @property {Boolean} hidden
7375      * true if this component is hidden. Read-only.
7376      */
7377     hidden : false,
7378     /**
7379      * true if this component is disabled. Read-only.
7380      */
7381     disabled : false,
7382     /**
7383      * true if this component has been rendered. Read-only.
7384      */
7385     rendered : false,
7386     
7387     /** @cfg {String} disableClass
7388      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7389      */
7390     disabledClass : "x-item-disabled",
7391         /** @cfg {Boolean} allowDomMove
7392          * Whether the component can move the Dom node when rendering (defaults to true).
7393          */
7394     allowDomMove : true,
7395     /** @cfg {String} hideMode
7396      * How this component should hidden. Supported values are
7397      * "visibility" (css visibility), "offsets" (negative offset position) and
7398      * "display" (css display) - defaults to "display".
7399      */
7400     hideMode: 'display',
7401
7402     // private
7403     ctype : "Roo.Component",
7404
7405     /** @cfg {String} actionMode 
7406      * which property holds the element that used for  hide() / show() / disable() / enable()
7407      * default is 'el' 
7408      */
7409     actionMode : "el",
7410
7411     // private
7412     getActionEl : function(){
7413         return this[this.actionMode];
7414     },
7415
7416     initComponent : Roo.emptyFn,
7417     /**
7418      * If this is a lazy rendering component, render it to its container element.
7419      * @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.
7420      */
7421     render : function(container, position){
7422         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7423             if(!container && this.el){
7424                 this.el = Roo.get(this.el);
7425                 container = this.el.dom.parentNode;
7426                 this.allowDomMove = false;
7427             }
7428             this.container = Roo.get(container);
7429             this.rendered = true;
7430             if(position !== undefined){
7431                 if(typeof position == 'number'){
7432                     position = this.container.dom.childNodes[position];
7433                 }else{
7434                     position = Roo.getDom(position);
7435                 }
7436             }
7437             this.onRender(this.container, position || null);
7438             if(this.cls){
7439                 this.el.addClass(this.cls);
7440                 delete this.cls;
7441             }
7442             if(this.style){
7443                 this.el.applyStyles(this.style);
7444                 delete this.style;
7445             }
7446             this.fireEvent("render", this);
7447             this.afterRender(this.container);
7448             if(this.hidden){
7449                 this.hide();
7450             }
7451             if(this.disabled){
7452                 this.disable();
7453             }
7454         }
7455         return this;
7456     },
7457
7458     // private
7459     // default function is not really useful
7460     onRender : function(ct, position){
7461         if(this.el){
7462             this.el = Roo.get(this.el);
7463             if(this.allowDomMove !== false){
7464                 ct.dom.insertBefore(this.el.dom, position);
7465             }
7466         }
7467     },
7468
7469     // private
7470     getAutoCreate : function(){
7471         var cfg = typeof this.autoCreate == "object" ?
7472                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7473         if(this.id && !cfg.id){
7474             cfg.id = this.id;
7475         }
7476         return cfg;
7477     },
7478
7479     // private
7480     afterRender : Roo.emptyFn,
7481
7482     /**
7483      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7484      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7485      */
7486     destroy : function(){
7487         if(this.fireEvent("beforedestroy", this) !== false){
7488             this.purgeListeners();
7489             this.beforeDestroy();
7490             if(this.rendered){
7491                 this.el.removeAllListeners();
7492                 this.el.remove();
7493                 if(this.actionMode == "container"){
7494                     this.container.remove();
7495                 }
7496             }
7497             this.onDestroy();
7498             Roo.ComponentMgr.unregister(this);
7499             this.fireEvent("destroy", this);
7500         }
7501     },
7502
7503         // private
7504     beforeDestroy : function(){
7505
7506     },
7507
7508         // private
7509         onDestroy : function(){
7510
7511     },
7512
7513     /**
7514      * Returns the underlying {@link Roo.Element}.
7515      * @return {Roo.Element} The element
7516      */
7517     getEl : function(){
7518         return this.el;
7519     },
7520
7521     /**
7522      * Returns the id of this component.
7523      * @return {String}
7524      */
7525     getId : function(){
7526         return this.id;
7527     },
7528
7529     /**
7530      * Try to focus this component.
7531      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7532      * @return {Roo.Component} this
7533      */
7534     focus : function(selectText){
7535         if(this.rendered){
7536             this.el.focus();
7537             if(selectText === true){
7538                 this.el.dom.select();
7539             }
7540         }
7541         return this;
7542     },
7543
7544     // private
7545     blur : function(){
7546         if(this.rendered){
7547             this.el.blur();
7548         }
7549         return this;
7550     },
7551
7552     /**
7553      * Disable this component.
7554      * @return {Roo.Component} this
7555      */
7556     disable : function(){
7557         if(this.rendered){
7558             this.onDisable();
7559         }
7560         this.disabled = true;
7561         this.fireEvent("disable", this);
7562         return this;
7563     },
7564
7565         // private
7566     onDisable : function(){
7567         this.getActionEl().addClass(this.disabledClass);
7568         this.el.dom.disabled = true;
7569     },
7570
7571     /**
7572      * Enable this component.
7573      * @return {Roo.Component} this
7574      */
7575     enable : function(){
7576         if(this.rendered){
7577             this.onEnable();
7578         }
7579         this.disabled = false;
7580         this.fireEvent("enable", this);
7581         return this;
7582     },
7583
7584         // private
7585     onEnable : function(){
7586         this.getActionEl().removeClass(this.disabledClass);
7587         this.el.dom.disabled = false;
7588     },
7589
7590     /**
7591      * Convenience function for setting disabled/enabled by boolean.
7592      * @param {Boolean} disabled
7593      */
7594     setDisabled : function(disabled){
7595         this[disabled ? "disable" : "enable"]();
7596     },
7597
7598     /**
7599      * Show this component.
7600      * @return {Roo.Component} this
7601      */
7602     show: function(){
7603         if(this.fireEvent("beforeshow", this) !== false){
7604             this.hidden = false;
7605             if(this.rendered){
7606                 this.onShow();
7607             }
7608             this.fireEvent("show", this);
7609         }
7610         return this;
7611     },
7612
7613     // private
7614     onShow : function(){
7615         var ae = this.getActionEl();
7616         if(this.hideMode == 'visibility'){
7617             ae.dom.style.visibility = "visible";
7618         }else if(this.hideMode == 'offsets'){
7619             ae.removeClass('x-hidden');
7620         }else{
7621             ae.dom.style.display = "";
7622         }
7623     },
7624
7625     /**
7626      * Hide this component.
7627      * @return {Roo.Component} this
7628      */
7629     hide: function(){
7630         if(this.fireEvent("beforehide", this) !== false){
7631             this.hidden = true;
7632             if(this.rendered){
7633                 this.onHide();
7634             }
7635             this.fireEvent("hide", this);
7636         }
7637         return this;
7638     },
7639
7640     // private
7641     onHide : function(){
7642         var ae = this.getActionEl();
7643         if(this.hideMode == 'visibility'){
7644             ae.dom.style.visibility = "hidden";
7645         }else if(this.hideMode == 'offsets'){
7646             ae.addClass('x-hidden');
7647         }else{
7648             ae.dom.style.display = "none";
7649         }
7650     },
7651
7652     /**
7653      * Convenience function to hide or show this component by boolean.
7654      * @param {Boolean} visible True to show, false to hide
7655      * @return {Roo.Component} this
7656      */
7657     setVisible: function(visible){
7658         if(visible) {
7659             this.show();
7660         }else{
7661             this.hide();
7662         }
7663         return this;
7664     },
7665
7666     /**
7667      * Returns true if this component is visible.
7668      */
7669     isVisible : function(){
7670         return this.getActionEl().isVisible();
7671     },
7672
7673     cloneConfig : function(overrides){
7674         overrides = overrides || {};
7675         var id = overrides.id || Roo.id();
7676         var cfg = Roo.applyIf(overrides, this.initialConfig);
7677         cfg.id = id; // prevent dup id
7678         return new this.constructor(cfg);
7679     }
7680 });/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  (function(){ 
7691 /**
7692  * @class Roo.Layer
7693  * @extends Roo.Element
7694  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7695  * automatic maintaining of shadow/shim positions.
7696  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7697  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7698  * you can pass a string with a CSS class name. False turns off the shadow.
7699  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7700  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7701  * @cfg {String} cls CSS class to add to the element
7702  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7703  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7704  * @constructor
7705  * @param {Object} config An object with config options.
7706  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7707  */
7708
7709 Roo.Layer = function(config, existingEl){
7710     config = config || {};
7711     var dh = Roo.DomHelper;
7712     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7713     if(existingEl){
7714         this.dom = Roo.getDom(existingEl);
7715     }
7716     if(!this.dom){
7717         var o = config.dh || {tag: "div", cls: "x-layer"};
7718         this.dom = dh.append(pel, o);
7719     }
7720     if(config.cls){
7721         this.addClass(config.cls);
7722     }
7723     this.constrain = config.constrain !== false;
7724     this.visibilityMode = Roo.Element.VISIBILITY;
7725     if(config.id){
7726         this.id = this.dom.id = config.id;
7727     }else{
7728         this.id = Roo.id(this.dom);
7729     }
7730     this.zindex = config.zindex || this.getZIndex();
7731     this.position("absolute", this.zindex);
7732     if(config.shadow){
7733         this.shadowOffset = config.shadowOffset || 4;
7734         this.shadow = new Roo.Shadow({
7735             offset : this.shadowOffset,
7736             mode : config.shadow
7737         });
7738     }else{
7739         this.shadowOffset = 0;
7740     }
7741     this.useShim = config.shim !== false && Roo.useShims;
7742     this.useDisplay = config.useDisplay;
7743     this.hide();
7744 };
7745
7746 var supr = Roo.Element.prototype;
7747
7748 // shims are shared among layer to keep from having 100 iframes
7749 var shims = [];
7750
7751 Roo.extend(Roo.Layer, Roo.Element, {
7752
7753     getZIndex : function(){
7754         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7755     },
7756
7757     getShim : function(){
7758         if(!this.useShim){
7759             return null;
7760         }
7761         if(this.shim){
7762             return this.shim;
7763         }
7764         var shim = shims.shift();
7765         if(!shim){
7766             shim = this.createShim();
7767             shim.enableDisplayMode('block');
7768             shim.dom.style.display = 'none';
7769             shim.dom.style.visibility = 'visible';
7770         }
7771         var pn = this.dom.parentNode;
7772         if(shim.dom.parentNode != pn){
7773             pn.insertBefore(shim.dom, this.dom);
7774         }
7775         shim.setStyle('z-index', this.getZIndex()-2);
7776         this.shim = shim;
7777         return shim;
7778     },
7779
7780     hideShim : function(){
7781         if(this.shim){
7782             this.shim.setDisplayed(false);
7783             shims.push(this.shim);
7784             delete this.shim;
7785         }
7786     },
7787
7788     disableShadow : function(){
7789         if(this.shadow){
7790             this.shadowDisabled = true;
7791             this.shadow.hide();
7792             this.lastShadowOffset = this.shadowOffset;
7793             this.shadowOffset = 0;
7794         }
7795     },
7796
7797     enableShadow : function(show){
7798         if(this.shadow){
7799             this.shadowDisabled = false;
7800             this.shadowOffset = this.lastShadowOffset;
7801             delete this.lastShadowOffset;
7802             if(show){
7803                 this.sync(true);
7804             }
7805         }
7806     },
7807
7808     // private
7809     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7810     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7811     sync : function(doShow){
7812         var sw = this.shadow;
7813         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7814             var sh = this.getShim();
7815
7816             var w = this.getWidth(),
7817                 h = this.getHeight();
7818
7819             var l = this.getLeft(true),
7820                 t = this.getTop(true);
7821
7822             if(sw && !this.shadowDisabled){
7823                 if(doShow && !sw.isVisible()){
7824                     sw.show(this);
7825                 }else{
7826                     sw.realign(l, t, w, h);
7827                 }
7828                 if(sh){
7829                     if(doShow){
7830                        sh.show();
7831                     }
7832                     // fit the shim behind the shadow, so it is shimmed too
7833                     var a = sw.adjusts, s = sh.dom.style;
7834                     s.left = (Math.min(l, l+a.l))+"px";
7835                     s.top = (Math.min(t, t+a.t))+"px";
7836                     s.width = (w+a.w)+"px";
7837                     s.height = (h+a.h)+"px";
7838                 }
7839             }else if(sh){
7840                 if(doShow){
7841                    sh.show();
7842                 }
7843                 sh.setSize(w, h);
7844                 sh.setLeftTop(l, t);
7845             }
7846             
7847         }
7848     },
7849
7850     // private
7851     destroy : function(){
7852         this.hideShim();
7853         if(this.shadow){
7854             this.shadow.hide();
7855         }
7856         this.removeAllListeners();
7857         var pn = this.dom.parentNode;
7858         if(pn){
7859             pn.removeChild(this.dom);
7860         }
7861         Roo.Element.uncache(this.id);
7862     },
7863
7864     remove : function(){
7865         this.destroy();
7866     },
7867
7868     // private
7869     beginUpdate : function(){
7870         this.updating = true;
7871     },
7872
7873     // private
7874     endUpdate : function(){
7875         this.updating = false;
7876         this.sync(true);
7877     },
7878
7879     // private
7880     hideUnders : function(negOffset){
7881         if(this.shadow){
7882             this.shadow.hide();
7883         }
7884         this.hideShim();
7885     },
7886
7887     // private
7888     constrainXY : function(){
7889         if(this.constrain){
7890             var vw = Roo.lib.Dom.getViewWidth(),
7891                 vh = Roo.lib.Dom.getViewHeight();
7892             var s = Roo.get(document).getScroll();
7893
7894             var xy = this.getXY();
7895             var x = xy[0], y = xy[1];   
7896             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7897             // only move it if it needs it
7898             var moved = false;
7899             // first validate right/bottom
7900             if((x + w) > vw+s.left){
7901                 x = vw - w - this.shadowOffset;
7902                 moved = true;
7903             }
7904             if((y + h) > vh+s.top){
7905                 y = vh - h - this.shadowOffset;
7906                 moved = true;
7907             }
7908             // then make sure top/left isn't negative
7909             if(x < s.left){
7910                 x = s.left;
7911                 moved = true;
7912             }
7913             if(y < s.top){
7914                 y = s.top;
7915                 moved = true;
7916             }
7917             if(moved){
7918                 if(this.avoidY){
7919                     var ay = this.avoidY;
7920                     if(y <= ay && (y+h) >= ay){
7921                         y = ay-h-5;   
7922                     }
7923                 }
7924                 xy = [x, y];
7925                 this.storeXY(xy);
7926                 supr.setXY.call(this, xy);
7927                 this.sync();
7928             }
7929         }
7930     },
7931
7932     isVisible : function(){
7933         return this.visible;    
7934     },
7935
7936     // private
7937     showAction : function(){
7938         this.visible = true; // track visibility to prevent getStyle calls
7939         if(this.useDisplay === true){
7940             this.setDisplayed("");
7941         }else if(this.lastXY){
7942             supr.setXY.call(this, this.lastXY);
7943         }else if(this.lastLT){
7944             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7945         }
7946     },
7947
7948     // private
7949     hideAction : function(){
7950         this.visible = false;
7951         if(this.useDisplay === true){
7952             this.setDisplayed(false);
7953         }else{
7954             this.setLeftTop(-10000,-10000);
7955         }
7956     },
7957
7958     // overridden Element method
7959     setVisible : function(v, a, d, c, e){
7960         if(v){
7961             this.showAction();
7962         }
7963         if(a && v){
7964             var cb = function(){
7965                 this.sync(true);
7966                 if(c){
7967                     c();
7968                 }
7969             }.createDelegate(this);
7970             supr.setVisible.call(this, true, true, d, cb, e);
7971         }else{
7972             if(!v){
7973                 this.hideUnders(true);
7974             }
7975             var cb = c;
7976             if(a){
7977                 cb = function(){
7978                     this.hideAction();
7979                     if(c){
7980                         c();
7981                     }
7982                 }.createDelegate(this);
7983             }
7984             supr.setVisible.call(this, v, a, d, cb, e);
7985             if(v){
7986                 this.sync(true);
7987             }else if(!a){
7988                 this.hideAction();
7989             }
7990         }
7991     },
7992
7993     storeXY : function(xy){
7994         delete this.lastLT;
7995         this.lastXY = xy;
7996     },
7997
7998     storeLeftTop : function(left, top){
7999         delete this.lastXY;
8000         this.lastLT = [left, top];
8001     },
8002
8003     // private
8004     beforeFx : function(){
8005         this.beforeAction();
8006         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8007     },
8008
8009     // private
8010     afterFx : function(){
8011         Roo.Layer.superclass.afterFx.apply(this, arguments);
8012         this.sync(this.isVisible());
8013     },
8014
8015     // private
8016     beforeAction : function(){
8017         if(!this.updating && this.shadow){
8018             this.shadow.hide();
8019         }
8020     },
8021
8022     // overridden Element method
8023     setLeft : function(left){
8024         this.storeLeftTop(left, this.getTop(true));
8025         supr.setLeft.apply(this, arguments);
8026         this.sync();
8027     },
8028
8029     setTop : function(top){
8030         this.storeLeftTop(this.getLeft(true), top);
8031         supr.setTop.apply(this, arguments);
8032         this.sync();
8033     },
8034
8035     setLeftTop : function(left, top){
8036         this.storeLeftTop(left, top);
8037         supr.setLeftTop.apply(this, arguments);
8038         this.sync();
8039     },
8040
8041     setXY : function(xy, a, d, c, e){
8042         this.fixDisplay();
8043         this.beforeAction();
8044         this.storeXY(xy);
8045         var cb = this.createCB(c);
8046         supr.setXY.call(this, xy, a, d, cb, e);
8047         if(!a){
8048             cb();
8049         }
8050     },
8051
8052     // private
8053     createCB : function(c){
8054         var el = this;
8055         return function(){
8056             el.constrainXY();
8057             el.sync(true);
8058             if(c){
8059                 c();
8060             }
8061         };
8062     },
8063
8064     // overridden Element method
8065     setX : function(x, a, d, c, e){
8066         this.setXY([x, this.getY()], a, d, c, e);
8067     },
8068
8069     // overridden Element method
8070     setY : function(y, a, d, c, e){
8071         this.setXY([this.getX(), y], a, d, c, e);
8072     },
8073
8074     // overridden Element method
8075     setSize : function(w, h, a, d, c, e){
8076         this.beforeAction();
8077         var cb = this.createCB(c);
8078         supr.setSize.call(this, w, h, a, d, cb, e);
8079         if(!a){
8080             cb();
8081         }
8082     },
8083
8084     // overridden Element method
8085     setWidth : function(w, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setWidth.call(this, w, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setHeight : function(h, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setHeight.call(this, h, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setBounds : function(x, y, w, h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         if(!a){
8109             this.storeXY([x, y]);
8110             supr.setXY.call(this, [x, y]);
8111             supr.setSize.call(this, w, h, a, d, cb, e);
8112             cb();
8113         }else{
8114             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8115         }
8116         return this;
8117     },
8118     
8119     /**
8120      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8121      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8122      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8123      * @param {Number} zindex The new z-index to set
8124      * @return {this} The Layer
8125      */
8126     setZIndex : function(zindex){
8127         this.zindex = zindex;
8128         this.setStyle("z-index", zindex + 2);
8129         if(this.shadow){
8130             this.shadow.setZIndex(zindex + 1);
8131         }
8132         if(this.shim){
8133             this.shim.setStyle("z-index", zindex);
8134         }
8135     }
8136 });
8137 })();/*
8138  * Based on:
8139  * Ext JS Library 1.1.1
8140  * Copyright(c) 2006-2007, Ext JS, LLC.
8141  *
8142  * Originally Released Under LGPL - original licence link has changed is not relivant.
8143  *
8144  * Fork - LGPL
8145  * <script type="text/javascript">
8146  */
8147
8148
8149 /**
8150  * @class Roo.Shadow
8151  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8152  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8153  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8154  * @constructor
8155  * Create a new Shadow
8156  * @param {Object} config The config object
8157  */
8158 Roo.Shadow = function(config){
8159     Roo.apply(this, config);
8160     if(typeof this.mode != "string"){
8161         this.mode = this.defaultMode;
8162     }
8163     var o = this.offset, a = {h: 0};
8164     var rad = Math.floor(this.offset/2);
8165     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8166         case "drop":
8167             a.w = 0;
8168             a.l = a.t = o;
8169             a.t -= 1;
8170             if(Roo.isIE){
8171                 a.l -= this.offset + rad;
8172                 a.t -= this.offset + rad;
8173                 a.w -= rad;
8174                 a.h -= rad;
8175                 a.t += 1;
8176             }
8177         break;
8178         case "sides":
8179             a.w = (o*2);
8180             a.l = -o;
8181             a.t = o-1;
8182             if(Roo.isIE){
8183                 a.l -= (this.offset - rad);
8184                 a.t -= this.offset + rad;
8185                 a.l += 1;
8186                 a.w -= (this.offset - rad)*2;
8187                 a.w -= rad + 1;
8188                 a.h -= 1;
8189             }
8190         break;
8191         case "frame":
8192             a.w = a.h = (o*2);
8193             a.l = a.t = -o;
8194             a.t += 1;
8195             a.h -= 2;
8196             if(Roo.isIE){
8197                 a.l -= (this.offset - rad);
8198                 a.t -= (this.offset - rad);
8199                 a.l += 1;
8200                 a.w -= (this.offset + rad + 1);
8201                 a.h -= (this.offset + rad);
8202                 a.h += 1;
8203             }
8204         break;
8205     };
8206
8207     this.adjusts = a;
8208 };
8209
8210 Roo.Shadow.prototype = {
8211     /**
8212      * @cfg {String} mode
8213      * The shadow display mode.  Supports the following options:<br />
8214      * sides: Shadow displays on both sides and bottom only<br />
8215      * frame: Shadow displays equally on all four sides<br />
8216      * drop: Traditional bottom-right drop shadow (default)
8217      */
8218     /**
8219      * @cfg {String} offset
8220      * The number of pixels to offset the shadow from the element (defaults to 4)
8221      */
8222     offset: 4,
8223
8224     // private
8225     defaultMode: "drop",
8226
8227     /**
8228      * Displays the shadow under the target element
8229      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8230      */
8231     show : function(target){
8232         target = Roo.get(target);
8233         if(!this.el){
8234             this.el = Roo.Shadow.Pool.pull();
8235             if(this.el.dom.nextSibling != target.dom){
8236                 this.el.insertBefore(target);
8237             }
8238         }
8239         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8240         if(Roo.isIE){
8241             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8242         }
8243         this.realign(
8244             target.getLeft(true),
8245             target.getTop(true),
8246             target.getWidth(),
8247             target.getHeight()
8248         );
8249         this.el.dom.style.display = "block";
8250     },
8251
8252     /**
8253      * Returns true if the shadow is visible, else false
8254      */
8255     isVisible : function(){
8256         return this.el ? true : false;  
8257     },
8258
8259     /**
8260      * Direct alignment when values are already available. Show must be called at least once before
8261      * calling this method to ensure it is initialized.
8262      * @param {Number} left The target element left position
8263      * @param {Number} top The target element top position
8264      * @param {Number} width The target element width
8265      * @param {Number} height The target element height
8266      */
8267     realign : function(l, t, w, h){
8268         if(!this.el){
8269             return;
8270         }
8271         var a = this.adjusts, d = this.el.dom, s = d.style;
8272         var iea = 0;
8273         s.left = (l+a.l)+"px";
8274         s.top = (t+a.t)+"px";
8275         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8276  
8277         if(s.width != sws || s.height != shs){
8278             s.width = sws;
8279             s.height = shs;
8280             if(!Roo.isIE){
8281                 var cn = d.childNodes;
8282                 var sww = Math.max(0, (sw-12))+"px";
8283                 cn[0].childNodes[1].style.width = sww;
8284                 cn[1].childNodes[1].style.width = sww;
8285                 cn[2].childNodes[1].style.width = sww;
8286                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8287             }
8288         }
8289     },
8290
8291     /**
8292      * Hides this shadow
8293      */
8294     hide : function(){
8295         if(this.el){
8296             this.el.dom.style.display = "none";
8297             Roo.Shadow.Pool.push(this.el);
8298             delete this.el;
8299         }
8300     },
8301
8302     /**
8303      * Adjust the z-index of this shadow
8304      * @param {Number} zindex The new z-index
8305      */
8306     setZIndex : function(z){
8307         this.zIndex = z;
8308         if(this.el){
8309             this.el.setStyle("z-index", z);
8310         }
8311     }
8312 };
8313
8314 // Private utility class that manages the internal Shadow cache
8315 Roo.Shadow.Pool = function(){
8316     var p = [];
8317     var markup = Roo.isIE ?
8318                  '<div class="x-ie-shadow"></div>' :
8319                  '<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>';
8320     return {
8321         pull : function(){
8322             var sh = p.shift();
8323             if(!sh){
8324                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8325                 sh.autoBoxAdjust = false;
8326             }
8327             return sh;
8328         },
8329
8330         push : function(sh){
8331             p.push(sh);
8332         }
8333     };
8334 }();/*
8335  * Based on:
8336  * Ext JS Library 1.1.1
8337  * Copyright(c) 2006-2007, Ext JS, LLC.
8338  *
8339  * Originally Released Under LGPL - original licence link has changed is not relivant.
8340  *
8341  * Fork - LGPL
8342  * <script type="text/javascript">
8343  */
8344
8345 /**
8346  * @class Roo.BoxComponent
8347  * @extends Roo.Component
8348  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8349  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8350  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8351  * layout containers.
8352  * @constructor
8353  * @param {Roo.Element/String/Object} config The configuration options.
8354  */
8355 Roo.BoxComponent = function(config){
8356     Roo.Component.call(this, config);
8357     this.addEvents({
8358         /**
8359          * @event resize
8360          * Fires after the component is resized.
8361              * @param {Roo.Component} this
8362              * @param {Number} adjWidth The box-adjusted width that was set
8363              * @param {Number} adjHeight The box-adjusted height that was set
8364              * @param {Number} rawWidth The width that was originally specified
8365              * @param {Number} rawHeight The height that was originally specified
8366              */
8367         resize : true,
8368         /**
8369          * @event move
8370          * Fires after the component is moved.
8371              * @param {Roo.Component} this
8372              * @param {Number} x The new x position
8373              * @param {Number} y The new y position
8374              */
8375         move : true
8376     });
8377 };
8378
8379 Roo.extend(Roo.BoxComponent, Roo.Component, {
8380     // private, set in afterRender to signify that the component has been rendered
8381     boxReady : false,
8382     // private, used to defer height settings to subclasses
8383     deferHeight: false,
8384     /** @cfg {Number} width
8385      * width (optional) size of component
8386      */
8387      /** @cfg {Number} height
8388      * height (optional) size of component
8389      */
8390      
8391     /**
8392      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8393      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8394      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8395      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8396      * @return {Roo.BoxComponent} this
8397      */
8398     setSize : function(w, h){
8399         // support for standard size objects
8400         if(typeof w == 'object'){
8401             h = w.height;
8402             w = w.width;
8403         }
8404         // not rendered
8405         if(!this.boxReady){
8406             this.width = w;
8407             this.height = h;
8408             return this;
8409         }
8410
8411         // prevent recalcs when not needed
8412         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8413             return this;
8414         }
8415         this.lastSize = {width: w, height: h};
8416
8417         var adj = this.adjustSize(w, h);
8418         var aw = adj.width, ah = adj.height;
8419         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8420             var rz = this.getResizeEl();
8421             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8422                 rz.setSize(aw, ah);
8423             }else if(!this.deferHeight && ah !== undefined){
8424                 rz.setHeight(ah);
8425             }else if(aw !== undefined){
8426                 rz.setWidth(aw);
8427             }
8428             this.onResize(aw, ah, w, h);
8429             this.fireEvent('resize', this, aw, ah, w, h);
8430         }
8431         return this;
8432     },
8433
8434     /**
8435      * Gets the current size of the component's underlying element.
8436      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8437      */
8438     getSize : function(){
8439         return this.el.getSize();
8440     },
8441
8442     /**
8443      * Gets the current XY position of the component's underlying element.
8444      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8445      * @return {Array} The XY position of the element (e.g., [100, 200])
8446      */
8447     getPosition : function(local){
8448         if(local === true){
8449             return [this.el.getLeft(true), this.el.getTop(true)];
8450         }
8451         return this.xy || this.el.getXY();
8452     },
8453
8454     /**
8455      * Gets the current box measurements of the component's underlying element.
8456      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8457      * @returns {Object} box An object in the format {x, y, width, height}
8458      */
8459     getBox : function(local){
8460         var s = this.el.getSize();
8461         if(local){
8462             s.x = this.el.getLeft(true);
8463             s.y = this.el.getTop(true);
8464         }else{
8465             var xy = this.xy || this.el.getXY();
8466             s.x = xy[0];
8467             s.y = xy[1];
8468         }
8469         return s;
8470     },
8471
8472     /**
8473      * Sets the current box measurements of the component's underlying element.
8474      * @param {Object} box An object in the format {x, y, width, height}
8475      * @returns {Roo.BoxComponent} this
8476      */
8477     updateBox : function(box){
8478         this.setSize(box.width, box.height);
8479         this.setPagePosition(box.x, box.y);
8480         return this;
8481     },
8482
8483     // protected
8484     getResizeEl : function(){
8485         return this.resizeEl || this.el;
8486     },
8487
8488     // protected
8489     getPositionEl : function(){
8490         return this.positionEl || this.el;
8491     },
8492
8493     /**
8494      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8495      * This method fires the move event.
8496      * @param {Number} left The new left
8497      * @param {Number} top The new top
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     setPosition : function(x, y){
8501         this.x = x;
8502         this.y = y;
8503         if(!this.boxReady){
8504             return this;
8505         }
8506         var adj = this.adjustPosition(x, y);
8507         var ax = adj.x, ay = adj.y;
8508
8509         var el = this.getPositionEl();
8510         if(ax !== undefined || ay !== undefined){
8511             if(ax !== undefined && ay !== undefined){
8512                 el.setLeftTop(ax, ay);
8513             }else if(ax !== undefined){
8514                 el.setLeft(ax);
8515             }else if(ay !== undefined){
8516                 el.setTop(ay);
8517             }
8518             this.onPosition(ax, ay);
8519             this.fireEvent('move', this, ax, ay);
8520         }
8521         return this;
8522     },
8523
8524     /**
8525      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8526      * This method fires the move event.
8527      * @param {Number} x The new x position
8528      * @param {Number} y The new y position
8529      * @returns {Roo.BoxComponent} this
8530      */
8531     setPagePosition : function(x, y){
8532         this.pageX = x;
8533         this.pageY = y;
8534         if(!this.boxReady){
8535             return;
8536         }
8537         if(x === undefined || y === undefined){ // cannot translate undefined points
8538             return;
8539         }
8540         var p = this.el.translatePoints(x, y);
8541         this.setPosition(p.left, p.top);
8542         return this;
8543     },
8544
8545     // private
8546     onRender : function(ct, position){
8547         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8548         if(this.resizeEl){
8549             this.resizeEl = Roo.get(this.resizeEl);
8550         }
8551         if(this.positionEl){
8552             this.positionEl = Roo.get(this.positionEl);
8553         }
8554     },
8555
8556     // private
8557     afterRender : function(){
8558         Roo.BoxComponent.superclass.afterRender.call(this);
8559         this.boxReady = true;
8560         this.setSize(this.width, this.height);
8561         if(this.x || this.y){
8562             this.setPosition(this.x, this.y);
8563         }
8564         if(this.pageX || this.pageY){
8565             this.setPagePosition(this.pageX, this.pageY);
8566         }
8567     },
8568
8569     /**
8570      * Force the component's size to recalculate based on the underlying element's current height and width.
8571      * @returns {Roo.BoxComponent} this
8572      */
8573     syncSize : function(){
8574         delete this.lastSize;
8575         this.setSize(this.el.getWidth(), this.el.getHeight());
8576         return this;
8577     },
8578
8579     /**
8580      * Called after the component is resized, this method is empty by default but can be implemented by any
8581      * subclass that needs to perform custom logic after a resize occurs.
8582      * @param {Number} adjWidth The box-adjusted width that was set
8583      * @param {Number} adjHeight The box-adjusted height that was set
8584      * @param {Number} rawWidth The width that was originally specified
8585      * @param {Number} rawHeight The height that was originally specified
8586      */
8587     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8588
8589     },
8590
8591     /**
8592      * Called after the component is moved, this method is empty by default but can be implemented by any
8593      * subclass that needs to perform custom logic after a move occurs.
8594      * @param {Number} x The new x position
8595      * @param {Number} y The new y position
8596      */
8597     onPosition : function(x, y){
8598
8599     },
8600
8601     // private
8602     adjustSize : function(w, h){
8603         if(this.autoWidth){
8604             w = 'auto';
8605         }
8606         if(this.autoHeight){
8607             h = 'auto';
8608         }
8609         return {width : w, height: h};
8610     },
8611
8612     // private
8613     adjustPosition : function(x, y){
8614         return {x : x, y: y};
8615     }
8616 });/*
8617  * Based on:
8618  * Ext JS Library 1.1.1
8619  * Copyright(c) 2006-2007, Ext JS, LLC.
8620  *
8621  * Originally Released Under LGPL - original licence link has changed is not relivant.
8622  *
8623  * Fork - LGPL
8624  * <script type="text/javascript">
8625  */
8626
8627
8628 /**
8629  * @class Roo.SplitBar
8630  * @extends Roo.util.Observable
8631  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8632  * <br><br>
8633  * Usage:
8634  * <pre><code>
8635 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8636                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8637 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8638 split.minSize = 100;
8639 split.maxSize = 600;
8640 split.animate = true;
8641 split.on('moved', splitterMoved);
8642 </code></pre>
8643  * @constructor
8644  * Create a new SplitBar
8645  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8646  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8647  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8648  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8649                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8650                         position of the SplitBar).
8651  */
8652 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8653     
8654     /** @private */
8655     this.el = Roo.get(dragElement, true);
8656     this.el.dom.unselectable = "on";
8657     /** @private */
8658     this.resizingEl = Roo.get(resizingElement, true);
8659
8660     /**
8661      * @private
8662      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8663      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8664      * @type Number
8665      */
8666     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8667     
8668     /**
8669      * The minimum size of the resizing element. (Defaults to 0)
8670      * @type Number
8671      */
8672     this.minSize = 0;
8673     
8674     /**
8675      * The maximum size of the resizing element. (Defaults to 2000)
8676      * @type Number
8677      */
8678     this.maxSize = 2000;
8679     
8680     /**
8681      * Whether to animate the transition to the new size
8682      * @type Boolean
8683      */
8684     this.animate = false;
8685     
8686     /**
8687      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8688      * @type Boolean
8689      */
8690     this.useShim = false;
8691     
8692     /** @private */
8693     this.shim = null;
8694     
8695     if(!existingProxy){
8696         /** @private */
8697         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8698     }else{
8699         this.proxy = Roo.get(existingProxy).dom;
8700     }
8701     /** @private */
8702     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8703     
8704     /** @private */
8705     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8706     
8707     /** @private */
8708     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8709     
8710     /** @private */
8711     this.dragSpecs = {};
8712     
8713     /**
8714      * @private The adapter to use to positon and resize elements
8715      */
8716     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8717     this.adapter.init(this);
8718     
8719     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8720         /** @private */
8721         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8722         this.el.addClass("x-splitbar-h");
8723     }else{
8724         /** @private */
8725         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8726         this.el.addClass("x-splitbar-v");
8727     }
8728     
8729     this.addEvents({
8730         /**
8731          * @event resize
8732          * Fires when the splitter is moved (alias for {@link #event-moved})
8733          * @param {Roo.SplitBar} this
8734          * @param {Number} newSize the new width or height
8735          */
8736         "resize" : true,
8737         /**
8738          * @event moved
8739          * Fires when the splitter is moved
8740          * @param {Roo.SplitBar} this
8741          * @param {Number} newSize the new width or height
8742          */
8743         "moved" : true,
8744         /**
8745          * @event beforeresize
8746          * Fires before the splitter is dragged
8747          * @param {Roo.SplitBar} this
8748          */
8749         "beforeresize" : true,
8750
8751         "beforeapply" : true
8752     });
8753
8754     Roo.util.Observable.call(this);
8755 };
8756
8757 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8758     onStartProxyDrag : function(x, y){
8759         this.fireEvent("beforeresize", this);
8760         if(!this.overlay){
8761             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8762             o.unselectable();
8763             o.enableDisplayMode("block");
8764             // all splitbars share the same overlay
8765             Roo.SplitBar.prototype.overlay = o;
8766         }
8767         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8768         this.overlay.show();
8769         Roo.get(this.proxy).setDisplayed("block");
8770         var size = this.adapter.getElementSize(this);
8771         this.activeMinSize = this.getMinimumSize();;
8772         this.activeMaxSize = this.getMaximumSize();;
8773         var c1 = size - this.activeMinSize;
8774         var c2 = Math.max(this.activeMaxSize - size, 0);
8775         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8776             this.dd.resetConstraints();
8777             this.dd.setXConstraint(
8778                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8779                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8780             );
8781             this.dd.setYConstraint(0, 0);
8782         }else{
8783             this.dd.resetConstraints();
8784             this.dd.setXConstraint(0, 0);
8785             this.dd.setYConstraint(
8786                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8787                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8788             );
8789          }
8790         this.dragSpecs.startSize = size;
8791         this.dragSpecs.startPoint = [x, y];
8792         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8793     },
8794     
8795     /** 
8796      * @private Called after the drag operation by the DDProxy
8797      */
8798     onEndProxyDrag : function(e){
8799         Roo.get(this.proxy).setDisplayed(false);
8800         var endPoint = Roo.lib.Event.getXY(e);
8801         if(this.overlay){
8802             this.overlay.hide();
8803         }
8804         var newSize;
8805         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8806             newSize = this.dragSpecs.startSize + 
8807                 (this.placement == Roo.SplitBar.LEFT ?
8808                     endPoint[0] - this.dragSpecs.startPoint[0] :
8809                     this.dragSpecs.startPoint[0] - endPoint[0]
8810                 );
8811         }else{
8812             newSize = this.dragSpecs.startSize + 
8813                 (this.placement == Roo.SplitBar.TOP ?
8814                     endPoint[1] - this.dragSpecs.startPoint[1] :
8815                     this.dragSpecs.startPoint[1] - endPoint[1]
8816                 );
8817         }
8818         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8819         if(newSize != this.dragSpecs.startSize){
8820             if(this.fireEvent('beforeapply', this, newSize) !== false){
8821                 this.adapter.setElementSize(this, newSize);
8822                 this.fireEvent("moved", this, newSize);
8823                 this.fireEvent("resize", this, newSize);
8824             }
8825         }
8826     },
8827     
8828     /**
8829      * Get the adapter this SplitBar uses
8830      * @return The adapter object
8831      */
8832     getAdapter : function(){
8833         return this.adapter;
8834     },
8835     
8836     /**
8837      * Set the adapter this SplitBar uses
8838      * @param {Object} adapter A SplitBar adapter object
8839      */
8840     setAdapter : function(adapter){
8841         this.adapter = adapter;
8842         this.adapter.init(this);
8843     },
8844     
8845     /**
8846      * Gets the minimum size for the resizing element
8847      * @return {Number} The minimum size
8848      */
8849     getMinimumSize : function(){
8850         return this.minSize;
8851     },
8852     
8853     /**
8854      * Sets the minimum size for the resizing element
8855      * @param {Number} minSize The minimum size
8856      */
8857     setMinimumSize : function(minSize){
8858         this.minSize = minSize;
8859     },
8860     
8861     /**
8862      * Gets the maximum size for the resizing element
8863      * @return {Number} The maximum size
8864      */
8865     getMaximumSize : function(){
8866         return this.maxSize;
8867     },
8868     
8869     /**
8870      * Sets the maximum size for the resizing element
8871      * @param {Number} maxSize The maximum size
8872      */
8873     setMaximumSize : function(maxSize){
8874         this.maxSize = maxSize;
8875     },
8876     
8877     /**
8878      * Sets the initialize size for the resizing element
8879      * @param {Number} size The initial size
8880      */
8881     setCurrentSize : function(size){
8882         var oldAnimate = this.animate;
8883         this.animate = false;
8884         this.adapter.setElementSize(this, size);
8885         this.animate = oldAnimate;
8886     },
8887     
8888     /**
8889      * Destroy this splitbar. 
8890      * @param {Boolean} removeEl True to remove the element
8891      */
8892     destroy : function(removeEl){
8893         if(this.shim){
8894             this.shim.remove();
8895         }
8896         this.dd.unreg();
8897         this.proxy.parentNode.removeChild(this.proxy);
8898         if(removeEl){
8899             this.el.remove();
8900         }
8901     }
8902 });
8903
8904 /**
8905  * @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.
8906  */
8907 Roo.SplitBar.createProxy = function(dir){
8908     var proxy = new Roo.Element(document.createElement("div"));
8909     proxy.unselectable();
8910     var cls = 'x-splitbar-proxy';
8911     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8912     document.body.appendChild(proxy.dom);
8913     return proxy.dom;
8914 };
8915
8916 /** 
8917  * @class Roo.SplitBar.BasicLayoutAdapter
8918  * Default Adapter. It assumes the splitter and resizing element are not positioned
8919  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8920  */
8921 Roo.SplitBar.BasicLayoutAdapter = function(){
8922 };
8923
8924 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8925     // do nothing for now
8926     init : function(s){
8927     
8928     },
8929     /**
8930      * Called before drag operations to get the current size of the resizing element. 
8931      * @param {Roo.SplitBar} s The SplitBar using this adapter
8932      */
8933      getElementSize : function(s){
8934         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8935             return s.resizingEl.getWidth();
8936         }else{
8937             return s.resizingEl.getHeight();
8938         }
8939     },
8940     
8941     /**
8942      * Called after drag operations to set the size of the resizing element.
8943      * @param {Roo.SplitBar} s The SplitBar using this adapter
8944      * @param {Number} newSize The new size to set
8945      * @param {Function} onComplete A function to be invoked when resizing is complete
8946      */
8947     setElementSize : function(s, newSize, onComplete){
8948         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8949             if(!s.animate){
8950                 s.resizingEl.setWidth(newSize);
8951                 if(onComplete){
8952                     onComplete(s, newSize);
8953                 }
8954             }else{
8955                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8956             }
8957         }else{
8958             
8959             if(!s.animate){
8960                 s.resizingEl.setHeight(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }
8968     }
8969 };
8970
8971 /** 
8972  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8973  * @extends Roo.SplitBar.BasicLayoutAdapter
8974  * Adapter that  moves the splitter element to align with the resized sizing element. 
8975  * Used with an absolute positioned SplitBar.
8976  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8977  * document.body, make sure you assign an id to the body element.
8978  */
8979 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8980     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8981     this.container = Roo.get(container);
8982 };
8983
8984 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8985     init : function(s){
8986         this.basic.init(s);
8987     },
8988     
8989     getElementSize : function(s){
8990         return this.basic.getElementSize(s);
8991     },
8992     
8993     setElementSize : function(s, newSize, onComplete){
8994         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8995     },
8996     
8997     moveSplitter : function(s){
8998         var yes = Roo.SplitBar;
8999         switch(s.placement){
9000             case yes.LEFT:
9001                 s.el.setX(s.resizingEl.getRight());
9002                 break;
9003             case yes.RIGHT:
9004                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9005                 break;
9006             case yes.TOP:
9007                 s.el.setY(s.resizingEl.getBottom());
9008                 break;
9009             case yes.BOTTOM:
9010                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9011                 break;
9012         }
9013     }
9014 };
9015
9016 /**
9017  * Orientation constant - Create a vertical SplitBar
9018  * @static
9019  * @type Number
9020  */
9021 Roo.SplitBar.VERTICAL = 1;
9022
9023 /**
9024  * Orientation constant - Create a horizontal SplitBar
9025  * @static
9026  * @type Number
9027  */
9028 Roo.SplitBar.HORIZONTAL = 2;
9029
9030 /**
9031  * Placement constant - The resizing element is to the left of the splitter element
9032  * @static
9033  * @type Number
9034  */
9035 Roo.SplitBar.LEFT = 1;
9036
9037 /**
9038  * Placement constant - The resizing element is to the right of the splitter element
9039  * @static
9040  * @type Number
9041  */
9042 Roo.SplitBar.RIGHT = 2;
9043
9044 /**
9045  * Placement constant - The resizing element is positioned above the splitter element
9046  * @static
9047  * @type Number
9048  */
9049 Roo.SplitBar.TOP = 3;
9050
9051 /**
9052  * Placement constant - The resizing element is positioned under splitter element
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.BOTTOM = 4;
9057 /*
9058  * Based on:
9059  * Ext JS Library 1.1.1
9060  * Copyright(c) 2006-2007, Ext JS, LLC.
9061  *
9062  * Originally Released Under LGPL - original licence link has changed is not relivant.
9063  *
9064  * Fork - LGPL
9065  * <script type="text/javascript">
9066  */
9067
9068 /**
9069  * @class Roo.View
9070  * @extends Roo.util.Observable
9071  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9072  * This class also supports single and multi selection modes. <br>
9073  * Create a data model bound view:
9074  <pre><code>
9075  var store = new Roo.data.Store(...);
9076
9077  var view = new Roo.View({
9078     el : "my-element",
9079     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9080  
9081     singleSelect: true,
9082     selectedClass: "ydataview-selected",
9083     store: store
9084  });
9085
9086  // listen for node click?
9087  view.on("click", function(vw, index, node, e){
9088  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9089  });
9090
9091  // load XML data
9092  dataModel.load("foobar.xml");
9093  </code></pre>
9094  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9095  * <br><br>
9096  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9097  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9098  * 
9099  * Note: old style constructor is still suported (container, template, config)
9100  * 
9101  * @constructor
9102  * Create a new View
9103  * @param {Object} config The config object
9104  * 
9105  */
9106 Roo.View = function(config, depreciated_tpl, depreciated_config){
9107     
9108     if (typeof(depreciated_tpl) == 'undefined') {
9109         // new way.. - universal constructor.
9110         Roo.apply(this, config);
9111         this.el  = Roo.get(this.el);
9112     } else {
9113         // old format..
9114         this.el  = Roo.get(config);
9115         this.tpl = depreciated_tpl;
9116         Roo.apply(this, depreciated_config);
9117     }
9118      
9119     
9120     if(typeof(this.tpl) == "string"){
9121         this.tpl = new Roo.Template(this.tpl);
9122     } else {
9123         // support xtype ctors..
9124         this.tpl = new Roo.factory(this.tpl, Roo);
9125     }
9126     
9127     
9128     this.tpl.compile();
9129    
9130
9131      
9132     /** @private */
9133     this.addEvents({
9134     /**
9135      * @event beforeclick
9136      * Fires before a click is processed. Returns false to cancel the default action.
9137      * @param {Roo.View} this
9138      * @param {Number} index The index of the target node
9139      * @param {HTMLElement} node The target node
9140      * @param {Roo.EventObject} e The raw event object
9141      */
9142         "beforeclick" : true,
9143     /**
9144      * @event click
9145      * Fires when a template node is clicked.
9146      * @param {Roo.View} this
9147      * @param {Number} index The index of the target node
9148      * @param {HTMLElement} node The target node
9149      * @param {Roo.EventObject} e The raw event object
9150      */
9151         "click" : true,
9152     /**
9153      * @event dblclick
9154      * Fires when a template node is double clicked.
9155      * @param {Roo.View} this
9156      * @param {Number} index The index of the target node
9157      * @param {HTMLElement} node The target node
9158      * @param {Roo.EventObject} e The raw event object
9159      */
9160         "dblclick" : true,
9161     /**
9162      * @event contextmenu
9163      * Fires when a template node is right clicked.
9164      * @param {Roo.View} this
9165      * @param {Number} index The index of the target node
9166      * @param {HTMLElement} node The target node
9167      * @param {Roo.EventObject} e The raw event object
9168      */
9169         "contextmenu" : true,
9170     /**
9171      * @event selectionchange
9172      * Fires when the selected nodes change.
9173      * @param {Roo.View} this
9174      * @param {Array} selections Array of the selected nodes
9175      */
9176         "selectionchange" : true,
9177
9178     /**
9179      * @event beforeselect
9180      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9181      * @param {Roo.View} this
9182      * @param {HTMLElement} node The node to be selected
9183      * @param {Array} selections Array of currently selected nodes
9184      */
9185         "beforeselect" : true
9186     });
9187
9188     this.el.on({
9189         "click": this.onClick,
9190         "dblclick": this.onDblClick,
9191         "contextmenu": this.onContextMenu,
9192         scope:this
9193     });
9194
9195     this.selections = [];
9196     this.nodes = [];
9197     this.cmp = new Roo.CompositeElementLite([]);
9198     if(this.store){
9199         this.store = Roo.factory(this.store, Roo.data);
9200         this.setStore(this.store, true);
9201     }
9202     Roo.View.superclass.constructor.call(this);
9203 };
9204
9205 Roo.extend(Roo.View, Roo.util.Observable, {
9206     
9207      /**
9208      * @cfg {Roo.data.Store} store Data store to load data from.
9209      */
9210     store : false,
9211     
9212     /**
9213      * @cfg {String|Roo.Element} el The container element.
9214      */
9215     el : '',
9216     
9217     /**
9218      * @cfg {String|Roo.Template} tpl The template used by this View 
9219      */
9220     tpl : false,
9221     
9222     /**
9223      * @cfg {String} selectedClass The css class to add to selected nodes
9224      */
9225     selectedClass : "x-view-selected",
9226      /**
9227      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9228      */
9229     emptyText : "",
9230     /**
9231      * @cfg {Boolean} multiSelect Allow multiple selection
9232      */
9233     
9234     multiSelect : false,
9235     /**
9236      * @cfg {Boolean} singleSelect Allow single selection
9237      */
9238     singleSelect:  false,
9239     
9240     /**
9241      * Returns the element this view is bound to.
9242      * @return {Roo.Element}
9243      */
9244     getEl : function(){
9245         return this.el;
9246     },
9247
9248     /**
9249      * Refreshes the view.
9250      */
9251     refresh : function(){
9252         var t = this.tpl;
9253         this.clearSelections();
9254         this.el.update("");
9255         var html = [];
9256         var records = this.store.getRange();
9257         if(records.length < 1){
9258             this.el.update(this.emptyText);
9259             return;
9260         }
9261         for(var i = 0, len = records.length; i < len; i++){
9262             var data = this.prepareData(records[i].data, i, records[i]);
9263             html[html.length] = t.apply(data);
9264         }
9265         this.el.update(html.join(""));
9266         this.nodes = this.el.dom.childNodes;
9267         this.updateIndexes(0);
9268     },
9269
9270     /**
9271      * Function to override to reformat the data that is sent to
9272      * the template for each node.
9273      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9274      * a JSON object for an UpdateManager bound view).
9275      */
9276     prepareData : function(data){
9277         return data;
9278     },
9279
9280     onUpdate : function(ds, record){
9281         this.clearSelections();
9282         var index = this.store.indexOf(record);
9283         var n = this.nodes[index];
9284         this.tpl.insertBefore(n, this.prepareData(record.data));
9285         n.parentNode.removeChild(n);
9286         this.updateIndexes(index, index);
9287     },
9288
9289     onAdd : function(ds, records, index){
9290         this.clearSelections();
9291         if(this.nodes.length == 0){
9292             this.refresh();
9293             return;
9294         }
9295         var n = this.nodes[index];
9296         for(var i = 0, len = records.length; i < len; i++){
9297             var d = this.prepareData(records[i].data);
9298             if(n){
9299                 this.tpl.insertBefore(n, d);
9300             }else{
9301                 this.tpl.append(this.el, d);
9302             }
9303         }
9304         this.updateIndexes(index);
9305     },
9306
9307     onRemove : function(ds, record, index){
9308         this.clearSelections();
9309         this.el.dom.removeChild(this.nodes[index]);
9310         this.updateIndexes(index);
9311     },
9312
9313     /**
9314      * Refresh an individual node.
9315      * @param {Number} index
9316      */
9317     refreshNode : function(index){
9318         this.onUpdate(this.store, this.store.getAt(index));
9319     },
9320
9321     updateIndexes : function(startIndex, endIndex){
9322         var ns = this.nodes;
9323         startIndex = startIndex || 0;
9324         endIndex = endIndex || ns.length - 1;
9325         for(var i = startIndex; i <= endIndex; i++){
9326             ns[i].nodeIndex = i;
9327         }
9328     },
9329
9330     /**
9331      * Changes the data store this view uses and refresh the view.
9332      * @param {Store} store
9333      */
9334     setStore : function(store, initial){
9335         if(!initial && this.store){
9336             this.store.un("datachanged", this.refresh);
9337             this.store.un("add", this.onAdd);
9338             this.store.un("remove", this.onRemove);
9339             this.store.un("update", this.onUpdate);
9340             this.store.un("clear", this.refresh);
9341         }
9342         if(store){
9343           
9344             store.on("datachanged", this.refresh, this);
9345             store.on("add", this.onAdd, this);
9346             store.on("remove", this.onRemove, this);
9347             store.on("update", this.onUpdate, this);
9348             store.on("clear", this.refresh, this);
9349         }
9350         
9351         if(store){
9352             this.refresh();
9353         }
9354     },
9355
9356     /**
9357      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9358      * @param {HTMLElement} node
9359      * @return {HTMLElement} The template node
9360      */
9361     findItemFromChild : function(node){
9362         var el = this.el.dom;
9363         if(!node || node.parentNode == el){
9364                     return node;
9365             }
9366             var p = node.parentNode;
9367             while(p && p != el){
9368             if(p.parentNode == el){
9369                 return p;
9370             }
9371             p = p.parentNode;
9372         }
9373             return null;
9374     },
9375
9376     /** @ignore */
9377     onClick : function(e){
9378         var item = this.findItemFromChild(e.getTarget());
9379         if(item){
9380             var index = this.indexOf(item);
9381             if(this.onItemClick(item, index, e) !== false){
9382                 this.fireEvent("click", this, index, item, e);
9383             }
9384         }else{
9385             this.clearSelections();
9386         }
9387     },
9388
9389     /** @ignore */
9390     onContextMenu : function(e){
9391         var item = this.findItemFromChild(e.getTarget());
9392         if(item){
9393             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9394         }
9395     },
9396
9397     /** @ignore */
9398     onDblClick : function(e){
9399         var item = this.findItemFromChild(e.getTarget());
9400         if(item){
9401             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9402         }
9403     },
9404
9405     onItemClick : function(item, index, e){
9406         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9407             return false;
9408         }
9409         if(this.multiSelect || this.singleSelect){
9410             if(this.multiSelect && e.shiftKey && this.lastSelection){
9411                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9412             }else{
9413                 this.select(item, this.multiSelect && e.ctrlKey);
9414                 this.lastSelection = item;
9415             }
9416             e.preventDefault();
9417         }
9418         return true;
9419     },
9420
9421     /**
9422      * Get the number of selected nodes.
9423      * @return {Number}
9424      */
9425     getSelectionCount : function(){
9426         return this.selections.length;
9427     },
9428
9429     /**
9430      * Get the currently selected nodes.
9431      * @return {Array} An array of HTMLElements
9432      */
9433     getSelectedNodes : function(){
9434         return this.selections;
9435     },
9436
9437     /**
9438      * Get the indexes of the selected nodes.
9439      * @return {Array}
9440      */
9441     getSelectedIndexes : function(){
9442         var indexes = [], s = this.selections;
9443         for(var i = 0, len = s.length; i < len; i++){
9444             indexes.push(s[i].nodeIndex);
9445         }
9446         return indexes;
9447     },
9448
9449     /**
9450      * Clear all selections
9451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9452      */
9453     clearSelections : function(suppressEvent){
9454         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9455             this.cmp.elements = this.selections;
9456             this.cmp.removeClass(this.selectedClass);
9457             this.selections = [];
9458             if(!suppressEvent){
9459                 this.fireEvent("selectionchange", this, this.selections);
9460             }
9461         }
9462     },
9463
9464     /**
9465      * Returns true if the passed node is selected
9466      * @param {HTMLElement/Number} node The node or node index
9467      * @return {Boolean}
9468      */
9469     isSelected : function(node){
9470         var s = this.selections;
9471         if(s.length < 1){
9472             return false;
9473         }
9474         node = this.getNode(node);
9475         return s.indexOf(node) !== -1;
9476     },
9477
9478     /**
9479      * Selects nodes.
9480      * @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
9481      * @param {Boolean} keepExisting (optional) true to keep existing selections
9482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9483      */
9484     select : function(nodeInfo, keepExisting, suppressEvent){
9485         if(nodeInfo instanceof Array){
9486             if(!keepExisting){
9487                 this.clearSelections(true);
9488             }
9489             for(var i = 0, len = nodeInfo.length; i < len; i++){
9490                 this.select(nodeInfo[i], true, true);
9491             }
9492         } else{
9493             var node = this.getNode(nodeInfo);
9494             if(node && !this.isSelected(node)){
9495                 if(!keepExisting){
9496                     this.clearSelections(true);
9497                 }
9498                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9499                     Roo.fly(node).addClass(this.selectedClass);
9500                     this.selections.push(node);
9501                     if(!suppressEvent){
9502                         this.fireEvent("selectionchange", this, this.selections);
9503                     }
9504                 }
9505             }
9506         }
9507     },
9508
9509     /**
9510      * Gets a template node.
9511      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9512      * @return {HTMLElement} The node or null if it wasn't found
9513      */
9514     getNode : function(nodeInfo){
9515         if(typeof nodeInfo == "string"){
9516             return document.getElementById(nodeInfo);
9517         }else if(typeof nodeInfo == "number"){
9518             return this.nodes[nodeInfo];
9519         }
9520         return nodeInfo;
9521     },
9522
9523     /**
9524      * Gets a range template nodes.
9525      * @param {Number} startIndex
9526      * @param {Number} endIndex
9527      * @return {Array} An array of nodes
9528      */
9529     getNodes : function(start, end){
9530         var ns = this.nodes;
9531         start = start || 0;
9532         end = typeof end == "undefined" ? ns.length - 1 : end;
9533         var nodes = [];
9534         if(start <= end){
9535             for(var i = start; i <= end; i++){
9536                 nodes.push(ns[i]);
9537             }
9538         } else{
9539             for(var i = start; i >= end; i--){
9540                 nodes.push(ns[i]);
9541             }
9542         }
9543         return nodes;
9544     },
9545
9546     /**
9547      * Finds the index of the passed node
9548      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9549      * @return {Number} The index of the node or -1
9550      */
9551     indexOf : function(node){
9552         node = this.getNode(node);
9553         if(typeof node.nodeIndex == "number"){
9554             return node.nodeIndex;
9555         }
9556         var ns = this.nodes;
9557         for(var i = 0, len = ns.length; i < len; i++){
9558             if(ns[i] == node){
9559                 return i;
9560             }
9561         }
9562         return -1;
9563     }
9564 });
9565 /*
9566  * Based on:
9567  * Ext JS Library 1.1.1
9568  * Copyright(c) 2006-2007, Ext JS, LLC.
9569  *
9570  * Originally Released Under LGPL - original licence link has changed is not relivant.
9571  *
9572  * Fork - LGPL
9573  * <script type="text/javascript">
9574  */
9575
9576 /**
9577  * @class Roo.JsonView
9578  * @extends Roo.View
9579  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9580 <pre><code>
9581 var view = new Roo.JsonView({
9582     container: "my-element",
9583     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9584     multiSelect: true, 
9585     jsonRoot: "data" 
9586 });
9587
9588 // listen for node click?
9589 view.on("click", function(vw, index, node, e){
9590     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9591 });
9592
9593 // direct load of JSON data
9594 view.load("foobar.php");
9595
9596 // Example from my blog list
9597 var tpl = new Roo.Template(
9598     '&lt;div class="entry"&gt;' +
9599     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9600     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9601     "&lt;/div&gt;&lt;hr /&gt;"
9602 );
9603
9604 var moreView = new Roo.JsonView({
9605     container :  "entry-list", 
9606     template : tpl,
9607     jsonRoot: "posts"
9608 });
9609 moreView.on("beforerender", this.sortEntries, this);
9610 moreView.load({
9611     url: "/blog/get-posts.php",
9612     params: "allposts=true",
9613     text: "Loading Blog Entries..."
9614 });
9615 </code></pre>
9616
9617 * Note: old code is supported with arguments : (container, template, config)
9618
9619
9620  * @constructor
9621  * Create a new JsonView
9622  * 
9623  * @param {Object} config The config object
9624  * 
9625  */
9626 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9627     
9628     
9629     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9630
9631     var um = this.el.getUpdateManager();
9632     um.setRenderer(this);
9633     um.on("update", this.onLoad, this);
9634     um.on("failure", this.onLoadException, this);
9635
9636     /**
9637      * @event beforerender
9638      * Fires before rendering of the downloaded JSON data.
9639      * @param {Roo.JsonView} this
9640      * @param {Object} data The JSON data loaded
9641      */
9642     /**
9643      * @event load
9644      * Fires when data is loaded.
9645      * @param {Roo.JsonView} this
9646      * @param {Object} data The JSON data loaded
9647      * @param {Object} response The raw Connect response object
9648      */
9649     /**
9650      * @event loadexception
9651      * Fires when loading fails.
9652      * @param {Roo.JsonView} this
9653      * @param {Object} response The raw Connect response object
9654      */
9655     this.addEvents({
9656         'beforerender' : true,
9657         'load' : true,
9658         'loadexception' : true
9659     });
9660 };
9661 Roo.extend(Roo.JsonView, Roo.View, {
9662     /**
9663      * @type {String} The root property in the loaded JSON object that contains the data
9664      */
9665     jsonRoot : "",
9666
9667     /**
9668      * Refreshes the view.
9669      */
9670     refresh : function(){
9671         this.clearSelections();
9672         this.el.update("");
9673         var html = [];
9674         var o = this.jsonData;
9675         if(o && o.length > 0){
9676             for(var i = 0, len = o.length; i < len; i++){
9677                 var data = this.prepareData(o[i], i, o);
9678                 html[html.length] = this.tpl.apply(data);
9679             }
9680         }else{
9681             html.push(this.emptyText);
9682         }
9683         this.el.update(html.join(""));
9684         this.nodes = this.el.dom.childNodes;
9685         this.updateIndexes(0);
9686     },
9687
9688     /**
9689      * 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.
9690      * @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:
9691      <pre><code>
9692      view.load({
9693          url: "your-url.php",
9694          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9695          callback: yourFunction,
9696          scope: yourObject, //(optional scope)
9697          discardUrl: false,
9698          nocache: false,
9699          text: "Loading...",
9700          timeout: 30,
9701          scripts: false
9702      });
9703      </code></pre>
9704      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9705      * 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.
9706      * @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}
9707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9708      * @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.
9709      */
9710     load : function(){
9711         var um = this.el.getUpdateManager();
9712         um.update.apply(um, arguments);
9713     },
9714
9715     render : function(el, response){
9716         this.clearSelections();
9717         this.el.update("");
9718         var o;
9719         try{
9720             o = Roo.util.JSON.decode(response.responseText);
9721             if(this.jsonRoot){
9722                 
9723                 o = o[this.jsonRoot];
9724             }
9725         } catch(e){
9726         }
9727         /**
9728          * The current JSON data or null
9729          */
9730         this.jsonData = o;
9731         this.beforeRender();
9732         this.refresh();
9733     },
9734
9735 /**
9736  * Get the number of records in the current JSON dataset
9737  * @return {Number}
9738  */
9739     getCount : function(){
9740         return this.jsonData ? this.jsonData.length : 0;
9741     },
9742
9743 /**
9744  * Returns the JSON object for the specified node(s)
9745  * @param {HTMLElement/Array} node The node or an array of nodes
9746  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9747  * you get the JSON object for the node
9748  */
9749     getNodeData : function(node){
9750         if(node instanceof Array){
9751             var data = [];
9752             for(var i = 0, len = node.length; i < len; i++){
9753                 data.push(this.getNodeData(node[i]));
9754             }
9755             return data;
9756         }
9757         return this.jsonData[this.indexOf(node)] || null;
9758     },
9759
9760     beforeRender : function(){
9761         this.snapshot = this.jsonData;
9762         if(this.sortInfo){
9763             this.sort.apply(this, this.sortInfo);
9764         }
9765         this.fireEvent("beforerender", this, this.jsonData);
9766     },
9767
9768     onLoad : function(el, o){
9769         this.fireEvent("load", this, this.jsonData, o);
9770     },
9771
9772     onLoadException : function(el, o){
9773         this.fireEvent("loadexception", this, o);
9774     },
9775
9776 /**
9777  * Filter the data by a specific property.
9778  * @param {String} property A property on your JSON objects
9779  * @param {String/RegExp} value Either string that the property values
9780  * should start with, or a RegExp to test against the property
9781  */
9782     filter : function(property, value){
9783         if(this.jsonData){
9784             var data = [];
9785             var ss = this.snapshot;
9786             if(typeof value == "string"){
9787                 var vlen = value.length;
9788                 if(vlen == 0){
9789                     this.clearFilter();
9790                     return;
9791                 }
9792                 value = value.toLowerCase();
9793                 for(var i = 0, len = ss.length; i < len; i++){
9794                     var o = ss[i];
9795                     if(o[property].substr(0, vlen).toLowerCase() == value){
9796                         data.push(o);
9797                     }
9798                 }
9799             } else if(value.exec){ // regex?
9800                 for(var i = 0, len = ss.length; i < len; i++){
9801                     var o = ss[i];
9802                     if(value.test(o[property])){
9803                         data.push(o);
9804                     }
9805                 }
9806             } else{
9807                 return;
9808             }
9809             this.jsonData = data;
9810             this.refresh();
9811         }
9812     },
9813
9814 /**
9815  * Filter by a function. The passed function will be called with each
9816  * object in the current dataset. If the function returns true the value is kept,
9817  * otherwise it is filtered.
9818  * @param {Function} fn
9819  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9820  */
9821     filterBy : function(fn, scope){
9822         if(this.jsonData){
9823             var data = [];
9824             var ss = this.snapshot;
9825             for(var i = 0, len = ss.length; i < len; i++){
9826                 var o = ss[i];
9827                 if(fn.call(scope || this, o)){
9828                     data.push(o);
9829                 }
9830             }
9831             this.jsonData = data;
9832             this.refresh();
9833         }
9834     },
9835
9836 /**
9837  * Clears the current filter.
9838  */
9839     clearFilter : function(){
9840         if(this.snapshot && this.jsonData != this.snapshot){
9841             this.jsonData = this.snapshot;
9842             this.refresh();
9843         }
9844     },
9845
9846
9847 /**
9848  * Sorts the data for this view and refreshes it.
9849  * @param {String} property A property on your JSON objects to sort on
9850  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9851  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9852  */
9853     sort : function(property, dir, sortType){
9854         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9855         if(this.jsonData){
9856             var p = property;
9857             var dsc = dir && dir.toLowerCase() == "desc";
9858             var f = function(o1, o2){
9859                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9860                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9861                 ;
9862                 if(v1 < v2){
9863                     return dsc ? +1 : -1;
9864                 } else if(v1 > v2){
9865                     return dsc ? -1 : +1;
9866                 } else{
9867                     return 0;
9868                 }
9869             };
9870             this.jsonData.sort(f);
9871             this.refresh();
9872             if(this.jsonData != this.snapshot){
9873                 this.snapshot.sort(f);
9874             }
9875         }
9876     }
9877 });/*
9878  * Based on:
9879  * Ext JS Library 1.1.1
9880  * Copyright(c) 2006-2007, Ext JS, LLC.
9881  *
9882  * Originally Released Under LGPL - original licence link has changed is not relivant.
9883  *
9884  * Fork - LGPL
9885  * <script type="text/javascript">
9886  */
9887  
9888
9889 /**
9890  * @class Roo.ColorPalette
9891  * @extends Roo.Component
9892  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9893  * Here's an example of typical usage:
9894  * <pre><code>
9895 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9896 cp.render('my-div');
9897
9898 cp.on('select', function(palette, selColor){
9899     // do something with selColor
9900 });
9901 </code></pre>
9902  * @constructor
9903  * Create a new ColorPalette
9904  * @param {Object} config The config object
9905  */
9906 Roo.ColorPalette = function(config){
9907     Roo.ColorPalette.superclass.constructor.call(this, config);
9908     this.addEvents({
9909         /**
9910              * @event select
9911              * Fires when a color is selected
9912              * @param {ColorPalette} this
9913              * @param {String} color The 6-digit color hex code (without the # symbol)
9914              */
9915         select: true
9916     });
9917
9918     if(this.handler){
9919         this.on("select", this.handler, this.scope, true);
9920     }
9921 };
9922 Roo.extend(Roo.ColorPalette, Roo.Component, {
9923     /**
9924      * @cfg {String} itemCls
9925      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9926      */
9927     itemCls : "x-color-palette",
9928     /**
9929      * @cfg {String} value
9930      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9931      * the hex codes are case-sensitive.
9932      */
9933     value : null,
9934     clickEvent:'click',
9935     // private
9936     ctype: "Roo.ColorPalette",
9937
9938     /**
9939      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9940      */
9941     allowReselect : false,
9942
9943     /**
9944      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9945      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9946      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9947      * of colors with the width setting until the box is symmetrical.</p>
9948      * <p>You can override individual colors if needed:</p>
9949      * <pre><code>
9950 var cp = new Roo.ColorPalette();
9951 cp.colors[0] = "FF0000";  // change the first box to red
9952 </code></pre>
9953
9954 Or you can provide a custom array of your own for complete control:
9955 <pre><code>
9956 var cp = new Roo.ColorPalette();
9957 cp.colors = ["000000", "993300", "333300"];
9958 </code></pre>
9959      * @type Array
9960      */
9961     colors : [
9962         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9963         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9964         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9965         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9966         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9967     ],
9968
9969     // private
9970     onRender : function(container, position){
9971         var t = new Roo.MasterTemplate(
9972             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9973         );
9974         var c = this.colors;
9975         for(var i = 0, len = c.length; i < len; i++){
9976             t.add([c[i]]);
9977         }
9978         var el = document.createElement("div");
9979         el.className = this.itemCls;
9980         t.overwrite(el);
9981         container.dom.insertBefore(el, position);
9982         this.el = Roo.get(el);
9983         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9984         if(this.clickEvent != 'click'){
9985             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9986         }
9987     },
9988
9989     // private
9990     afterRender : function(){
9991         Roo.ColorPalette.superclass.afterRender.call(this);
9992         if(this.value){
9993             var s = this.value;
9994             this.value = null;
9995             this.select(s);
9996         }
9997     },
9998
9999     // private
10000     handleClick : function(e, t){
10001         e.preventDefault();
10002         if(!this.disabled){
10003             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10004             this.select(c.toUpperCase());
10005         }
10006     },
10007
10008     /**
10009      * Selects the specified color in the palette (fires the select event)
10010      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10011      */
10012     select : function(color){
10013         color = color.replace("#", "");
10014         if(color != this.value || this.allowReselect){
10015             var el = this.el;
10016             if(this.value){
10017                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10018             }
10019             el.child("a.color-"+color).addClass("x-color-palette-sel");
10020             this.value = color;
10021             this.fireEvent("select", this, color);
10022         }
10023     }
10024 });/*
10025  * Based on:
10026  * Ext JS Library 1.1.1
10027  * Copyright(c) 2006-2007, Ext JS, LLC.
10028  *
10029  * Originally Released Under LGPL - original licence link has changed is not relivant.
10030  *
10031  * Fork - LGPL
10032  * <script type="text/javascript">
10033  */
10034  
10035 /**
10036  * @class Roo.DatePicker
10037  * @extends Roo.Component
10038  * Simple date picker class.
10039  * @constructor
10040  * Create a new DatePicker
10041  * @param {Object} config The config object
10042  */
10043 Roo.DatePicker = function(config){
10044     Roo.DatePicker.superclass.constructor.call(this, config);
10045
10046     this.value = config && config.value ?
10047                  config.value.clearTime() : new Date().clearTime();
10048
10049     this.addEvents({
10050         /**
10051              * @event select
10052              * Fires when a date is selected
10053              * @param {DatePicker} this
10054              * @param {Date} date The selected date
10055              */
10056         select: true
10057     });
10058
10059     if(this.handler){
10060         this.on("select", this.handler,  this.scope || this);
10061     }
10062     // build the disabledDatesRE
10063     if(!this.disabledDatesRE && this.disabledDates){
10064         var dd = this.disabledDates;
10065         var re = "(?:";
10066         for(var i = 0; i < dd.length; i++){
10067             re += dd[i];
10068             if(i != dd.length-1) re += "|";
10069         }
10070         this.disabledDatesRE = new RegExp(re + ")");
10071     }
10072 };
10073
10074 Roo.extend(Roo.DatePicker, Roo.Component, {
10075     /**
10076      * @cfg {String} todayText
10077      * The text to display on the button that selects the current date (defaults to "Today")
10078      */
10079     todayText : "Today",
10080     /**
10081      * @cfg {String} okText
10082      * The text to display on the ok button
10083      */
10084     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10085     /**
10086      * @cfg {String} cancelText
10087      * The text to display on the cancel button
10088      */
10089     cancelText : "Cancel",
10090     /**
10091      * @cfg {String} todayTip
10092      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10093      */
10094     todayTip : "{0} (Spacebar)",
10095     /**
10096      * @cfg {Date} minDate
10097      * Minimum allowable date (JavaScript date object, defaults to null)
10098      */
10099     minDate : null,
10100     /**
10101      * @cfg {Date} maxDate
10102      * Maximum allowable date (JavaScript date object, defaults to null)
10103      */
10104     maxDate : null,
10105     /**
10106      * @cfg {String} minText
10107      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10108      */
10109     minText : "This date is before the minimum date",
10110     /**
10111      * @cfg {String} maxText
10112      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10113      */
10114     maxText : "This date is after the maximum date",
10115     /**
10116      * @cfg {String} format
10117      * The default date format string which can be overriden for localization support.  The format must be
10118      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10119      */
10120     format : "m/d/y",
10121     /**
10122      * @cfg {Array} disabledDays
10123      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10124      */
10125     disabledDays : null,
10126     /**
10127      * @cfg {String} disabledDaysText
10128      * The tooltip to display when the date falls on a disabled day (defaults to "")
10129      */
10130     disabledDaysText : "",
10131     /**
10132      * @cfg {RegExp} disabledDatesRE
10133      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10134      */
10135     disabledDatesRE : null,
10136     /**
10137      * @cfg {String} disabledDatesText
10138      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10139      */
10140     disabledDatesText : "",
10141     /**
10142      * @cfg {Boolean} constrainToViewport
10143      * True to constrain the date picker to the viewport (defaults to true)
10144      */
10145     constrainToViewport : true,
10146     /**
10147      * @cfg {Array} monthNames
10148      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10149      */
10150     monthNames : Date.monthNames,
10151     /**
10152      * @cfg {Array} dayNames
10153      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10154      */
10155     dayNames : Date.dayNames,
10156     /**
10157      * @cfg {String} nextText
10158      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10159      */
10160     nextText: 'Next Month (Control+Right)',
10161     /**
10162      * @cfg {String} prevText
10163      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10164      */
10165     prevText: 'Previous Month (Control+Left)',
10166     /**
10167      * @cfg {String} monthYearText
10168      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10169      */
10170     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10171     /**
10172      * @cfg {Number} startDay
10173      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10174      */
10175     startDay : 0,
10176     /**
10177      * @cfg {Bool} showClear
10178      * Show a clear button (usefull for date form elements that can be blank.)
10179      */
10180     
10181     showClear: false,
10182     
10183     /**
10184      * Sets the value of the date field
10185      * @param {Date} value The date to set
10186      */
10187     setValue : function(value){
10188         var old = this.value;
10189         this.value = value.clearTime(true);
10190         if(this.el){
10191             this.update(this.value);
10192         }
10193     },
10194
10195     /**
10196      * Gets the current selected value of the date field
10197      * @return {Date} The selected date
10198      */
10199     getValue : function(){
10200         return this.value;
10201     },
10202
10203     // private
10204     focus : function(){
10205         if(this.el){
10206             this.update(this.activeDate);
10207         }
10208     },
10209
10210     // private
10211     onRender : function(container, position){
10212         var m = [
10213              '<table cellspacing="0">',
10214                 '<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>',
10215                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10216         var dn = this.dayNames;
10217         for(var i = 0; i < 7; i++){
10218             var d = this.startDay+i;
10219             if(d > 6){
10220                 d = d-7;
10221             }
10222             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10223         }
10224         m[m.length] = "</tr></thead><tbody><tr>";
10225         for(var i = 0; i < 42; i++) {
10226             if(i % 7 == 0 && i != 0){
10227                 m[m.length] = "</tr><tr>";
10228             }
10229             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10230         }
10231         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10232             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10233
10234         var el = document.createElement("div");
10235         el.className = "x-date-picker";
10236         el.innerHTML = m.join("");
10237
10238         container.dom.insertBefore(el, position);
10239
10240         this.el = Roo.get(el);
10241         this.eventEl = Roo.get(el.firstChild);
10242
10243         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10244             handler: this.showPrevMonth,
10245             scope: this,
10246             preventDefault:true,
10247             stopDefault:true
10248         });
10249
10250         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10251             handler: this.showNextMonth,
10252             scope: this,
10253             preventDefault:true,
10254             stopDefault:true
10255         });
10256
10257         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10258
10259         this.monthPicker = this.el.down('div.x-date-mp');
10260         this.monthPicker.enableDisplayMode('block');
10261         
10262         var kn = new Roo.KeyNav(this.eventEl, {
10263             "left" : function(e){
10264                 e.ctrlKey ?
10265                     this.showPrevMonth() :
10266                     this.update(this.activeDate.add("d", -1));
10267             },
10268
10269             "right" : function(e){
10270                 e.ctrlKey ?
10271                     this.showNextMonth() :
10272                     this.update(this.activeDate.add("d", 1));
10273             },
10274
10275             "up" : function(e){
10276                 e.ctrlKey ?
10277                     this.showNextYear() :
10278                     this.update(this.activeDate.add("d", -7));
10279             },
10280
10281             "down" : function(e){
10282                 e.ctrlKey ?
10283                     this.showPrevYear() :
10284                     this.update(this.activeDate.add("d", 7));
10285             },
10286
10287             "pageUp" : function(e){
10288                 this.showNextMonth();
10289             },
10290
10291             "pageDown" : function(e){
10292                 this.showPrevMonth();
10293             },
10294
10295             "enter" : function(e){
10296                 e.stopPropagation();
10297                 return true;
10298             },
10299
10300             scope : this
10301         });
10302
10303         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10304
10305         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10306
10307         this.el.unselectable();
10308         
10309         this.cells = this.el.select("table.x-date-inner tbody td");
10310         this.textNodes = this.el.query("table.x-date-inner tbody span");
10311
10312         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10313             text: "&#160;",
10314             tooltip: this.monthYearText
10315         });
10316
10317         this.mbtn.on('click', this.showMonthPicker, this);
10318         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10319
10320
10321         var today = (new Date()).dateFormat(this.format);
10322         
10323         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10324         if (this.showClear) {
10325             baseTb.add( new Roo.Toolbar.Fill());
10326         }
10327         baseTb.add({
10328             text: String.format(this.todayText, today),
10329             tooltip: String.format(this.todayTip, today),
10330             handler: this.selectToday,
10331             scope: this
10332         });
10333         
10334         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10335             
10336         //});
10337         if (this.showClear) {
10338             
10339             baseTb.add( new Roo.Toolbar.Fill());
10340             baseTb.add({
10341                 text: '&#160;',
10342                 cls: 'x-btn-icon x-btn-clear',
10343                 handler: function() {
10344                     //this.value = '';
10345                     this.fireEvent("select", this, '');
10346                 },
10347                 scope: this
10348             });
10349         }
10350         
10351         
10352         if(Roo.isIE){
10353             this.el.repaint();
10354         }
10355         this.update(this.value);
10356     },
10357
10358     createMonthPicker : function(){
10359         if(!this.monthPicker.dom.firstChild){
10360             var buf = ['<table border="0" cellspacing="0">'];
10361             for(var i = 0; i < 6; i++){
10362                 buf.push(
10363                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10364                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10365                     i == 0 ?
10366                     '<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>' :
10367                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10368                 );
10369             }
10370             buf.push(
10371                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10372                     this.okText,
10373                     '</button><button type="button" class="x-date-mp-cancel">',
10374                     this.cancelText,
10375                     '</button></td></tr>',
10376                 '</table>'
10377             );
10378             this.monthPicker.update(buf.join(''));
10379             this.monthPicker.on('click', this.onMonthClick, this);
10380             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10381
10382             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10383             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10384
10385             this.mpMonths.each(function(m, a, i){
10386                 i += 1;
10387                 if((i%2) == 0){
10388                     m.dom.xmonth = 5 + Math.round(i * .5);
10389                 }else{
10390                     m.dom.xmonth = Math.round((i-1) * .5);
10391                 }
10392             });
10393         }
10394     },
10395
10396     showMonthPicker : function(){
10397         this.createMonthPicker();
10398         var size = this.el.getSize();
10399         this.monthPicker.setSize(size);
10400         this.monthPicker.child('table').setSize(size);
10401
10402         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10403         this.updateMPMonth(this.mpSelMonth);
10404         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10405         this.updateMPYear(this.mpSelYear);
10406
10407         this.monthPicker.slideIn('t', {duration:.2});
10408     },
10409
10410     updateMPYear : function(y){
10411         this.mpyear = y;
10412         var ys = this.mpYears.elements;
10413         for(var i = 1; i <= 10; i++){
10414             var td = ys[i-1], y2;
10415             if((i%2) == 0){
10416                 y2 = y + Math.round(i * .5);
10417                 td.firstChild.innerHTML = y2;
10418                 td.xyear = y2;
10419             }else{
10420                 y2 = y - (5-Math.round(i * .5));
10421                 td.firstChild.innerHTML = y2;
10422                 td.xyear = y2;
10423             }
10424             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10425         }
10426     },
10427
10428     updateMPMonth : function(sm){
10429         this.mpMonths.each(function(m, a, i){
10430             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10431         });
10432     },
10433
10434     selectMPMonth: function(m){
10435         
10436     },
10437
10438     onMonthClick : function(e, t){
10439         e.stopEvent();
10440         var el = new Roo.Element(t), pn;
10441         if(el.is('button.x-date-mp-cancel')){
10442             this.hideMonthPicker();
10443         }
10444         else if(el.is('button.x-date-mp-ok')){
10445             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10446             this.hideMonthPicker();
10447         }
10448         else if(pn = el.up('td.x-date-mp-month', 2)){
10449             this.mpMonths.removeClass('x-date-mp-sel');
10450             pn.addClass('x-date-mp-sel');
10451             this.mpSelMonth = pn.dom.xmonth;
10452         }
10453         else if(pn = el.up('td.x-date-mp-year', 2)){
10454             this.mpYears.removeClass('x-date-mp-sel');
10455             pn.addClass('x-date-mp-sel');
10456             this.mpSelYear = pn.dom.xyear;
10457         }
10458         else if(el.is('a.x-date-mp-prev')){
10459             this.updateMPYear(this.mpyear-10);
10460         }
10461         else if(el.is('a.x-date-mp-next')){
10462             this.updateMPYear(this.mpyear+10);
10463         }
10464     },
10465
10466     onMonthDblClick : function(e, t){
10467         e.stopEvent();
10468         var el = new Roo.Element(t), pn;
10469         if(pn = el.up('td.x-date-mp-month', 2)){
10470             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10471             this.hideMonthPicker();
10472         }
10473         else if(pn = el.up('td.x-date-mp-year', 2)){
10474             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10475             this.hideMonthPicker();
10476         }
10477     },
10478
10479     hideMonthPicker : function(disableAnim){
10480         if(this.monthPicker){
10481             if(disableAnim === true){
10482                 this.monthPicker.hide();
10483             }else{
10484                 this.monthPicker.slideOut('t', {duration:.2});
10485             }
10486         }
10487     },
10488
10489     // private
10490     showPrevMonth : function(e){
10491         this.update(this.activeDate.add("mo", -1));
10492     },
10493
10494     // private
10495     showNextMonth : function(e){
10496         this.update(this.activeDate.add("mo", 1));
10497     },
10498
10499     // private
10500     showPrevYear : function(){
10501         this.update(this.activeDate.add("y", -1));
10502     },
10503
10504     // private
10505     showNextYear : function(){
10506         this.update(this.activeDate.add("y", 1));
10507     },
10508
10509     // private
10510     handleMouseWheel : function(e){
10511         var delta = e.getWheelDelta();
10512         if(delta > 0){
10513             this.showPrevMonth();
10514             e.stopEvent();
10515         } else if(delta < 0){
10516             this.showNextMonth();
10517             e.stopEvent();
10518         }
10519     },
10520
10521     // private
10522     handleDateClick : function(e, t){
10523         e.stopEvent();
10524         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10525             this.setValue(new Date(t.dateValue));
10526             this.fireEvent("select", this, this.value);
10527         }
10528     },
10529
10530     // private
10531     selectToday : function(){
10532         this.setValue(new Date().clearTime());
10533         this.fireEvent("select", this, this.value);
10534     },
10535
10536     // private
10537     update : function(date){
10538         var vd = this.activeDate;
10539         this.activeDate = date;
10540         if(vd && this.el){
10541             var t = date.getTime();
10542             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10543                 this.cells.removeClass("x-date-selected");
10544                 this.cells.each(function(c){
10545                    if(c.dom.firstChild.dateValue == t){
10546                        c.addClass("x-date-selected");
10547                        setTimeout(function(){
10548                             try{c.dom.firstChild.focus();}catch(e){}
10549                        }, 50);
10550                        return false;
10551                    }
10552                 });
10553                 return;
10554             }
10555         }
10556         var days = date.getDaysInMonth();
10557         var firstOfMonth = date.getFirstDateOfMonth();
10558         var startingPos = firstOfMonth.getDay()-this.startDay;
10559
10560         if(startingPos <= this.startDay){
10561             startingPos += 7;
10562         }
10563
10564         var pm = date.add("mo", -1);
10565         var prevStart = pm.getDaysInMonth()-startingPos;
10566
10567         var cells = this.cells.elements;
10568         var textEls = this.textNodes;
10569         days += startingPos;
10570
10571         // convert everything to numbers so it's fast
10572         var day = 86400000;
10573         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10574         var today = new Date().clearTime().getTime();
10575         var sel = date.clearTime().getTime();
10576         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10577         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10578         var ddMatch = this.disabledDatesRE;
10579         var ddText = this.disabledDatesText;
10580         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10581         var ddaysText = this.disabledDaysText;
10582         var format = this.format;
10583
10584         var setCellClass = function(cal, cell){
10585             cell.title = "";
10586             var t = d.getTime();
10587             cell.firstChild.dateValue = t;
10588             if(t == today){
10589                 cell.className += " x-date-today";
10590                 cell.title = cal.todayText;
10591             }
10592             if(t == sel){
10593                 cell.className += " x-date-selected";
10594                 setTimeout(function(){
10595                     try{cell.firstChild.focus();}catch(e){}
10596                 }, 50);
10597             }
10598             // disabling
10599             if(t < min) {
10600                 cell.className = " x-date-disabled";
10601                 cell.title = cal.minText;
10602                 return;
10603             }
10604             if(t > max) {
10605                 cell.className = " x-date-disabled";
10606                 cell.title = cal.maxText;
10607                 return;
10608             }
10609             if(ddays){
10610                 if(ddays.indexOf(d.getDay()) != -1){
10611                     cell.title = ddaysText;
10612                     cell.className = " x-date-disabled";
10613                 }
10614             }
10615             if(ddMatch && format){
10616                 var fvalue = d.dateFormat(format);
10617                 if(ddMatch.test(fvalue)){
10618                     cell.title = ddText.replace("%0", fvalue);
10619                     cell.className = " x-date-disabled";
10620                 }
10621             }
10622         };
10623
10624         var i = 0;
10625         for(; i < startingPos; i++) {
10626             textEls[i].innerHTML = (++prevStart);
10627             d.setDate(d.getDate()+1);
10628             cells[i].className = "x-date-prevday";
10629             setCellClass(this, cells[i]);
10630         }
10631         for(; i < days; i++){
10632             intDay = i - startingPos + 1;
10633             textEls[i].innerHTML = (intDay);
10634             d.setDate(d.getDate()+1);
10635             cells[i].className = "x-date-active";
10636             setCellClass(this, cells[i]);
10637         }
10638         var extraDays = 0;
10639         for(; i < 42; i++) {
10640              textEls[i].innerHTML = (++extraDays);
10641              d.setDate(d.getDate()+1);
10642              cells[i].className = "x-date-nextday";
10643              setCellClass(this, cells[i]);
10644         }
10645
10646         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10647
10648         if(!this.internalRender){
10649             var main = this.el.dom.firstChild;
10650             var w = main.offsetWidth;
10651             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10652             Roo.fly(main).setWidth(w);
10653             this.internalRender = true;
10654             // opera does not respect the auto grow header center column
10655             // then, after it gets a width opera refuses to recalculate
10656             // without a second pass
10657             if(Roo.isOpera && !this.secondPass){
10658                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10659                 this.secondPass = true;
10660                 this.update.defer(10, this, [date]);
10661             }
10662         }
10663     }
10664 });/*
10665  * Based on:
10666  * Ext JS Library 1.1.1
10667  * Copyright(c) 2006-2007, Ext JS, LLC.
10668  *
10669  * Originally Released Under LGPL - original licence link has changed is not relivant.
10670  *
10671  * Fork - LGPL
10672  * <script type="text/javascript">
10673  */
10674 /**
10675  * @class Roo.TabPanel
10676  * @extends Roo.util.Observable
10677  * A lightweight tab container.
10678  * <br><br>
10679  * Usage:
10680  * <pre><code>
10681 // basic tabs 1, built from existing content
10682 var tabs = new Roo.TabPanel("tabs1");
10683 tabs.addTab("script", "View Script");
10684 tabs.addTab("markup", "View Markup");
10685 tabs.activate("script");
10686
10687 // more advanced tabs, built from javascript
10688 var jtabs = new Roo.TabPanel("jtabs");
10689 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10690
10691 // set up the UpdateManager
10692 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10693 var updater = tab2.getUpdateManager();
10694 updater.setDefaultUrl("ajax1.htm");
10695 tab2.on('activate', updater.refresh, updater, true);
10696
10697 // Use setUrl for Ajax loading
10698 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10699 tab3.setUrl("ajax2.htm", null, true);
10700
10701 // Disabled tab
10702 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10703 tab4.disable();
10704
10705 jtabs.activate("jtabs-1");
10706  * </code></pre>
10707  * @constructor
10708  * Create a new TabPanel.
10709  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10710  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10711  */
10712 Roo.TabPanel = function(container, config){
10713     /**
10714     * The container element for this TabPanel.
10715     * @type Roo.Element
10716     */
10717     this.el = Roo.get(container, true);
10718     if(config){
10719         if(typeof config == "boolean"){
10720             this.tabPosition = config ? "bottom" : "top";
10721         }else{
10722             Roo.apply(this, config);
10723         }
10724     }
10725     if(this.tabPosition == "bottom"){
10726         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10727         this.el.addClass("x-tabs-bottom");
10728     }
10729     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10730     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10731     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10732     if(Roo.isIE){
10733         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10734     }
10735     if(this.tabPosition != "bottom"){
10736     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10737      * @type Roo.Element
10738      */
10739       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10740       this.el.addClass("x-tabs-top");
10741     }
10742     this.items = [];
10743
10744     this.bodyEl.setStyle("position", "relative");
10745
10746     this.active = null;
10747     this.activateDelegate = this.activate.createDelegate(this);
10748
10749     this.addEvents({
10750         /**
10751          * @event tabchange
10752          * Fires when the active tab changes
10753          * @param {Roo.TabPanel} this
10754          * @param {Roo.TabPanelItem} activePanel The new active tab
10755          */
10756         "tabchange": true,
10757         /**
10758          * @event beforetabchange
10759          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10760          * @param {Roo.TabPanel} this
10761          * @param {Object} e Set cancel to true on this object to cancel the tab change
10762          * @param {Roo.TabPanelItem} tab The tab being changed to
10763          */
10764         "beforetabchange" : true
10765     });
10766
10767     Roo.EventManager.onWindowResize(this.onResize, this);
10768     this.cpad = this.el.getPadding("lr");
10769     this.hiddenCount = 0;
10770
10771     Roo.TabPanel.superclass.constructor.call(this);
10772 };
10773
10774 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10775         /*
10776          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10777          */
10778     tabPosition : "top",
10779         /*
10780          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10781          */
10782     currentTabWidth : 0,
10783         /*
10784          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10785          */
10786     minTabWidth : 40,
10787         /*
10788          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10789          */
10790     maxTabWidth : 250,
10791         /*
10792          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10793          */
10794     preferredTabWidth : 175,
10795         /*
10796          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10797          */
10798     resizeTabs : false,
10799         /*
10800          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10801          */
10802     monitorResize : true,
10803
10804     /**
10805      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10806      * @param {String} id The id of the div to use <b>or create</b>
10807      * @param {String} text The text for the tab
10808      * @param {String} content (optional) Content to put in the TabPanelItem body
10809      * @param {Boolean} closable (optional) True to create a close icon on the tab
10810      * @return {Roo.TabPanelItem} The created TabPanelItem
10811      */
10812     addTab : function(id, text, content, closable){
10813         var item = new Roo.TabPanelItem(this, id, text, closable);
10814         this.addTabItem(item);
10815         if(content){
10816             item.setContent(content);
10817         }
10818         return item;
10819     },
10820
10821     /**
10822      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10823      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10824      * @return {Roo.TabPanelItem}
10825      */
10826     getTab : function(id){
10827         return this.items[id];
10828     },
10829
10830     /**
10831      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10832      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10833      */
10834     hideTab : function(id){
10835         var t = this.items[id];
10836         if(!t.isHidden()){
10837            t.setHidden(true);
10838            this.hiddenCount++;
10839            this.autoSizeTabs();
10840         }
10841     },
10842
10843     /**
10844      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10845      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10846      */
10847     unhideTab : function(id){
10848         var t = this.items[id];
10849         if(t.isHidden()){
10850            t.setHidden(false);
10851            this.hiddenCount--;
10852            this.autoSizeTabs();
10853         }
10854     },
10855
10856     /**
10857      * Adds an existing {@link Roo.TabPanelItem}.
10858      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10859      */
10860     addTabItem : function(item){
10861         this.items[item.id] = item;
10862         this.items.push(item);
10863         if(this.resizeTabs){
10864            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10865            this.autoSizeTabs();
10866         }else{
10867             item.autoSize();
10868         }
10869     },
10870
10871     /**
10872      * Removes a {@link Roo.TabPanelItem}.
10873      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10874      */
10875     removeTab : function(id){
10876         var items = this.items;
10877         var tab = items[id];
10878         if(!tab) { return; }
10879         var index = items.indexOf(tab);
10880         if(this.active == tab && items.length > 1){
10881             var newTab = this.getNextAvailable(index);
10882             if(newTab) {
10883                 newTab.activate();
10884             }
10885         }
10886         this.stripEl.dom.removeChild(tab.pnode.dom);
10887         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10888             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10889         }
10890         items.splice(index, 1);
10891         delete this.items[tab.id];
10892         tab.fireEvent("close", tab);
10893         tab.purgeListeners();
10894         this.autoSizeTabs();
10895     },
10896
10897     getNextAvailable : function(start){
10898         var items = this.items;
10899         var index = start;
10900         // look for a next tab that will slide over to
10901         // replace the one being removed
10902         while(index < items.length){
10903             var item = items[++index];
10904             if(item && !item.isHidden()){
10905                 return item;
10906             }
10907         }
10908         // if one isn't found select the previous tab (on the left)
10909         index = start;
10910         while(index >= 0){
10911             var item = items[--index];
10912             if(item && !item.isHidden()){
10913                 return item;
10914             }
10915         }
10916         return null;
10917     },
10918
10919     /**
10920      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10921      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10922      */
10923     disableTab : function(id){
10924         var tab = this.items[id];
10925         if(tab && this.active != tab){
10926             tab.disable();
10927         }
10928     },
10929
10930     /**
10931      * Enables a {@link Roo.TabPanelItem} that is disabled.
10932      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10933      */
10934     enableTab : function(id){
10935         var tab = this.items[id];
10936         tab.enable();
10937     },
10938
10939     /**
10940      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10941      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10942      * @return {Roo.TabPanelItem} The TabPanelItem.
10943      */
10944     activate : function(id){
10945         var tab = this.items[id];
10946         if(!tab){
10947             return null;
10948         }
10949         if(tab == this.active || tab.disabled){
10950             return tab;
10951         }
10952         var e = {};
10953         this.fireEvent("beforetabchange", this, e, tab);
10954         if(e.cancel !== true && !tab.disabled){
10955             if(this.active){
10956                 this.active.hide();
10957             }
10958             this.active = this.items[id];
10959             this.active.show();
10960             this.fireEvent("tabchange", this, this.active);
10961         }
10962         return tab;
10963     },
10964
10965     /**
10966      * Gets the active {@link Roo.TabPanelItem}.
10967      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10968      */
10969     getActiveTab : function(){
10970         return this.active;
10971     },
10972
10973     /**
10974      * Updates the tab body element to fit the height of the container element
10975      * for overflow scrolling
10976      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10977      */
10978     syncHeight : function(targetHeight){
10979         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10980         var bm = this.bodyEl.getMargins();
10981         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10982         this.bodyEl.setHeight(newHeight);
10983         return newHeight;
10984     },
10985
10986     onResize : function(){
10987         if(this.monitorResize){
10988             this.autoSizeTabs();
10989         }
10990     },
10991
10992     /**
10993      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10994      */
10995     beginUpdate : function(){
10996         this.updating = true;
10997     },
10998
10999     /**
11000      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11001      */
11002     endUpdate : function(){
11003         this.updating = false;
11004         this.autoSizeTabs();
11005     },
11006
11007     /**
11008      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11009      */
11010     autoSizeTabs : function(){
11011         var count = this.items.length;
11012         var vcount = count - this.hiddenCount;
11013         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11014         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11015         var availWidth = Math.floor(w / vcount);
11016         var b = this.stripBody;
11017         if(b.getWidth() > w){
11018             var tabs = this.items;
11019             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11020             if(availWidth < this.minTabWidth){
11021                 /*if(!this.sleft){    // incomplete scrolling code
11022                     this.createScrollButtons();
11023                 }
11024                 this.showScroll();
11025                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11026             }
11027         }else{
11028             if(this.currentTabWidth < this.preferredTabWidth){
11029                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11030             }
11031         }
11032     },
11033
11034     /**
11035      * Returns the number of tabs in this TabPanel.
11036      * @return {Number}
11037      */
11038      getCount : function(){
11039          return this.items.length;
11040      },
11041
11042     /**
11043      * Resizes all the tabs to the passed width
11044      * @param {Number} The new width
11045      */
11046     setTabWidth : function(width){
11047         this.currentTabWidth = width;
11048         for(var i = 0, len = this.items.length; i < len; i++) {
11049                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11050         }
11051     },
11052
11053     /**
11054      * Destroys this TabPanel
11055      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11056      */
11057     destroy : function(removeEl){
11058         Roo.EventManager.removeResizeListener(this.onResize, this);
11059         for(var i = 0, len = this.items.length; i < len; i++){
11060             this.items[i].purgeListeners();
11061         }
11062         if(removeEl === true){
11063             this.el.update("");
11064             this.el.remove();
11065         }
11066     }
11067 });
11068
11069 /**
11070  * @class Roo.TabPanelItem
11071  * @extends Roo.util.Observable
11072  * Represents an individual item (tab plus body) in a TabPanel.
11073  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11074  * @param {String} id The id of this TabPanelItem
11075  * @param {String} text The text for the tab of this TabPanelItem
11076  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11077  */
11078 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11079     /**
11080      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11081      * @type Roo.TabPanel
11082      */
11083     this.tabPanel = tabPanel;
11084     /**
11085      * The id for this TabPanelItem
11086      * @type String
11087      */
11088     this.id = id;
11089     /** @private */
11090     this.disabled = false;
11091     /** @private */
11092     this.text = text;
11093     /** @private */
11094     this.loaded = false;
11095     this.closable = closable;
11096
11097     /**
11098      * The body element for this TabPanelItem.
11099      * @type Roo.Element
11100      */
11101     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11102     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11103     this.bodyEl.setStyle("display", "block");
11104     this.bodyEl.setStyle("zoom", "1");
11105     this.hideAction();
11106
11107     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11108     /** @private */
11109     this.el = Roo.get(els.el, true);
11110     this.inner = Roo.get(els.inner, true);
11111     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11112     this.pnode = Roo.get(els.el.parentNode, true);
11113     this.el.on("mousedown", this.onTabMouseDown, this);
11114     this.el.on("click", this.onTabClick, this);
11115     /** @private */
11116     if(closable){
11117         var c = Roo.get(els.close, true);
11118         c.dom.title = this.closeText;
11119         c.addClassOnOver("close-over");
11120         c.on("click", this.closeClick, this);
11121      }
11122
11123     this.addEvents({
11124          /**
11125          * @event activate
11126          * Fires when this tab becomes the active tab.
11127          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11128          * @param {Roo.TabPanelItem} this
11129          */
11130         "activate": true,
11131         /**
11132          * @event beforeclose
11133          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11134          * @param {Roo.TabPanelItem} this
11135          * @param {Object} e Set cancel to true on this object to cancel the close.
11136          */
11137         "beforeclose": true,
11138         /**
11139          * @event close
11140          * Fires when this tab is closed.
11141          * @param {Roo.TabPanelItem} this
11142          */
11143          "close": true,
11144         /**
11145          * @event deactivate
11146          * Fires when this tab is no longer the active tab.
11147          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11148          * @param {Roo.TabPanelItem} this
11149          */
11150          "deactivate" : true
11151     });
11152     this.hidden = false;
11153
11154     Roo.TabPanelItem.superclass.constructor.call(this);
11155 };
11156
11157 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11158     purgeListeners : function(){
11159        Roo.util.Observable.prototype.purgeListeners.call(this);
11160        this.el.removeAllListeners();
11161     },
11162     /**
11163      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11164      */
11165     show : function(){
11166         this.pnode.addClass("on");
11167         this.showAction();
11168         if(Roo.isOpera){
11169             this.tabPanel.stripWrap.repaint();
11170         }
11171         this.fireEvent("activate", this.tabPanel, this);
11172     },
11173
11174     /**
11175      * Returns true if this tab is the active tab.
11176      * @return {Boolean}
11177      */
11178     isActive : function(){
11179         return this.tabPanel.getActiveTab() == this;
11180     },
11181
11182     /**
11183      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11184      */
11185     hide : function(){
11186         this.pnode.removeClass("on");
11187         this.hideAction();
11188         this.fireEvent("deactivate", this.tabPanel, this);
11189     },
11190
11191     hideAction : function(){
11192         this.bodyEl.hide();
11193         this.bodyEl.setStyle("position", "absolute");
11194         this.bodyEl.setLeft("-20000px");
11195         this.bodyEl.setTop("-20000px");
11196     },
11197
11198     showAction : function(){
11199         this.bodyEl.setStyle("position", "relative");
11200         this.bodyEl.setTop("");
11201         this.bodyEl.setLeft("");
11202         this.bodyEl.show();
11203     },
11204
11205     /**
11206      * Set the tooltip for the tab.
11207      * @param {String} tooltip The tab's tooltip
11208      */
11209     setTooltip : function(text){
11210         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11211             this.textEl.dom.qtip = text;
11212             this.textEl.dom.removeAttribute('title');
11213         }else{
11214             this.textEl.dom.title = text;
11215         }
11216     },
11217
11218     onTabClick : function(e){
11219         e.preventDefault();
11220         this.tabPanel.activate(this.id);
11221     },
11222
11223     onTabMouseDown : function(e){
11224         e.preventDefault();
11225         this.tabPanel.activate(this.id);
11226     },
11227
11228     getWidth : function(){
11229         return this.inner.getWidth();
11230     },
11231
11232     setWidth : function(width){
11233         var iwidth = width - this.pnode.getPadding("lr");
11234         this.inner.setWidth(iwidth);
11235         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11236         this.pnode.setWidth(width);
11237     },
11238
11239     /**
11240      * Show or hide the tab
11241      * @param {Boolean} hidden True to hide or false to show.
11242      */
11243     setHidden : function(hidden){
11244         this.hidden = hidden;
11245         this.pnode.setStyle("display", hidden ? "none" : "");
11246     },
11247
11248     /**
11249      * Returns true if this tab is "hidden"
11250      * @return {Boolean}
11251      */
11252     isHidden : function(){
11253         return this.hidden;
11254     },
11255
11256     /**
11257      * Returns the text for this tab
11258      * @return {String}
11259      */
11260     getText : function(){
11261         return this.text;
11262     },
11263
11264     autoSize : function(){
11265         //this.el.beginMeasure();
11266         this.textEl.setWidth(1);
11267         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11268         //this.el.endMeasure();
11269     },
11270
11271     /**
11272      * Sets the text for the tab (Note: this also sets the tooltip text)
11273      * @param {String} text The tab's text and tooltip
11274      */
11275     setText : function(text){
11276         this.text = text;
11277         this.textEl.update(text);
11278         this.setTooltip(text);
11279         if(!this.tabPanel.resizeTabs){
11280             this.autoSize();
11281         }
11282     },
11283     /**
11284      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11285      */
11286     activate : function(){
11287         this.tabPanel.activate(this.id);
11288     },
11289
11290     /**
11291      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11292      */
11293     disable : function(){
11294         if(this.tabPanel.active != this){
11295             this.disabled = true;
11296             this.pnode.addClass("disabled");
11297         }
11298     },
11299
11300     /**
11301      * Enables this TabPanelItem if it was previously disabled.
11302      */
11303     enable : function(){
11304         this.disabled = false;
11305         this.pnode.removeClass("disabled");
11306     },
11307
11308     /**
11309      * Sets the content for this TabPanelItem.
11310      * @param {String} content The content
11311      * @param {Boolean} loadScripts true to look for and load scripts
11312      */
11313     setContent : function(content, loadScripts){
11314         this.bodyEl.update(content, loadScripts);
11315     },
11316
11317     /**
11318      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11319      * @return {Roo.UpdateManager} The UpdateManager
11320      */
11321     getUpdateManager : function(){
11322         return this.bodyEl.getUpdateManager();
11323     },
11324
11325     /**
11326      * Set a URL to be used to load the content for this TabPanelItem.
11327      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11328      * @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)
11329      * @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)
11330      * @return {Roo.UpdateManager} The UpdateManager
11331      */
11332     setUrl : function(url, params, loadOnce){
11333         if(this.refreshDelegate){
11334             this.un('activate', this.refreshDelegate);
11335         }
11336         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11337         this.on("activate", this.refreshDelegate);
11338         return this.bodyEl.getUpdateManager();
11339     },
11340
11341     /** @private */
11342     _handleRefresh : function(url, params, loadOnce){
11343         if(!loadOnce || !this.loaded){
11344             var updater = this.bodyEl.getUpdateManager();
11345             updater.update(url, params, this._setLoaded.createDelegate(this));
11346         }
11347     },
11348
11349     /**
11350      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11351      *   Will fail silently if the setUrl method has not been called.
11352      *   This does not activate the panel, just updates its content.
11353      */
11354     refresh : function(){
11355         if(this.refreshDelegate){
11356            this.loaded = false;
11357            this.refreshDelegate();
11358         }
11359     },
11360
11361     /** @private */
11362     _setLoaded : function(){
11363         this.loaded = true;
11364     },
11365
11366     /** @private */
11367     closeClick : function(e){
11368         var o = {};
11369         e.stopEvent();
11370         this.fireEvent("beforeclose", this, o);
11371         if(o.cancel !== true){
11372             this.tabPanel.removeTab(this.id);
11373         }
11374     },
11375     /**
11376      * The text displayed in the tooltip for the close icon.
11377      * @type String
11378      */
11379     closeText : "Close this tab"
11380 });
11381
11382 /** @private */
11383 Roo.TabPanel.prototype.createStrip = function(container){
11384     var strip = document.createElement("div");
11385     strip.className = "x-tabs-wrap";
11386     container.appendChild(strip);
11387     return strip;
11388 };
11389 /** @private */
11390 Roo.TabPanel.prototype.createStripList = function(strip){
11391     // div wrapper for retard IE
11392     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>';
11393     return strip.firstChild.firstChild.firstChild.firstChild;
11394 };
11395 /** @private */
11396 Roo.TabPanel.prototype.createBody = function(container){
11397     var body = document.createElement("div");
11398     Roo.id(body, "tab-body");
11399     Roo.fly(body).addClass("x-tabs-body");
11400     container.appendChild(body);
11401     return body;
11402 };
11403 /** @private */
11404 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11405     var body = Roo.getDom(id);
11406     if(!body){
11407         body = document.createElement("div");
11408         body.id = id;
11409     }
11410     Roo.fly(body).addClass("x-tabs-item-body");
11411     bodyEl.insertBefore(body, bodyEl.firstChild);
11412     return body;
11413 };
11414 /** @private */
11415 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11416     var td = document.createElement("td");
11417     stripEl.appendChild(td);
11418     if(closable){
11419         td.className = "x-tabs-closable";
11420         if(!this.closeTpl){
11421             this.closeTpl = new Roo.Template(
11422                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11423                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11424                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11425             );
11426         }
11427         var el = this.closeTpl.overwrite(td, {"text": text});
11428         var close = el.getElementsByTagName("div")[0];
11429         var inner = el.getElementsByTagName("em")[0];
11430         return {"el": el, "close": close, "inner": inner};
11431     } else {
11432         if(!this.tabTpl){
11433             this.tabTpl = new Roo.Template(
11434                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11435                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11436             );
11437         }
11438         var el = this.tabTpl.overwrite(td, {"text": text});
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "inner": inner};
11441     }
11442 };/*
11443  * Based on:
11444  * Ext JS Library 1.1.1
11445  * Copyright(c) 2006-2007, Ext JS, LLC.
11446  *
11447  * Originally Released Under LGPL - original licence link has changed is not relivant.
11448  *
11449  * Fork - LGPL
11450  * <script type="text/javascript">
11451  */
11452
11453 /**
11454  * @class Roo.Button
11455  * @extends Roo.util.Observable
11456  * Simple Button class
11457  * @cfg {String} text The button text
11458  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11459  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11460  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11461  * @cfg {Object} scope The scope of the handler
11462  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11463  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11464  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11465  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11466  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11467  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11468    applies if enableToggle = true)
11469  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11470  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11471   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11472  * @constructor
11473  * Create a new button
11474  * @param {Object} config The config object
11475  */
11476 Roo.Button = function(renderTo, config)
11477 {
11478     if (!config) {
11479         config = renderTo;
11480         renderTo = config.renderTo || false;
11481     }
11482     
11483     Roo.apply(this, config);
11484     this.addEvents({
11485         /**
11486              * @event click
11487              * Fires when this button is clicked
11488              * @param {Button} this
11489              * @param {EventObject} e The click event
11490              */
11491             "click" : true,
11492         /**
11493              * @event toggle
11494              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11495              * @param {Button} this
11496              * @param {Boolean} pressed
11497              */
11498             "toggle" : true,
11499         /**
11500              * @event mouseover
11501              * Fires when the mouse hovers over the button
11502              * @param {Button} this
11503              * @param {Event} e The event object
11504              */
11505         'mouseover' : true,
11506         /**
11507              * @event mouseout
11508              * Fires when the mouse exits the button
11509              * @param {Button} this
11510              * @param {Event} e The event object
11511              */
11512         'mouseout': true,
11513          /**
11514              * @event render
11515              * Fires when the button is rendered
11516              * @param {Button} this
11517              */
11518         'render': true
11519     });
11520     if(this.menu){
11521         this.menu = Roo.menu.MenuMgr.get(this.menu);
11522     }
11523     // register listeners first!!  - so render can be captured..
11524     Roo.util.Observable.call(this);
11525     if(renderTo){
11526         this.render(renderTo);
11527     }
11528     
11529   
11530 };
11531
11532 Roo.extend(Roo.Button, Roo.util.Observable, {
11533     /**
11534      * 
11535      */
11536     
11537     /**
11538      * Read-only. True if this button is hidden
11539      * @type Boolean
11540      */
11541     hidden : false,
11542     /**
11543      * Read-only. True if this button is disabled
11544      * @type Boolean
11545      */
11546     disabled : false,
11547     /**
11548      * Read-only. True if this button is pressed (only if enableToggle = true)
11549      * @type Boolean
11550      */
11551     pressed : false,
11552
11553     /**
11554      * @cfg {Number} tabIndex 
11555      * The DOM tabIndex for this button (defaults to undefined)
11556      */
11557     tabIndex : undefined,
11558
11559     /**
11560      * @cfg {Boolean} enableToggle
11561      * True to enable pressed/not pressed toggling (defaults to false)
11562      */
11563     enableToggle: false,
11564     /**
11565      * @cfg {Mixed} menu
11566      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11567      */
11568     menu : undefined,
11569     /**
11570      * @cfg {String} menuAlign
11571      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11572      */
11573     menuAlign : "tl-bl?",
11574
11575     /**
11576      * @cfg {String} iconCls
11577      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11578      */
11579     iconCls : undefined,
11580     /**
11581      * @cfg {String} type
11582      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11583      */
11584     type : 'button',
11585
11586     // private
11587     menuClassTarget: 'tr',
11588
11589     /**
11590      * @cfg {String} clickEvent
11591      * The type of event to map to the button's event handler (defaults to 'click')
11592      */
11593     clickEvent : 'click',
11594
11595     /**
11596      * @cfg {Boolean} handleMouseEvents
11597      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11598      */
11599     handleMouseEvents : true,
11600
11601     /**
11602      * @cfg {String} tooltipType
11603      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11604      */
11605     tooltipType : 'qtip',
11606
11607     /**
11608      * @cfg {String} cls
11609      * A CSS class to apply to the button's main element.
11610      */
11611     
11612     /**
11613      * @cfg {Roo.Template} template (Optional)
11614      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11615      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11616      * require code modifications if required elements (e.g. a button) aren't present.
11617      */
11618
11619     // private
11620     render : function(renderTo){
11621         var btn;
11622         if(this.hideParent){
11623             this.parentEl = Roo.get(renderTo);
11624         }
11625         if(!this.dhconfig){
11626             if(!this.template){
11627                 if(!Roo.Button.buttonTemplate){
11628                     // hideous table template
11629                     Roo.Button.buttonTemplate = new Roo.Template(
11630                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11631                         '<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>',
11632                         "</tr></tbody></table>");
11633                 }
11634                 this.template = Roo.Button.buttonTemplate;
11635             }
11636             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11637             var btnEl = btn.child("button:first");
11638             btnEl.on('focus', this.onFocus, this);
11639             btnEl.on('blur', this.onBlur, this);
11640             if(this.cls){
11641                 btn.addClass(this.cls);
11642             }
11643             if(this.icon){
11644                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11645             }
11646             if(this.iconCls){
11647                 btnEl.addClass(this.iconCls);
11648                 if(!this.cls){
11649                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11650                 }
11651             }
11652             if(this.tabIndex !== undefined){
11653                 btnEl.dom.tabIndex = this.tabIndex;
11654             }
11655             if(this.tooltip){
11656                 if(typeof this.tooltip == 'object'){
11657                     Roo.QuickTips.tips(Roo.apply({
11658                           target: btnEl.id
11659                     }, this.tooltip));
11660                 } else {
11661                     btnEl.dom[this.tooltipType] = this.tooltip;
11662                 }
11663             }
11664         }else{
11665             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11666         }
11667         this.el = btn;
11668         if(this.id){
11669             this.el.dom.id = this.el.id = this.id;
11670         }
11671         if(this.menu){
11672             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11673             this.menu.on("show", this.onMenuShow, this);
11674             this.menu.on("hide", this.onMenuHide, this);
11675         }
11676         btn.addClass("x-btn");
11677         if(Roo.isIE && !Roo.isIE7){
11678             this.autoWidth.defer(1, this);
11679         }else{
11680             this.autoWidth();
11681         }
11682         if(this.handleMouseEvents){
11683             btn.on("mouseover", this.onMouseOver, this);
11684             btn.on("mouseout", this.onMouseOut, this);
11685             btn.on("mousedown", this.onMouseDown, this);
11686         }
11687         btn.on(this.clickEvent, this.onClick, this);
11688         //btn.on("mouseup", this.onMouseUp, this);
11689         if(this.hidden){
11690             this.hide();
11691         }
11692         if(this.disabled){
11693             this.disable();
11694         }
11695         Roo.ButtonToggleMgr.register(this);
11696         if(this.pressed){
11697             this.el.addClass("x-btn-pressed");
11698         }
11699         if(this.repeat){
11700             var repeater = new Roo.util.ClickRepeater(btn,
11701                 typeof this.repeat == "object" ? this.repeat : {}
11702             );
11703             repeater.on("click", this.onClick,  this);
11704         }
11705         
11706         this.fireEvent('render', this);
11707         
11708     },
11709     /**
11710      * Returns the button's underlying element
11711      * @return {Roo.Element} The element
11712      */
11713     getEl : function(){
11714         return this.el;  
11715     },
11716     
11717     /**
11718      * Destroys this Button and removes any listeners.
11719      */
11720     destroy : function(){
11721         Roo.ButtonToggleMgr.unregister(this);
11722         this.el.removeAllListeners();
11723         this.purgeListeners();
11724         this.el.remove();
11725     },
11726
11727     // private
11728     autoWidth : function(){
11729         if(this.el){
11730             this.el.setWidth("auto");
11731             if(Roo.isIE7 && Roo.isStrict){
11732                 var ib = this.el.child('button');
11733                 if(ib && ib.getWidth() > 20){
11734                     ib.clip();
11735                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11736                 }
11737             }
11738             if(this.minWidth){
11739                 if(this.hidden){
11740                     this.el.beginMeasure();
11741                 }
11742                 if(this.el.getWidth() < this.minWidth){
11743                     this.el.setWidth(this.minWidth);
11744                 }
11745                 if(this.hidden){
11746                     this.el.endMeasure();
11747                 }
11748             }
11749         }
11750     },
11751
11752     /**
11753      * Assigns this button's click handler
11754      * @param {Function} handler The function to call when the button is clicked
11755      * @param {Object} scope (optional) Scope for the function passed in
11756      */
11757     setHandler : function(handler, scope){
11758         this.handler = handler;
11759         this.scope = scope;  
11760     },
11761     
11762     /**
11763      * Sets this button's text
11764      * @param {String} text The button text
11765      */
11766     setText : function(text){
11767         this.text = text;
11768         if(this.el){
11769             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11770         }
11771         this.autoWidth();
11772     },
11773     
11774     /**
11775      * Gets the text for this button
11776      * @return {String} The button text
11777      */
11778     getText : function(){
11779         return this.text;  
11780     },
11781     
11782     /**
11783      * Show this button
11784      */
11785     show: function(){
11786         this.hidden = false;
11787         if(this.el){
11788             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11789         }
11790     },
11791     
11792     /**
11793      * Hide this button
11794      */
11795     hide: function(){
11796         this.hidden = true;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11799         }
11800     },
11801     
11802     /**
11803      * Convenience function for boolean show/hide
11804      * @param {Boolean} visible True to show, false to hide
11805      */
11806     setVisible: function(visible){
11807         if(visible) {
11808             this.show();
11809         }else{
11810             this.hide();
11811         }
11812     },
11813     
11814     /**
11815      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11816      * @param {Boolean} state (optional) Force a particular state
11817      */
11818     toggle : function(state){
11819         state = state === undefined ? !this.pressed : state;
11820         if(state != this.pressed){
11821             if(state){
11822                 this.el.addClass("x-btn-pressed");
11823                 this.pressed = true;
11824                 this.fireEvent("toggle", this, true);
11825             }else{
11826                 this.el.removeClass("x-btn-pressed");
11827                 this.pressed = false;
11828                 this.fireEvent("toggle", this, false);
11829             }
11830             if(this.toggleHandler){
11831                 this.toggleHandler.call(this.scope || this, this, state);
11832             }
11833         }
11834     },
11835     
11836     /**
11837      * Focus the button
11838      */
11839     focus : function(){
11840         this.el.child('button:first').focus();
11841     },
11842     
11843     /**
11844      * Disable this button
11845      */
11846     disable : function(){
11847         if(this.el){
11848             this.el.addClass("x-btn-disabled");
11849         }
11850         this.disabled = true;
11851     },
11852     
11853     /**
11854      * Enable this button
11855      */
11856     enable : function(){
11857         if(this.el){
11858             this.el.removeClass("x-btn-disabled");
11859         }
11860         this.disabled = false;
11861     },
11862
11863     /**
11864      * Convenience function for boolean enable/disable
11865      * @param {Boolean} enabled True to enable, false to disable
11866      */
11867     setDisabled : function(v){
11868         this[v !== true ? "enable" : "disable"]();
11869     },
11870
11871     // private
11872     onClick : function(e){
11873         if(e){
11874             e.preventDefault();
11875         }
11876         if(e.button != 0){
11877             return;
11878         }
11879         if(!this.disabled){
11880             if(this.enableToggle){
11881                 this.toggle();
11882             }
11883             if(this.menu && !this.menu.isVisible()){
11884                 this.menu.show(this.el, this.menuAlign);
11885             }
11886             this.fireEvent("click", this, e);
11887             if(this.handler){
11888                 this.el.removeClass("x-btn-over");
11889                 this.handler.call(this.scope || this, this, e);
11890             }
11891         }
11892     },
11893     // private
11894     onMouseOver : function(e){
11895         if(!this.disabled){
11896             this.el.addClass("x-btn-over");
11897             this.fireEvent('mouseover', this, e);
11898         }
11899     },
11900     // private
11901     onMouseOut : function(e){
11902         if(!e.within(this.el,  true)){
11903             this.el.removeClass("x-btn-over");
11904             this.fireEvent('mouseout', this, e);
11905         }
11906     },
11907     // private
11908     onFocus : function(e){
11909         if(!this.disabled){
11910             this.el.addClass("x-btn-focus");
11911         }
11912     },
11913     // private
11914     onBlur : function(e){
11915         this.el.removeClass("x-btn-focus");
11916     },
11917     // private
11918     onMouseDown : function(e){
11919         if(!this.disabled && e.button == 0){
11920             this.el.addClass("x-btn-click");
11921             Roo.get(document).on('mouseup', this.onMouseUp, this);
11922         }
11923     },
11924     // private
11925     onMouseUp : function(e){
11926         if(e.button == 0){
11927             this.el.removeClass("x-btn-click");
11928             Roo.get(document).un('mouseup', this.onMouseUp, this);
11929         }
11930     },
11931     // private
11932     onMenuShow : function(e){
11933         this.el.addClass("x-btn-menu-active");
11934     },
11935     // private
11936     onMenuHide : function(e){
11937         this.el.removeClass("x-btn-menu-active");
11938     }   
11939 });
11940
11941 // Private utility class used by Button
11942 Roo.ButtonToggleMgr = function(){
11943    var groups = {};
11944    
11945    function toggleGroup(btn, state){
11946        if(state){
11947            var g = groups[btn.toggleGroup];
11948            for(var i = 0, l = g.length; i < l; i++){
11949                if(g[i] != btn){
11950                    g[i].toggle(false);
11951                }
11952            }
11953        }
11954    }
11955    
11956    return {
11957        register : function(btn){
11958            if(!btn.toggleGroup){
11959                return;
11960            }
11961            var g = groups[btn.toggleGroup];
11962            if(!g){
11963                g = groups[btn.toggleGroup] = [];
11964            }
11965            g.push(btn);
11966            btn.on("toggle", toggleGroup);
11967        },
11968        
11969        unregister : function(btn){
11970            if(!btn.toggleGroup){
11971                return;
11972            }
11973            var g = groups[btn.toggleGroup];
11974            if(g){
11975                g.remove(btn);
11976                btn.un("toggle", toggleGroup);
11977            }
11978        }
11979    };
11980 }();/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990  
11991 /**
11992  * @class Roo.SplitButton
11993  * @extends Roo.Button
11994  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11995  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11996  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11997  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11998  * @cfg {String} arrowTooltip The title attribute of the arrow
11999  * @constructor
12000  * Create a new menu button
12001  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12002  * @param {Object} config The config object
12003  */
12004 Roo.SplitButton = function(renderTo, config){
12005     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12006     /**
12007      * @event arrowclick
12008      * Fires when this button's arrow is clicked
12009      * @param {SplitButton} this
12010      * @param {EventObject} e The click event
12011      */
12012     this.addEvents({"arrowclick":true});
12013 };
12014
12015 Roo.extend(Roo.SplitButton, Roo.Button, {
12016     render : function(renderTo){
12017         // this is one sweet looking template!
12018         var tpl = new Roo.Template(
12019             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12020             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12021             '<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>',
12022             "</tbody></table></td><td>",
12023             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12024             '<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>',
12025             "</tbody></table></td></tr></table>"
12026         );
12027         var btn = tpl.append(renderTo, [this.text, this.type], true);
12028         var btnEl = btn.child("button");
12029         if(this.cls){
12030             btn.addClass(this.cls);
12031         }
12032         if(this.icon){
12033             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12034         }
12035         if(this.iconCls){
12036             btnEl.addClass(this.iconCls);
12037             if(!this.cls){
12038                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12039             }
12040         }
12041         this.el = btn;
12042         if(this.handleMouseEvents){
12043             btn.on("mouseover", this.onMouseOver, this);
12044             btn.on("mouseout", this.onMouseOut, this);
12045             btn.on("mousedown", this.onMouseDown, this);
12046             btn.on("mouseup", this.onMouseUp, this);
12047         }
12048         btn.on(this.clickEvent, this.onClick, this);
12049         if(this.tooltip){
12050             if(typeof this.tooltip == 'object'){
12051                 Roo.QuickTips.tips(Roo.apply({
12052                       target: btnEl.id
12053                 }, this.tooltip));
12054             } else {
12055                 btnEl.dom[this.tooltipType] = this.tooltip;
12056             }
12057         }
12058         if(this.arrowTooltip){
12059             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12060         }
12061         if(this.hidden){
12062             this.hide();
12063         }
12064         if(this.disabled){
12065             this.disable();
12066         }
12067         if(this.pressed){
12068             this.el.addClass("x-btn-pressed");
12069         }
12070         if(Roo.isIE && !Roo.isIE7){
12071             this.autoWidth.defer(1, this);
12072         }else{
12073             this.autoWidth();
12074         }
12075         if(this.menu){
12076             this.menu.on("show", this.onMenuShow, this);
12077             this.menu.on("hide", this.onMenuHide, this);
12078         }
12079         this.fireEvent('render', this);
12080     },
12081
12082     // private
12083     autoWidth : function(){
12084         if(this.el){
12085             var tbl = this.el.child("table:first");
12086             var tbl2 = this.el.child("table:last");
12087             this.el.setWidth("auto");
12088             tbl.setWidth("auto");
12089             if(Roo.isIE7 && Roo.isStrict){
12090                 var ib = this.el.child('button:first');
12091                 if(ib && ib.getWidth() > 20){
12092                     ib.clip();
12093                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12094                 }
12095             }
12096             if(this.minWidth){
12097                 if(this.hidden){
12098                     this.el.beginMeasure();
12099                 }
12100                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12101                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12102                 }
12103                 if(this.hidden){
12104                     this.el.endMeasure();
12105                 }
12106             }
12107             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12108         } 
12109     },
12110     /**
12111      * Sets this button's click handler
12112      * @param {Function} handler The function to call when the button is clicked
12113      * @param {Object} scope (optional) Scope for the function passed above
12114      */
12115     setHandler : function(handler, scope){
12116         this.handler = handler;
12117         this.scope = scope;  
12118     },
12119     
12120     /**
12121      * Sets this button's arrow click handler
12122      * @param {Function} handler The function to call when the arrow is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setArrowHandler : function(handler, scope){
12126         this.arrowHandler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Focus the button
12132      */
12133     focus : function(){
12134         if(this.el){
12135             this.el.child("button:first").focus();
12136         }
12137     },
12138
12139     // private
12140     onClick : function(e){
12141         e.preventDefault();
12142         if(!this.disabled){
12143             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12144                 if(this.menu && !this.menu.isVisible()){
12145                     this.menu.show(this.el, this.menuAlign);
12146                 }
12147                 this.fireEvent("arrowclick", this, e);
12148                 if(this.arrowHandler){
12149                     this.arrowHandler.call(this.scope || this, this, e);
12150                 }
12151             }else{
12152                 this.fireEvent("click", this, e);
12153                 if(this.handler){
12154                     this.handler.call(this.scope || this, this, e);
12155                 }
12156             }
12157         }
12158     },
12159     // private
12160     onMouseDown : function(e){
12161         if(!this.disabled){
12162             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12163         }
12164     },
12165     // private
12166     onMouseUp : function(e){
12167         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12168     }   
12169 });
12170
12171
12172 // backwards compat
12173 Roo.MenuButton = Roo.SplitButton;/*
12174  * Based on:
12175  * Ext JS Library 1.1.1
12176  * Copyright(c) 2006-2007, Ext JS, LLC.
12177  *
12178  * Originally Released Under LGPL - original licence link has changed is not relivant.
12179  *
12180  * Fork - LGPL
12181  * <script type="text/javascript">
12182  */
12183
12184 /**
12185  * @class Roo.Toolbar
12186  * Basic Toolbar class.
12187  * @constructor
12188  * Creates a new Toolbar
12189  * @param {Object} config The config object
12190  */ 
12191 Roo.Toolbar = function(container, buttons, config)
12192 {
12193     /// old consturctor format still supported..
12194     if(container instanceof Array){ // omit the container for later rendering
12195         buttons = container;
12196         config = buttons;
12197         container = null;
12198     }
12199     if (typeof(container) == 'object' && container.xtype) {
12200         config = container;
12201         container = config.container;
12202         buttons = config.buttons; // not really - use items!!
12203     }
12204     var xitems = [];
12205     if (config && config.items) {
12206         xitems = config.items;
12207         delete config.items;
12208     }
12209     Roo.apply(this, config);
12210     this.buttons = buttons;
12211     
12212     if(container){
12213         this.render(container);
12214     }
12215     Roo.each(xitems, function(b) {
12216         this.add(b);
12217     }, this);
12218     
12219 };
12220
12221 Roo.Toolbar.prototype = {
12222     /**
12223      * @cfg {Roo.data.Store} items
12224      * array of button configs or elements to add
12225      */
12226     
12227     /**
12228      * @cfg {String/HTMLElement/Element} container
12229      * The id or element that will contain the toolbar
12230      */
12231     // private
12232     render : function(ct){
12233         this.el = Roo.get(ct);
12234         if(this.cls){
12235             this.el.addClass(this.cls);
12236         }
12237         // using a table allows for vertical alignment
12238         // 100% width is needed by Safari...
12239         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12240         this.tr = this.el.child("tr", true);
12241         var autoId = 0;
12242         this.items = new Roo.util.MixedCollection(false, function(o){
12243             return o.id || ("item" + (++autoId));
12244         });
12245         if(this.buttons){
12246             this.add.apply(this, this.buttons);
12247             delete this.buttons;
12248         }
12249     },
12250
12251     /**
12252      * Adds element(s) to the toolbar -- this function takes a variable number of 
12253      * arguments of mixed type and adds them to the toolbar.
12254      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12255      * <ul>
12256      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12257      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12258      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12259      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12260      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12261      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12262      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12263      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12264      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12265      * </ul>
12266      * @param {Mixed} arg2
12267      * @param {Mixed} etc.
12268      */
12269     add : function(){
12270         var a = arguments, l = a.length;
12271         for(var i = 0; i < l; i++){
12272             this._add(a[i]);
12273         }
12274     },
12275     // private..
12276     _add : function(el) {
12277         
12278         if (el.xtype) {
12279             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12280         }
12281         
12282         if (el.applyTo){ // some kind of form field
12283             return this.addField(el);
12284         } 
12285         if (el.render){ // some kind of Toolbar.Item
12286             return this.addItem(el);
12287         }
12288         if (typeof el == "string"){ // string
12289             if(el == "separator" || el == "-"){
12290                 return this.addSeparator();
12291             }
12292             if (el == " "){
12293                 return this.addSpacer();
12294             }
12295             if(el == "->"){
12296                 return this.addFill();
12297             }
12298             return this.addText(el);
12299             
12300         }
12301         if(el.tagName){ // element
12302             return this.addElement(el);
12303         }
12304         if(typeof el == "object"){ // must be button config?
12305             return this.addButton(el);
12306         }
12307         // and now what?!?!
12308         return false;
12309         
12310     },
12311     
12312     /**
12313      * Add an Xtype element
12314      * @param {Object} xtype Xtype Object
12315      * @return {Object} created Object
12316      */
12317     addxtype : function(e){
12318         return this.add(e);  
12319     },
12320     
12321     /**
12322      * Returns the Element for this toolbar.
12323      * @return {Roo.Element}
12324      */
12325     getEl : function(){
12326         return this.el;  
12327     },
12328     
12329     /**
12330      * Adds a separator
12331      * @return {Roo.Toolbar.Item} The separator item
12332      */
12333     addSeparator : function(){
12334         return this.addItem(new Roo.Toolbar.Separator());
12335     },
12336
12337     /**
12338      * Adds a spacer element
12339      * @return {Roo.Toolbar.Spacer} The spacer item
12340      */
12341     addSpacer : function(){
12342         return this.addItem(new Roo.Toolbar.Spacer());
12343     },
12344
12345     /**
12346      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12347      * @return {Roo.Toolbar.Fill} The fill item
12348      */
12349     addFill : function(){
12350         return this.addItem(new Roo.Toolbar.Fill());
12351     },
12352
12353     /**
12354      * Adds any standard HTML element to the toolbar
12355      * @param {String/HTMLElement/Element} el The element or id of the element to add
12356      * @return {Roo.Toolbar.Item} The element's item
12357      */
12358     addElement : function(el){
12359         return this.addItem(new Roo.Toolbar.Item(el));
12360     },
12361     /**
12362      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12363      * @type Roo.util.MixedCollection  
12364      */
12365     items : false,
12366      
12367     /**
12368      * Adds any Toolbar.Item or subclass
12369      * @param {Roo.Toolbar.Item} item
12370      * @return {Roo.Toolbar.Item} The item
12371      */
12372     addItem : function(item){
12373         var td = this.nextBlock();
12374         item.render(td);
12375         this.items.add(item);
12376         return item;
12377     },
12378     
12379     /**
12380      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12381      * @param {Object/Array} config A button config or array of configs
12382      * @return {Roo.Toolbar.Button/Array}
12383      */
12384     addButton : function(config){
12385         if(config instanceof Array){
12386             var buttons = [];
12387             for(var i = 0, len = config.length; i < len; i++) {
12388                 buttons.push(this.addButton(config[i]));
12389             }
12390             return buttons;
12391         }
12392         var b = config;
12393         if(!(config instanceof Roo.Toolbar.Button)){
12394             b = config.split ?
12395                 new Roo.Toolbar.SplitButton(config) :
12396                 new Roo.Toolbar.Button(config);
12397         }
12398         var td = this.nextBlock();
12399         b.render(td);
12400         this.items.add(b);
12401         return b;
12402     },
12403     
12404     /**
12405      * Adds text to the toolbar
12406      * @param {String} text The text to add
12407      * @return {Roo.Toolbar.Item} The element's item
12408      */
12409     addText : function(text){
12410         return this.addItem(new Roo.Toolbar.TextItem(text));
12411     },
12412     
12413     /**
12414      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12415      * @param {Number} index The index where the item is to be inserted
12416      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12417      * @return {Roo.Toolbar.Button/Item}
12418      */
12419     insertButton : function(index, item){
12420         if(item instanceof Array){
12421             var buttons = [];
12422             for(var i = 0, len = item.length; i < len; i++) {
12423                buttons.push(this.insertButton(index + i, item[i]));
12424             }
12425             return buttons;
12426         }
12427         if (!(item instanceof Roo.Toolbar.Button)){
12428            item = new Roo.Toolbar.Button(item);
12429         }
12430         var td = document.createElement("td");
12431         this.tr.insertBefore(td, this.tr.childNodes[index]);
12432         item.render(td);
12433         this.items.insert(index, item);
12434         return item;
12435     },
12436     
12437     /**
12438      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12439      * @param {Object} config
12440      * @return {Roo.Toolbar.Item} The element's item
12441      */
12442     addDom : function(config, returnEl){
12443         var td = this.nextBlock();
12444         Roo.DomHelper.overwrite(td, config);
12445         var ti = new Roo.Toolbar.Item(td.firstChild);
12446         ti.render(td);
12447         this.items.add(ti);
12448         return ti;
12449     },
12450
12451     /**
12452      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12453      * @type Roo.util.MixedCollection  
12454      */
12455     fields : false,
12456     
12457     /**
12458      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12459      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12460      * @param {Roo.form.Field} field
12461      * @return {Roo.ToolbarItem}
12462      */
12463      
12464       
12465     addField : function(field) {
12466         if (!this.fields) {
12467             var autoId = 0;
12468             this.fields = new Roo.util.MixedCollection(false, function(o){
12469                 return o.id || ("item" + (++autoId));
12470             });
12471
12472         }
12473         
12474         var td = this.nextBlock();
12475         field.render(td);
12476         var ti = new Roo.Toolbar.Item(td.firstChild);
12477         ti.render(td);
12478         this.items.add(ti);
12479         this.fields.add(field);
12480         return ti;
12481     },
12482     /**
12483      * Hide the toolbar
12484      * @method hide
12485      */
12486      
12487       
12488     hide : function()
12489     {
12490         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12491         this.el.child('div').hide();
12492     },
12493     /**
12494      * Show the toolbar
12495      * @method show
12496      */
12497     show : function()
12498     {
12499         this.el.child('div').show();
12500     },
12501       
12502     // private
12503     nextBlock : function(){
12504         var td = document.createElement("td");
12505         this.tr.appendChild(td);
12506         return td;
12507     },
12508
12509     // private
12510     destroy : function(){
12511         if(this.items){ // rendered?
12512             Roo.destroy.apply(Roo, this.items.items);
12513         }
12514         if(this.fields){ // rendered?
12515             Roo.destroy.apply(Roo, this.fields.items);
12516         }
12517         Roo.Element.uncache(this.el, this.tr);
12518     }
12519 };
12520
12521 /**
12522  * @class Roo.Toolbar.Item
12523  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12524  * @constructor
12525  * Creates a new Item
12526  * @param {HTMLElement} el 
12527  */
12528 Roo.Toolbar.Item = function(el){
12529     this.el = Roo.getDom(el);
12530     this.id = Roo.id(this.el);
12531     this.hidden = false;
12532 };
12533
12534 Roo.Toolbar.Item.prototype = {
12535     
12536     /**
12537      * Get this item's HTML Element
12538      * @return {HTMLElement}
12539      */
12540     getEl : function(){
12541        return this.el;  
12542     },
12543
12544     // private
12545     render : function(td){
12546         this.td = td;
12547         td.appendChild(this.el);
12548     },
12549     
12550     /**
12551      * Removes and destroys this item.
12552      */
12553     destroy : function(){
12554         this.td.parentNode.removeChild(this.td);
12555     },
12556     
12557     /**
12558      * Shows this item.
12559      */
12560     show: function(){
12561         this.hidden = false;
12562         this.td.style.display = "";
12563     },
12564     
12565     /**
12566      * Hides this item.
12567      */
12568     hide: function(){
12569         this.hidden = true;
12570         this.td.style.display = "none";
12571     },
12572     
12573     /**
12574      * Convenience function for boolean show/hide.
12575      * @param {Boolean} visible true to show/false to hide
12576      */
12577     setVisible: function(visible){
12578         if(visible) {
12579             this.show();
12580         }else{
12581             this.hide();
12582         }
12583     },
12584     
12585     /**
12586      * Try to focus this item.
12587      */
12588     focus : function(){
12589         Roo.fly(this.el).focus();
12590     },
12591     
12592     /**
12593      * Disables this item.
12594      */
12595     disable : function(){
12596         Roo.fly(this.td).addClass("x-item-disabled");
12597         this.disabled = true;
12598         this.el.disabled = true;
12599     },
12600     
12601     /**
12602      * Enables this item.
12603      */
12604     enable : function(){
12605         Roo.fly(this.td).removeClass("x-item-disabled");
12606         this.disabled = false;
12607         this.el.disabled = false;
12608     }
12609 };
12610
12611
12612 /**
12613  * @class Roo.Toolbar.Separator
12614  * @extends Roo.Toolbar.Item
12615  * A simple toolbar separator class
12616  * @constructor
12617  * Creates a new Separator
12618  */
12619 Roo.Toolbar.Separator = function(){
12620     var s = document.createElement("span");
12621     s.className = "ytb-sep";
12622     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12623 };
12624 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12625     enable:Roo.emptyFn,
12626     disable:Roo.emptyFn,
12627     focus:Roo.emptyFn
12628 });
12629
12630 /**
12631  * @class Roo.Toolbar.Spacer
12632  * @extends Roo.Toolbar.Item
12633  * A simple element that adds extra horizontal space to a toolbar.
12634  * @constructor
12635  * Creates a new Spacer
12636  */
12637 Roo.Toolbar.Spacer = function(){
12638     var s = document.createElement("div");
12639     s.className = "ytb-spacer";
12640     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12641 };
12642 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12643     enable:Roo.emptyFn,
12644     disable:Roo.emptyFn,
12645     focus:Roo.emptyFn
12646 });
12647
12648 /**
12649  * @class Roo.Toolbar.Fill
12650  * @extends Roo.Toolbar.Spacer
12651  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12652  * @constructor
12653  * Creates a new Spacer
12654  */
12655 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12656     // private
12657     render : function(td){
12658         td.style.width = '100%';
12659         Roo.Toolbar.Fill.superclass.render.call(this, td);
12660     }
12661 });
12662
12663 /**
12664  * @class Roo.Toolbar.TextItem
12665  * @extends Roo.Toolbar.Item
12666  * A simple class that renders text directly into a toolbar.
12667  * @constructor
12668  * Creates a new TextItem
12669  * @param {String} text
12670  */
12671 Roo.Toolbar.TextItem = function(text){
12672     if (typeof(text) == 'object') {
12673         text = text.text;
12674     }
12675     var s = document.createElement("span");
12676     s.className = "ytb-text";
12677     s.innerHTML = text;
12678     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12679 };
12680 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12681     enable:Roo.emptyFn,
12682     disable:Roo.emptyFn,
12683     focus:Roo.emptyFn
12684 });
12685
12686 /**
12687  * @class Roo.Toolbar.Button
12688  * @extends Roo.Button
12689  * A button that renders into a toolbar.
12690  * @constructor
12691  * Creates a new Button
12692  * @param {Object} config A standard {@link Roo.Button} config object
12693  */
12694 Roo.Toolbar.Button = function(config){
12695     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12696 };
12697 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12698     render : function(td){
12699         this.td = td;
12700         Roo.Toolbar.Button.superclass.render.call(this, td);
12701     },
12702     
12703     /**
12704      * Removes and destroys this button
12705      */
12706     destroy : function(){
12707         Roo.Toolbar.Button.superclass.destroy.call(this);
12708         this.td.parentNode.removeChild(this.td);
12709     },
12710     
12711     /**
12712      * Shows this button
12713      */
12714     show: function(){
12715         this.hidden = false;
12716         this.td.style.display = "";
12717     },
12718     
12719     /**
12720      * Hides this button
12721      */
12722     hide: function(){
12723         this.hidden = true;
12724         this.td.style.display = "none";
12725     },
12726
12727     /**
12728      * Disables this item
12729      */
12730     disable : function(){
12731         Roo.fly(this.td).addClass("x-item-disabled");
12732         this.disabled = true;
12733     },
12734
12735     /**
12736      * Enables this item
12737      */
12738     enable : function(){
12739         Roo.fly(this.td).removeClass("x-item-disabled");
12740         this.disabled = false;
12741     }
12742 });
12743 // backwards compat
12744 Roo.ToolbarButton = Roo.Toolbar.Button;
12745
12746 /**
12747  * @class Roo.Toolbar.SplitButton
12748  * @extends Roo.SplitButton
12749  * A menu button that renders into a toolbar.
12750  * @constructor
12751  * Creates a new SplitButton
12752  * @param {Object} config A standard {@link Roo.SplitButton} config object
12753  */
12754 Roo.Toolbar.SplitButton = function(config){
12755     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12756 };
12757 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12758     render : function(td){
12759         this.td = td;
12760         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12761     },
12762     
12763     /**
12764      * Removes and destroys this button
12765      */
12766     destroy : function(){
12767         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12768         this.td.parentNode.removeChild(this.td);
12769     },
12770     
12771     /**
12772      * Shows this button
12773      */
12774     show: function(){
12775         this.hidden = false;
12776         this.td.style.display = "";
12777     },
12778     
12779     /**
12780      * Hides this button
12781      */
12782     hide: function(){
12783         this.hidden = true;
12784         this.td.style.display = "none";
12785     }
12786 });
12787
12788 // backwards compat
12789 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799  
12800 /**
12801  * @class Roo.PagingToolbar
12802  * @extends Roo.Toolbar
12803  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12804  * @constructor
12805  * Create a new PagingToolbar
12806  * @param {Object} config The config object
12807  */
12808 Roo.PagingToolbar = function(el, ds, config)
12809 {
12810     // old args format still supported... - xtype is prefered..
12811     if (typeof(el) == 'object' && el.xtype) {
12812         // created from xtype...
12813         config = el;
12814         ds = el.dataSource;
12815         el = config.container;
12816     }
12817     var items = [];
12818     if (config.items) {
12819         items = config.items;
12820         config.items = [];
12821     }
12822     
12823     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12824     this.ds = ds;
12825     this.cursor = 0;
12826     this.renderButtons(this.el);
12827     this.bind(ds);
12828     
12829     // supprot items array.
12830    
12831     Roo.each(items, function(e) {
12832         this.add(Roo.factory(e));
12833     },this);
12834     
12835 };
12836
12837 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12838     /**
12839      * @cfg {Roo.data.Store} dataSource
12840      * The underlying data store providing the paged data
12841      */
12842     /**
12843      * @cfg {String/HTMLElement/Element} container
12844      * container The id or element that will contain the toolbar
12845      */
12846     /**
12847      * @cfg {Boolean} displayInfo
12848      * True to display the displayMsg (defaults to false)
12849      */
12850     /**
12851      * @cfg {Number} pageSize
12852      * The number of records to display per page (defaults to 20)
12853      */
12854     pageSize: 20,
12855     /**
12856      * @cfg {String} displayMsg
12857      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12858      */
12859     displayMsg : 'Displaying {0} - {1} of {2}',
12860     /**
12861      * @cfg {String} emptyMsg
12862      * The message to display when no records are found (defaults to "No data to display")
12863      */
12864     emptyMsg : 'No data to display',
12865     /**
12866      * Customizable piece of the default paging text (defaults to "Page")
12867      * @type String
12868      */
12869     beforePageText : "Page",
12870     /**
12871      * Customizable piece of the default paging text (defaults to "of %0")
12872      * @type String
12873      */
12874     afterPageText : "of {0}",
12875     /**
12876      * Customizable piece of the default paging text (defaults to "First Page")
12877      * @type String
12878      */
12879     firstText : "First Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "Previous Page")
12882      * @type String
12883      */
12884     prevText : "Previous Page",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "Next Page")
12887      * @type String
12888      */
12889     nextText : "Next Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Last Page")
12892      * @type String
12893      */
12894     lastText : "Last Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Refresh")
12897      * @type String
12898      */
12899     refreshText : "Refresh",
12900
12901     // private
12902     renderButtons : function(el){
12903         Roo.PagingToolbar.superclass.render.call(this, el);
12904         this.first = this.addButton({
12905             tooltip: this.firstText,
12906             cls: "x-btn-icon x-grid-page-first",
12907             disabled: true,
12908             handler: this.onClick.createDelegate(this, ["first"])
12909         });
12910         this.prev = this.addButton({
12911             tooltip: this.prevText,
12912             cls: "x-btn-icon x-grid-page-prev",
12913             disabled: true,
12914             handler: this.onClick.createDelegate(this, ["prev"])
12915         });
12916         //this.addSeparator();
12917         this.add(this.beforePageText);
12918         this.field = Roo.get(this.addDom({
12919            tag: "input",
12920            type: "text",
12921            size: "3",
12922            value: "1",
12923            cls: "x-grid-page-number"
12924         }).el);
12925         this.field.on("keydown", this.onPagingKeydown, this);
12926         this.field.on("focus", function(){this.dom.select();});
12927         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12928         this.field.setHeight(18);
12929         //this.addSeparator();
12930         this.next = this.addButton({
12931             tooltip: this.nextText,
12932             cls: "x-btn-icon x-grid-page-next",
12933             disabled: true,
12934             handler: this.onClick.createDelegate(this, ["next"])
12935         });
12936         this.last = this.addButton({
12937             tooltip: this.lastText,
12938             cls: "x-btn-icon x-grid-page-last",
12939             disabled: true,
12940             handler: this.onClick.createDelegate(this, ["last"])
12941         });
12942         //this.addSeparator();
12943         this.loading = this.addButton({
12944             tooltip: this.refreshText,
12945             cls: "x-btn-icon x-grid-loading",
12946             handler: this.onClick.createDelegate(this, ["refresh"])
12947         });
12948
12949         if(this.displayInfo){
12950             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12951         }
12952     },
12953
12954     // private
12955     updateInfo : function(){
12956         if(this.displayEl){
12957             var count = this.ds.getCount();
12958             var msg = count == 0 ?
12959                 this.emptyMsg :
12960                 String.format(
12961                     this.displayMsg,
12962                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12963                 );
12964             this.displayEl.update(msg);
12965         }
12966     },
12967
12968     // private
12969     onLoad : function(ds, r, o){
12970        this.cursor = o.params ? o.params.start : 0;
12971        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12972
12973        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12974        this.field.dom.value = ap;
12975        this.first.setDisabled(ap == 1);
12976        this.prev.setDisabled(ap == 1);
12977        this.next.setDisabled(ap == ps);
12978        this.last.setDisabled(ap == ps);
12979        this.loading.enable();
12980        this.updateInfo();
12981     },
12982
12983     // private
12984     getPageData : function(){
12985         var total = this.ds.getTotalCount();
12986         return {
12987             total : total,
12988             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12989             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12990         };
12991     },
12992
12993     // private
12994     onLoadError : function(){
12995         this.loading.enable();
12996     },
12997
12998     // private
12999     onPagingKeydown : function(e){
13000         var k = e.getKey();
13001         var d = this.getPageData();
13002         if(k == e.RETURN){
13003             var v = this.field.dom.value, pageNum;
13004             if(!v || isNaN(pageNum = parseInt(v, 10))){
13005                 this.field.dom.value = d.activePage;
13006                 return;
13007             }
13008             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13009             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13010             e.stopEvent();
13011         }
13012         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))
13013         {
13014           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13015           this.field.dom.value = pageNum;
13016           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13017           e.stopEvent();
13018         }
13019         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13020         {
13021           var v = this.field.dom.value, pageNum; 
13022           var increment = (e.shiftKey) ? 10 : 1;
13023           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13024             increment *= -1;
13025           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13026             this.field.dom.value = d.activePage;
13027             return;
13028           }
13029           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13030           {
13031             this.field.dom.value = parseInt(v, 10) + increment;
13032             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13033             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13034           }
13035           e.stopEvent();
13036         }
13037     },
13038
13039     // private
13040     beforeLoad : function(){
13041         if(this.loading){
13042             this.loading.disable();
13043         }
13044     },
13045
13046     // private
13047     onClick : function(which){
13048         var ds = this.ds;
13049         switch(which){
13050             case "first":
13051                 ds.load({params:{start: 0, limit: this.pageSize}});
13052             break;
13053             case "prev":
13054                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13055             break;
13056             case "next":
13057                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13058             break;
13059             case "last":
13060                 var total = ds.getTotalCount();
13061                 var extra = total % this.pageSize;
13062                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13063                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13064             break;
13065             case "refresh":
13066                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13067             break;
13068         }
13069     },
13070
13071     /**
13072      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13073      * @param {Roo.data.Store} store The data store to unbind
13074      */
13075     unbind : function(ds){
13076         ds.un("beforeload", this.beforeLoad, this);
13077         ds.un("load", this.onLoad, this);
13078         ds.un("loadexception", this.onLoadError, this);
13079         ds.un("remove", this.updateInfo, this);
13080         ds.un("add", this.updateInfo, this);
13081         this.ds = undefined;
13082     },
13083
13084     /**
13085      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13086      * @param {Roo.data.Store} store The data store to bind
13087      */
13088     bind : function(ds){
13089         ds.on("beforeload", this.beforeLoad, this);
13090         ds.on("load", this.onLoad, this);
13091         ds.on("loadexception", this.onLoadError, this);
13092         ds.on("remove", this.updateInfo, this);
13093         ds.on("add", this.updateInfo, this);
13094         this.ds = ds;
13095     }
13096 });/*
13097  * Based on:
13098  * Ext JS Library 1.1.1
13099  * Copyright(c) 2006-2007, Ext JS, LLC.
13100  *
13101  * Originally Released Under LGPL - original licence link has changed is not relivant.
13102  *
13103  * Fork - LGPL
13104  * <script type="text/javascript">
13105  */
13106
13107 /**
13108  * @class Roo.Resizable
13109  * @extends Roo.util.Observable
13110  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13111  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13112  * 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
13113  * the element will be wrapped for you automatically.</p>
13114  * <p>Here is the list of valid resize handles:</p>
13115  * <pre>
13116 Value   Description
13117 ------  -------------------
13118  'n'     north
13119  's'     south
13120  'e'     east
13121  'w'     west
13122  'nw'    northwest
13123  'sw'    southwest
13124  'se'    southeast
13125  'ne'    northeast
13126  'hd'    horizontal drag
13127  'all'   all
13128 </pre>
13129  * <p>Here's an example showing the creation of a typical Resizable:</p>
13130  * <pre><code>
13131 var resizer = new Roo.Resizable("element-id", {
13132     handles: 'all',
13133     minWidth: 200,
13134     minHeight: 100,
13135     maxWidth: 500,
13136     maxHeight: 400,
13137     pinned: true
13138 });
13139 resizer.on("resize", myHandler);
13140 </code></pre>
13141  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13142  * resizer.east.setDisplayed(false);</p>
13143  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13144  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13145  * resize operation's new size (defaults to [0, 0])
13146  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13147  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13148  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13149  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13150  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13151  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13152  * @cfg {Number} width The width of the element in pixels (defaults to null)
13153  * @cfg {Number} height The height of the element in pixels (defaults to null)
13154  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13155  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13156  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13157  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13158  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13159  * in favor of the handles config option (defaults to false)
13160  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13161  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13162  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13163  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13164  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13165  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13166  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13167  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13168  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13169  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13170  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13171  * @constructor
13172  * Create a new resizable component
13173  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13174  * @param {Object} config configuration options
13175   */
13176 Roo.Resizable = function(el, config)
13177 {
13178     this.el = Roo.get(el);
13179
13180     if(config && config.wrap){
13181         config.resizeChild = this.el;
13182         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13183         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13184         this.el.setStyle("overflow", "hidden");
13185         this.el.setPositioning(config.resizeChild.getPositioning());
13186         config.resizeChild.clearPositioning();
13187         if(!config.width || !config.height){
13188             var csize = config.resizeChild.getSize();
13189             this.el.setSize(csize.width, csize.height);
13190         }
13191         if(config.pinned && !config.adjustments){
13192             config.adjustments = "auto";
13193         }
13194     }
13195
13196     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13197     this.proxy.unselectable();
13198     this.proxy.enableDisplayMode('block');
13199
13200     Roo.apply(this, config);
13201
13202     if(this.pinned){
13203         this.disableTrackOver = true;
13204         this.el.addClass("x-resizable-pinned");
13205     }
13206     // if the element isn't positioned, make it relative
13207     var position = this.el.getStyle("position");
13208     if(position != "absolute" && position != "fixed"){
13209         this.el.setStyle("position", "relative");
13210     }
13211     if(!this.handles){ // no handles passed, must be legacy style
13212         this.handles = 's,e,se';
13213         if(this.multiDirectional){
13214             this.handles += ',n,w';
13215         }
13216     }
13217     if(this.handles == "all"){
13218         this.handles = "n s e w ne nw se sw";
13219     }
13220     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13221     var ps = Roo.Resizable.positions;
13222     for(var i = 0, len = hs.length; i < len; i++){
13223         if(hs[i] && ps[hs[i]]){
13224             var pos = ps[hs[i]];
13225             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13226         }
13227     }
13228     // legacy
13229     this.corner = this.southeast;
13230     
13231     // updateBox = the box can move..
13232     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13233         this.updateBox = true;
13234     }
13235
13236     this.activeHandle = null;
13237
13238     if(this.resizeChild){
13239         if(typeof this.resizeChild == "boolean"){
13240             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13241         }else{
13242             this.resizeChild = Roo.get(this.resizeChild, true);
13243         }
13244     }
13245     
13246     if(this.adjustments == "auto"){
13247         var rc = this.resizeChild;
13248         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13249         if(rc && (hw || hn)){
13250             rc.position("relative");
13251             rc.setLeft(hw ? hw.el.getWidth() : 0);
13252             rc.setTop(hn ? hn.el.getHeight() : 0);
13253         }
13254         this.adjustments = [
13255             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13256             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13257         ];
13258     }
13259
13260     if(this.draggable){
13261         this.dd = this.dynamic ?
13262             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13263         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13264     }
13265
13266     // public events
13267     this.addEvents({
13268         /**
13269          * @event beforeresize
13270          * Fired before resize is allowed. Set enabled to false to cancel resize.
13271          * @param {Roo.Resizable} this
13272          * @param {Roo.EventObject} e The mousedown event
13273          */
13274         "beforeresize" : true,
13275         /**
13276          * @event resize
13277          * Fired after a resize.
13278          * @param {Roo.Resizable} this
13279          * @param {Number} width The new width
13280          * @param {Number} height The new height
13281          * @param {Roo.EventObject} e The mouseup event
13282          */
13283         "resize" : true
13284     });
13285
13286     if(this.width !== null && this.height !== null){
13287         this.resizeTo(this.width, this.height);
13288     }else{
13289         this.updateChildSize();
13290     }
13291     if(Roo.isIE){
13292         this.el.dom.style.zoom = 1;
13293     }
13294     Roo.Resizable.superclass.constructor.call(this);
13295 };
13296
13297 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13298         resizeChild : false,
13299         adjustments : [0, 0],
13300         minWidth : 5,
13301         minHeight : 5,
13302         maxWidth : 10000,
13303         maxHeight : 10000,
13304         enabled : true,
13305         animate : false,
13306         duration : .35,
13307         dynamic : false,
13308         handles : false,
13309         multiDirectional : false,
13310         disableTrackOver : false,
13311         easing : 'easeOutStrong',
13312         widthIncrement : 0,
13313         heightIncrement : 0,
13314         pinned : false,
13315         width : null,
13316         height : null,
13317         preserveRatio : false,
13318         transparent: false,
13319         minX: 0,
13320         minY: 0,
13321         draggable: false,
13322
13323         /**
13324          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13325          */
13326         constrainTo: undefined,
13327         /**
13328          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13329          */
13330         resizeRegion: undefined,
13331
13332
13333     /**
13334      * Perform a manual resize
13335      * @param {Number} width
13336      * @param {Number} height
13337      */
13338     resizeTo : function(width, height){
13339         this.el.setSize(width, height);
13340         this.updateChildSize();
13341         this.fireEvent("resize", this, width, height, null);
13342     },
13343
13344     // private
13345     startSizing : function(e, handle){
13346         this.fireEvent("beforeresize", this, e);
13347         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13348
13349             if(!this.overlay){
13350                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13351                 this.overlay.unselectable();
13352                 this.overlay.enableDisplayMode("block");
13353                 this.overlay.on("mousemove", this.onMouseMove, this);
13354                 this.overlay.on("mouseup", this.onMouseUp, this);
13355             }
13356             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13357
13358             this.resizing = true;
13359             this.startBox = this.el.getBox();
13360             this.startPoint = e.getXY();
13361             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13362                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13363
13364             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13365             this.overlay.show();
13366
13367             if(this.constrainTo) {
13368                 var ct = Roo.get(this.constrainTo);
13369                 this.resizeRegion = ct.getRegion().adjust(
13370                     ct.getFrameWidth('t'),
13371                     ct.getFrameWidth('l'),
13372                     -ct.getFrameWidth('b'),
13373                     -ct.getFrameWidth('r')
13374                 );
13375             }
13376
13377             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13378             this.proxy.show();
13379             this.proxy.setBox(this.startBox);
13380             if(!this.dynamic){
13381                 this.proxy.setStyle('visibility', 'visible');
13382             }
13383         }
13384     },
13385
13386     // private
13387     onMouseDown : function(handle, e){
13388         if(this.enabled){
13389             e.stopEvent();
13390             this.activeHandle = handle;
13391             this.startSizing(e, handle);
13392         }
13393     },
13394
13395     // private
13396     onMouseUp : function(e){
13397         var size = this.resizeElement();
13398         this.resizing = false;
13399         this.handleOut();
13400         this.overlay.hide();
13401         this.proxy.hide();
13402         this.fireEvent("resize", this, size.width, size.height, e);
13403     },
13404
13405     // private
13406     updateChildSize : function(){
13407         if(this.resizeChild){
13408             var el = this.el;
13409             var child = this.resizeChild;
13410             var adj = this.adjustments;
13411             if(el.dom.offsetWidth){
13412                 var b = el.getSize(true);
13413                 child.setSize(b.width+adj[0], b.height+adj[1]);
13414             }
13415             // Second call here for IE
13416             // The first call enables instant resizing and
13417             // the second call corrects scroll bars if they
13418             // exist
13419             if(Roo.isIE){
13420                 setTimeout(function(){
13421                     if(el.dom.offsetWidth){
13422                         var b = el.getSize(true);
13423                         child.setSize(b.width+adj[0], b.height+adj[1]);
13424                     }
13425                 }, 10);
13426             }
13427         }
13428     },
13429
13430     // private
13431     snap : function(value, inc, min){
13432         if(!inc || !value) return value;
13433         var newValue = value;
13434         var m = value % inc;
13435         if(m > 0){
13436             if(m > (inc/2)){
13437                 newValue = value + (inc-m);
13438             }else{
13439                 newValue = value - m;
13440             }
13441         }
13442         return Math.max(min, newValue);
13443     },
13444
13445     // private
13446     resizeElement : function(){
13447         var box = this.proxy.getBox();
13448         if(this.updateBox){
13449             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13450         }else{
13451             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13452         }
13453         this.updateChildSize();
13454         if(!this.dynamic){
13455             this.proxy.hide();
13456         }
13457         return box;
13458     },
13459
13460     // private
13461     constrain : function(v, diff, m, mx){
13462         if(v - diff < m){
13463             diff = v - m;
13464         }else if(v - diff > mx){
13465             diff = mx - v;
13466         }
13467         return diff;
13468     },
13469
13470     // private
13471     onMouseMove : function(e){
13472         if(this.enabled){
13473             try{// try catch so if something goes wrong the user doesn't get hung
13474
13475             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13476                 return;
13477             }
13478
13479             //var curXY = this.startPoint;
13480             var curSize = this.curSize || this.startBox;
13481             var x = this.startBox.x, y = this.startBox.y;
13482             var ox = x, oy = y;
13483             var w = curSize.width, h = curSize.height;
13484             var ow = w, oh = h;
13485             var mw = this.minWidth, mh = this.minHeight;
13486             var mxw = this.maxWidth, mxh = this.maxHeight;
13487             var wi = this.widthIncrement;
13488             var hi = this.heightIncrement;
13489
13490             var eventXY = e.getXY();
13491             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13492             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13493
13494             var pos = this.activeHandle.position;
13495
13496             switch(pos){
13497                 case "east":
13498                     w += diffX;
13499                     w = Math.min(Math.max(mw, w), mxw);
13500                     break;
13501              
13502                 case "south":
13503                     h += diffY;
13504                     h = Math.min(Math.max(mh, h), mxh);
13505                     break;
13506                 case "southeast":
13507                     w += diffX;
13508                     h += diffY;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     h = Math.min(Math.max(mh, h), mxh);
13511                     break;
13512                 case "north":
13513                     diffY = this.constrain(h, diffY, mh, mxh);
13514                     y += diffY;
13515                     h -= diffY;
13516                     break;
13517                 case "hdrag":
13518                     
13519                     if (wi) {
13520                         var adiffX = Math.abs(diffX);
13521                         var sub = (adiffX % wi); // how much 
13522                         if (sub > (wi/2)) { // far enough to snap
13523                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13524                         } else {
13525                             // remove difference.. 
13526                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13527                         }
13528                     }
13529                     x += diffX;
13530                     x = Math.max(this.minX, x);
13531                     break;
13532                 case "west":
13533                     diffX = this.constrain(w, diffX, mw, mxw);
13534                     x += diffX;
13535                     w -= diffX;
13536                     break;
13537                 case "northeast":
13538                     w += diffX;
13539                     w = Math.min(Math.max(mw, w), mxw);
13540                     diffY = this.constrain(h, diffY, mh, mxh);
13541                     y += diffY;
13542                     h -= diffY;
13543                     break;
13544                 case "northwest":
13545                     diffX = this.constrain(w, diffX, mw, mxw);
13546                     diffY = this.constrain(h, diffY, mh, mxh);
13547                     y += diffY;
13548                     h -= diffY;
13549                     x += diffX;
13550                     w -= diffX;
13551                     break;
13552                case "southwest":
13553                     diffX = this.constrain(w, diffX, mw, mxw);
13554                     h += diffY;
13555                     h = Math.min(Math.max(mh, h), mxh);
13556                     x += diffX;
13557                     w -= diffX;
13558                     break;
13559             }
13560
13561             var sw = this.snap(w, wi, mw);
13562             var sh = this.snap(h, hi, mh);
13563             if(sw != w || sh != h){
13564                 switch(pos){
13565                     case "northeast":
13566                         y -= sh - h;
13567                     break;
13568                     case "north":
13569                         y -= sh - h;
13570                         break;
13571                     case "southwest":
13572                         x -= sw - w;
13573                     break;
13574                     case "west":
13575                         x -= sw - w;
13576                         break;
13577                     case "northwest":
13578                         x -= sw - w;
13579                         y -= sh - h;
13580                     break;
13581                 }
13582                 w = sw;
13583                 h = sh;
13584             }
13585
13586             if(this.preserveRatio){
13587                 switch(pos){
13588                     case "southeast":
13589                     case "east":
13590                         h = oh * (w/ow);
13591                         h = Math.min(Math.max(mh, h), mxh);
13592                         w = ow * (h/oh);
13593                        break;
13594                     case "south":
13595                         w = ow * (h/oh);
13596                         w = Math.min(Math.max(mw, w), mxw);
13597                         h = oh * (w/ow);
13598                         break;
13599                     case "northeast":
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                     break;
13604                     case "north":
13605                         var tw = w;
13606                         w = ow * (h/oh);
13607                         w = Math.min(Math.max(mw, w), mxw);
13608                         h = oh * (w/ow);
13609                         x += (tw - w) / 2;
13610                         break;
13611                     case "southwest":
13612                         h = oh * (w/ow);
13613                         h = Math.min(Math.max(mh, h), mxh);
13614                         var tw = w;
13615                         w = ow * (h/oh);
13616                         x += tw - w;
13617                         break;
13618                     case "west":
13619                         var th = h;
13620                         h = oh * (w/ow);
13621                         h = Math.min(Math.max(mh, h), mxh);
13622                         y += (th - h) / 2;
13623                         var tw = w;
13624                         w = ow * (h/oh);
13625                         x += tw - w;
13626                        break;
13627                     case "northwest":
13628                         var tw = w;
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         w = ow * (h/oh);
13633                         y += th - h;
13634                         x += tw - w;
13635                        break;
13636
13637                 }
13638             }
13639             if (pos == 'hdrag') {
13640                 w = ow;
13641             }
13642             this.proxy.setBounds(x, y, w, h);
13643             if(this.dynamic){
13644                 this.resizeElement();
13645             }
13646             }catch(e){}
13647         }
13648     },
13649
13650     // private
13651     handleOver : function(){
13652         if(this.enabled){
13653             this.el.addClass("x-resizable-over");
13654         }
13655     },
13656
13657     // private
13658     handleOut : function(){
13659         if(!this.resizing){
13660             this.el.removeClass("x-resizable-over");
13661         }
13662     },
13663
13664     /**
13665      * Returns the element this component is bound to.
13666      * @return {Roo.Element}
13667      */
13668     getEl : function(){
13669         return this.el;
13670     },
13671
13672     /**
13673      * Returns the resizeChild element (or null).
13674      * @return {Roo.Element}
13675      */
13676     getResizeChild : function(){
13677         return this.resizeChild;
13678     },
13679
13680     /**
13681      * Destroys this resizable. If the element was wrapped and
13682      * removeEl is not true then the element remains.
13683      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13684      */
13685     destroy : function(removeEl){
13686         this.proxy.remove();
13687         if(this.overlay){
13688             this.overlay.removeAllListeners();
13689             this.overlay.remove();
13690         }
13691         var ps = Roo.Resizable.positions;
13692         for(var k in ps){
13693             if(typeof ps[k] != "function" && this[ps[k]]){
13694                 var h = this[ps[k]];
13695                 h.el.removeAllListeners();
13696                 h.el.remove();
13697             }
13698         }
13699         if(removeEl){
13700             this.el.update("");
13701             this.el.remove();
13702         }
13703     }
13704 });
13705
13706 // private
13707 // hash to map config positions to true positions
13708 Roo.Resizable.positions = {
13709     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13710     hd: "hdrag"
13711 };
13712
13713 // private
13714 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13715     if(!this.tpl){
13716         // only initialize the template if resizable is used
13717         var tpl = Roo.DomHelper.createTemplate(
13718             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13719         );
13720         tpl.compile();
13721         Roo.Resizable.Handle.prototype.tpl = tpl;
13722     }
13723     this.position = pos;
13724     this.rz = rz;
13725     // show north drag fro topdra
13726     var handlepos = pos == 'hdrag' ? 'north' : pos;
13727     
13728     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13729     if (pos == 'hdrag') {
13730         this.el.setStyle('cursor', 'pointer');
13731     }
13732     this.el.unselectable();
13733     if(transparent){
13734         this.el.setOpacity(0);
13735     }
13736     this.el.on("mousedown", this.onMouseDown, this);
13737     if(!disableTrackOver){
13738         this.el.on("mouseover", this.onMouseOver, this);
13739         this.el.on("mouseout", this.onMouseOut, this);
13740     }
13741 };
13742
13743 // private
13744 Roo.Resizable.Handle.prototype = {
13745     afterResize : function(rz){
13746         // do nothing
13747     },
13748     // private
13749     onMouseDown : function(e){
13750         this.rz.onMouseDown(this, e);
13751     },
13752     // private
13753     onMouseOver : function(e){
13754         this.rz.handleOver(this, e);
13755     },
13756     // private
13757     onMouseOut : function(e){
13758         this.rz.handleOut(this, e);
13759     }
13760 };/*
13761  * Based on:
13762  * Ext JS Library 1.1.1
13763  * Copyright(c) 2006-2007, Ext JS, LLC.
13764  *
13765  * Originally Released Under LGPL - original licence link has changed is not relivant.
13766  *
13767  * Fork - LGPL
13768  * <script type="text/javascript">
13769  */
13770
13771 /**
13772  * @class Roo.Editor
13773  * @extends Roo.Component
13774  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13775  * @constructor
13776  * Create a new Editor
13777  * @param {Roo.form.Field} field The Field object (or descendant)
13778  * @param {Object} config The config object
13779  */
13780 Roo.Editor = function(field, config){
13781     Roo.Editor.superclass.constructor.call(this, config);
13782     this.field = field;
13783     this.addEvents({
13784         /**
13785              * @event beforestartedit
13786              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13787              * false from the handler of this event.
13788              * @param {Editor} this
13789              * @param {Roo.Element} boundEl The underlying element bound to this editor
13790              * @param {Mixed} value The field value being set
13791              */
13792         "beforestartedit" : true,
13793         /**
13794              * @event startedit
13795              * Fires when this editor is displayed
13796              * @param {Roo.Element} boundEl The underlying element bound to this editor
13797              * @param {Mixed} value The starting field value
13798              */
13799         "startedit" : true,
13800         /**
13801              * @event beforecomplete
13802              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13803              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13804              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13805              * event will not fire since no edit actually occurred.
13806              * @param {Editor} this
13807              * @param {Mixed} value The current field value
13808              * @param {Mixed} startValue The original field value
13809              */
13810         "beforecomplete" : true,
13811         /**
13812              * @event complete
13813              * Fires after editing is complete and any changed value has been written to the underlying field.
13814              * @param {Editor} this
13815              * @param {Mixed} value The current field value
13816              * @param {Mixed} startValue The original field value
13817              */
13818         "complete" : true,
13819         /**
13820          * @event specialkey
13821          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13822          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13823          * @param {Roo.form.Field} this
13824          * @param {Roo.EventObject} e The event object
13825          */
13826         "specialkey" : true
13827     });
13828 };
13829
13830 Roo.extend(Roo.Editor, Roo.Component, {
13831     /**
13832      * @cfg {Boolean/String} autosize
13833      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13834      * or "height" to adopt the height only (defaults to false)
13835      */
13836     /**
13837      * @cfg {Boolean} revertInvalid
13838      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13839      * validation fails (defaults to true)
13840      */
13841     /**
13842      * @cfg {Boolean} ignoreNoChange
13843      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13844      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13845      * will never be ignored.
13846      */
13847     /**
13848      * @cfg {Boolean} hideEl
13849      * False to keep the bound element visible while the editor is displayed (defaults to true)
13850      */
13851     /**
13852      * @cfg {Mixed} value
13853      * The data value of the underlying field (defaults to "")
13854      */
13855     value : "",
13856     /**
13857      * @cfg {String} alignment
13858      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13859      */
13860     alignment: "c-c?",
13861     /**
13862      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13863      * for bottom-right shadow (defaults to "frame")
13864      */
13865     shadow : "frame",
13866     /**
13867      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13868      */
13869     constrain : false,
13870     /**
13871      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13872      */
13873     completeOnEnter : false,
13874     /**
13875      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13876      */
13877     cancelOnEsc : false,
13878     /**
13879      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13880      */
13881     updateEl : false,
13882
13883     // private
13884     onRender : function(ct, position){
13885         this.el = new Roo.Layer({
13886             shadow: this.shadow,
13887             cls: "x-editor",
13888             parentEl : ct,
13889             shim : this.shim,
13890             shadowOffset:4,
13891             id: this.id,
13892             constrain: this.constrain
13893         });
13894         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13895         if(this.field.msgTarget != 'title'){
13896             this.field.msgTarget = 'qtip';
13897         }
13898         this.field.render(this.el);
13899         if(Roo.isGecko){
13900             this.field.el.dom.setAttribute('autocomplete', 'off');
13901         }
13902         this.field.on("specialkey", this.onSpecialKey, this);
13903         if(this.swallowKeys){
13904             this.field.el.swallowEvent(['keydown','keypress']);
13905         }
13906         this.field.show();
13907         this.field.on("blur", this.onBlur, this);
13908         if(this.field.grow){
13909             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13910         }
13911     },
13912
13913     onSpecialKey : function(field, e){
13914         //Roo.log('editor onSpecialKey');
13915         if(this.completeOnEnter && e.getKey() == e.ENTER){
13916             e.stopEvent();
13917             this.completeEdit();
13918         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13919             this.cancelEdit();
13920         }else{
13921             this.fireEvent('specialkey', field, e);
13922         }
13923     },
13924
13925     /**
13926      * Starts the editing process and shows the editor.
13927      * @param {String/HTMLElement/Element} el The element to edit
13928      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13929       * to the innerHTML of el.
13930      */
13931     startEdit : function(el, value){
13932         if(this.editing){
13933             this.completeEdit();
13934         }
13935         this.boundEl = Roo.get(el);
13936         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13937         if(!this.rendered){
13938             this.render(this.parentEl || document.body);
13939         }
13940         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13941             return;
13942         }
13943         this.startValue = v;
13944         this.field.setValue(v);
13945         if(this.autoSize){
13946             var sz = this.boundEl.getSize();
13947             switch(this.autoSize){
13948                 case "width":
13949                 this.setSize(sz.width,  "");
13950                 break;
13951                 case "height":
13952                 this.setSize("",  sz.height);
13953                 break;
13954                 default:
13955                 this.setSize(sz.width,  sz.height);
13956             }
13957         }
13958         this.el.alignTo(this.boundEl, this.alignment);
13959         this.editing = true;
13960         if(Roo.QuickTips){
13961             Roo.QuickTips.disable();
13962         }
13963         this.show();
13964     },
13965
13966     /**
13967      * Sets the height and width of this editor.
13968      * @param {Number} width The new width
13969      * @param {Number} height The new height
13970      */
13971     setSize : function(w, h){
13972         this.field.setSize(w, h);
13973         if(this.el){
13974             this.el.sync();
13975         }
13976     },
13977
13978     /**
13979      * Realigns the editor to the bound field based on the current alignment config value.
13980      */
13981     realign : function(){
13982         this.el.alignTo(this.boundEl, this.alignment);
13983     },
13984
13985     /**
13986      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13987      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13988      */
13989     completeEdit : function(remainVisible){
13990         if(!this.editing){
13991             return;
13992         }
13993         var v = this.getValue();
13994         if(this.revertInvalid !== false && !this.field.isValid()){
13995             v = this.startValue;
13996             this.cancelEdit(true);
13997         }
13998         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13999             this.editing = false;
14000             this.hide();
14001             return;
14002         }
14003         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14004             this.editing = false;
14005             if(this.updateEl && this.boundEl){
14006                 this.boundEl.update(v);
14007             }
14008             if(remainVisible !== true){
14009                 this.hide();
14010             }
14011             this.fireEvent("complete", this, v, this.startValue);
14012         }
14013     },
14014
14015     // private
14016     onShow : function(){
14017         this.el.show();
14018         if(this.hideEl !== false){
14019             this.boundEl.hide();
14020         }
14021         this.field.show();
14022         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14023             this.fixIEFocus = true;
14024             this.deferredFocus.defer(50, this);
14025         }else{
14026             this.field.focus();
14027         }
14028         this.fireEvent("startedit", this.boundEl, this.startValue);
14029     },
14030
14031     deferredFocus : function(){
14032         if(this.editing){
14033             this.field.focus();
14034         }
14035     },
14036
14037     /**
14038      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14039      * reverted to the original starting value.
14040      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14041      * cancel (defaults to false)
14042      */
14043     cancelEdit : function(remainVisible){
14044         if(this.editing){
14045             this.setValue(this.startValue);
14046             if(remainVisible !== true){
14047                 this.hide();
14048             }
14049         }
14050     },
14051
14052     // private
14053     onBlur : function(){
14054         if(this.allowBlur !== true && this.editing){
14055             this.completeEdit();
14056         }
14057     },
14058
14059     // private
14060     onHide : function(){
14061         if(this.editing){
14062             this.completeEdit();
14063             return;
14064         }
14065         this.field.blur();
14066         if(this.field.collapse){
14067             this.field.collapse();
14068         }
14069         this.el.hide();
14070         if(this.hideEl !== false){
14071             this.boundEl.show();
14072         }
14073         if(Roo.QuickTips){
14074             Roo.QuickTips.enable();
14075         }
14076     },
14077
14078     /**
14079      * Sets the data value of the editor
14080      * @param {Mixed} value Any valid value supported by the underlying field
14081      */
14082     setValue : function(v){
14083         this.field.setValue(v);
14084     },
14085
14086     /**
14087      * Gets the data value of the editor
14088      * @return {Mixed} The data value
14089      */
14090     getValue : function(){
14091         return this.field.getValue();
14092     }
14093 });/*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103  
14104 /**
14105  * @class Roo.BasicDialog
14106  * @extends Roo.util.Observable
14107  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14108  * <pre><code>
14109 var dlg = new Roo.BasicDialog("my-dlg", {
14110     height: 200,
14111     width: 300,
14112     minHeight: 100,
14113     minWidth: 150,
14114     modal: true,
14115     proxyDrag: true,
14116     shadow: true
14117 });
14118 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14119 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14120 dlg.addButton('Cancel', dlg.hide, dlg);
14121 dlg.show();
14122 </code></pre>
14123   <b>A Dialog should always be a direct child of the body element.</b>
14124  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14125  * @cfg {String} title Default text to display in the title bar (defaults to null)
14126  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14127  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14128  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14129  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14130  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14131  * (defaults to null with no animation)
14132  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14133  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14134  * property for valid values (defaults to 'all')
14135  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14136  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14137  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14138  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14139  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14140  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14141  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14142  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14143  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14144  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14145  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14146  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14147  * draggable = true (defaults to false)
14148  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14149  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14150  * shadow (defaults to false)
14151  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14152  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14153  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14154  * @cfg {Array} buttons Array of buttons
14155  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14156  * @constructor
14157  * Create a new BasicDialog.
14158  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14159  * @param {Object} config Configuration options
14160  */
14161 Roo.BasicDialog = function(el, config){
14162     this.el = Roo.get(el);
14163     var dh = Roo.DomHelper;
14164     if(!this.el && config && config.autoCreate){
14165         if(typeof config.autoCreate == "object"){
14166             if(!config.autoCreate.id){
14167                 config.autoCreate.id = el;
14168             }
14169             this.el = dh.append(document.body,
14170                         config.autoCreate, true);
14171         }else{
14172             this.el = dh.append(document.body,
14173                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14174         }
14175     }
14176     el = this.el;
14177     el.setDisplayed(true);
14178     el.hide = this.hideAction;
14179     this.id = el.id;
14180     el.addClass("x-dlg");
14181
14182     Roo.apply(this, config);
14183
14184     this.proxy = el.createProxy("x-dlg-proxy");
14185     this.proxy.hide = this.hideAction;
14186     this.proxy.setOpacity(.5);
14187     this.proxy.hide();
14188
14189     if(config.width){
14190         el.setWidth(config.width);
14191     }
14192     if(config.height){
14193         el.setHeight(config.height);
14194     }
14195     this.size = el.getSize();
14196     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14197         this.xy = [config.x,config.y];
14198     }else{
14199         this.xy = el.getCenterXY(true);
14200     }
14201     /** The header element @type Roo.Element */
14202     this.header = el.child("> .x-dlg-hd");
14203     /** The body element @type Roo.Element */
14204     this.body = el.child("> .x-dlg-bd");
14205     /** The footer element @type Roo.Element */
14206     this.footer = el.child("> .x-dlg-ft");
14207
14208     if(!this.header){
14209         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14210     }
14211     if(!this.body){
14212         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14213     }
14214
14215     this.header.unselectable();
14216     if(this.title){
14217         this.header.update(this.title);
14218     }
14219     // this element allows the dialog to be focused for keyboard event
14220     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14221     this.focusEl.swallowEvent("click", true);
14222
14223     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14224
14225     // wrap the body and footer for special rendering
14226     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14227     if(this.footer){
14228         this.bwrap.dom.appendChild(this.footer.dom);
14229     }
14230
14231     this.bg = this.el.createChild({
14232         tag: "div", cls:"x-dlg-bg",
14233         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14234     });
14235     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14236
14237
14238     if(this.autoScroll !== false && !this.autoTabs){
14239         this.body.setStyle("overflow", "auto");
14240     }
14241
14242     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14243
14244     if(this.closable !== false){
14245         this.el.addClass("x-dlg-closable");
14246         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14247         this.close.on("click", this.closeClick, this);
14248         this.close.addClassOnOver("x-dlg-close-over");
14249     }
14250     if(this.collapsible !== false){
14251         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14252         this.collapseBtn.on("click", this.collapseClick, this);
14253         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14254         this.header.on("dblclick", this.collapseClick, this);
14255     }
14256     if(this.resizable !== false){
14257         this.el.addClass("x-dlg-resizable");
14258         this.resizer = new Roo.Resizable(el, {
14259             minWidth: this.minWidth || 80,
14260             minHeight:this.minHeight || 80,
14261             handles: this.resizeHandles || "all",
14262             pinned: true
14263         });
14264         this.resizer.on("beforeresize", this.beforeResize, this);
14265         this.resizer.on("resize", this.onResize, this);
14266     }
14267     if(this.draggable !== false){
14268         el.addClass("x-dlg-draggable");
14269         if (!this.proxyDrag) {
14270             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14271         }
14272         else {
14273             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14274         }
14275         dd.setHandleElId(this.header.id);
14276         dd.endDrag = this.endMove.createDelegate(this);
14277         dd.startDrag = this.startMove.createDelegate(this);
14278         dd.onDrag = this.onDrag.createDelegate(this);
14279         dd.scroll = false;
14280         this.dd = dd;
14281     }
14282     if(this.modal){
14283         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14284         this.mask.enableDisplayMode("block");
14285         this.mask.hide();
14286         this.el.addClass("x-dlg-modal");
14287     }
14288     if(this.shadow){
14289         this.shadow = new Roo.Shadow({
14290             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14291             offset : this.shadowOffset
14292         });
14293     }else{
14294         this.shadowOffset = 0;
14295     }
14296     if(Roo.useShims && this.shim !== false){
14297         this.shim = this.el.createShim();
14298         this.shim.hide = this.hideAction;
14299         this.shim.hide();
14300     }else{
14301         this.shim = false;
14302     }
14303     if(this.autoTabs){
14304         this.initTabs();
14305     }
14306     if (this.buttons) { 
14307         var bts= this.buttons;
14308         this.buttons = [];
14309         Roo.each(bts, function(b) {
14310             this.addButton(b);
14311         }, this);
14312     }
14313     
14314     
14315     this.addEvents({
14316         /**
14317          * @event keydown
14318          * Fires when a key is pressed
14319          * @param {Roo.BasicDialog} this
14320          * @param {Roo.EventObject} e
14321          */
14322         "keydown" : true,
14323         /**
14324          * @event move
14325          * Fires when this dialog is moved by the user.
14326          * @param {Roo.BasicDialog} this
14327          * @param {Number} x The new page X
14328          * @param {Number} y The new page Y
14329          */
14330         "move" : true,
14331         /**
14332          * @event resize
14333          * Fires when this dialog is resized by the user.
14334          * @param {Roo.BasicDialog} this
14335          * @param {Number} width The new width
14336          * @param {Number} height The new height
14337          */
14338         "resize" : true,
14339         /**
14340          * @event beforehide
14341          * Fires before this dialog is hidden.
14342          * @param {Roo.BasicDialog} this
14343          */
14344         "beforehide" : true,
14345         /**
14346          * @event hide
14347          * Fires when this dialog is hidden.
14348          * @param {Roo.BasicDialog} this
14349          */
14350         "hide" : true,
14351         /**
14352          * @event beforeshow
14353          * Fires before this dialog is shown.
14354          * @param {Roo.BasicDialog} this
14355          */
14356         "beforeshow" : true,
14357         /**
14358          * @event show
14359          * Fires when this dialog is shown.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "show" : true
14363     });
14364     el.on("keydown", this.onKeyDown, this);
14365     el.on("mousedown", this.toFront, this);
14366     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14367     this.el.hide();
14368     Roo.DialogManager.register(this);
14369     Roo.BasicDialog.superclass.constructor.call(this);
14370 };
14371
14372 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14373     shadowOffset: Roo.isIE ? 6 : 5,
14374     minHeight: 80,
14375     minWidth: 200,
14376     minButtonWidth: 75,
14377     defaultButton: null,
14378     buttonAlign: "right",
14379     tabTag: 'div',
14380     firstShow: true,
14381
14382     /**
14383      * Sets the dialog title text
14384      * @param {String} text The title text to display
14385      * @return {Roo.BasicDialog} this
14386      */
14387     setTitle : function(text){
14388         this.header.update(text);
14389         return this;
14390     },
14391
14392     // private
14393     closeClick : function(){
14394         this.hide();
14395     },
14396
14397     // private
14398     collapseClick : function(){
14399         this[this.collapsed ? "expand" : "collapse"]();
14400     },
14401
14402     /**
14403      * Collapses the dialog to its minimized state (only the title bar is visible).
14404      * Equivalent to the user clicking the collapse dialog button.
14405      */
14406     collapse : function(){
14407         if(!this.collapsed){
14408             this.collapsed = true;
14409             this.el.addClass("x-dlg-collapsed");
14410             this.restoreHeight = this.el.getHeight();
14411             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14412         }
14413     },
14414
14415     /**
14416      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14417      * clicking the expand dialog button.
14418      */
14419     expand : function(){
14420         if(this.collapsed){
14421             this.collapsed = false;
14422             this.el.removeClass("x-dlg-collapsed");
14423             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14424         }
14425     },
14426
14427     /**
14428      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14429      * @return {Roo.TabPanel} The tabs component
14430      */
14431     initTabs : function(){
14432         var tabs = this.getTabs();
14433         while(tabs.getTab(0)){
14434             tabs.removeTab(0);
14435         }
14436         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14437             var dom = el.dom;
14438             tabs.addTab(Roo.id(dom), dom.title);
14439             dom.title = "";
14440         });
14441         tabs.activate(0);
14442         return tabs;
14443     },
14444
14445     // private
14446     beforeResize : function(){
14447         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14448     },
14449
14450     // private
14451     onResize : function(){
14452         this.refreshSize();
14453         this.syncBodyHeight();
14454         this.adjustAssets();
14455         this.focus();
14456         this.fireEvent("resize", this, this.size.width, this.size.height);
14457     },
14458
14459     // private
14460     onKeyDown : function(e){
14461         if(this.isVisible()){
14462             this.fireEvent("keydown", this, e);
14463         }
14464     },
14465
14466     /**
14467      * Resizes the dialog.
14468      * @param {Number} width
14469      * @param {Number} height
14470      * @return {Roo.BasicDialog} this
14471      */
14472     resizeTo : function(width, height){
14473         this.el.setSize(width, height);
14474         this.size = {width: width, height: height};
14475         this.syncBodyHeight();
14476         if(this.fixedcenter){
14477             this.center();
14478         }
14479         if(this.isVisible()){
14480             this.constrainXY();
14481             this.adjustAssets();
14482         }
14483         this.fireEvent("resize", this, width, height);
14484         return this;
14485     },
14486
14487
14488     /**
14489      * Resizes the dialog to fit the specified content size.
14490      * @param {Number} width
14491      * @param {Number} height
14492      * @return {Roo.BasicDialog} this
14493      */
14494     setContentSize : function(w, h){
14495         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14496         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14497         //if(!this.el.isBorderBox()){
14498             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14499             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14500         //}
14501         if(this.tabs){
14502             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14503             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14504         }
14505         this.resizeTo(w, h);
14506         return this;
14507     },
14508
14509     /**
14510      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14511      * executed in response to a particular key being pressed while the dialog is active.
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14513      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14514      * @param {Function} fn The function to call
14515      * @param {Object} scope (optional) The scope of the function
14516      * @return {Roo.BasicDialog} this
14517      */
14518     addKeyListener : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key["key"];
14522             shift = key["shift"];
14523             ctrl = key["ctrl"];
14524             alt = key["alt"];
14525         }else{
14526             keyCode = key;
14527         }
14528         var handler = function(dlg, e){
14529             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14530                 var k = e.getKey();
14531                 if(keyCode instanceof Array){
14532                     for(var i = 0, len = keyCode.length; i < len; i++){
14533                         if(keyCode[i] == k){
14534                           fn.call(scope || window, dlg, k, e);
14535                           return;
14536                         }
14537                     }
14538                 }else{
14539                     if(k == keyCode){
14540                         fn.call(scope || window, dlg, k, e);
14541                     }
14542                 }
14543             }
14544         };
14545         this.on("keydown", handler);
14546         return this;
14547     },
14548
14549     /**
14550      * Returns the TabPanel component (creates it if it doesn't exist).
14551      * Note: If you wish to simply check for the existence of tabs without creating them,
14552      * check for a null 'tabs' property.
14553      * @return {Roo.TabPanel} The tabs component
14554      */
14555     getTabs : function(){
14556         if(!this.tabs){
14557             this.el.addClass("x-dlg-auto-tabs");
14558             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14559             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14560         }
14561         return this.tabs;
14562     },
14563
14564     /**
14565      * Adds a button to the footer section of the dialog.
14566      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14567      * object or a valid Roo.DomHelper element config
14568      * @param {Function} handler The function called when the button is clicked
14569      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14570      * @return {Roo.Button} The new button
14571      */
14572     addButton : function(config, handler, scope){
14573         var dh = Roo.DomHelper;
14574         if(!this.footer){
14575             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14576         }
14577         if(!this.btnContainer){
14578             var tb = this.footer.createChild({
14579
14580                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14581                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14582             }, null, true);
14583             this.btnContainer = tb.firstChild.firstChild.firstChild;
14584         }
14585         var bconfig = {
14586             handler: handler,
14587             scope: scope,
14588             minWidth: this.minButtonWidth,
14589             hideParent:true
14590         };
14591         if(typeof config == "string"){
14592             bconfig.text = config;
14593         }else{
14594             if(config.tag){
14595                 bconfig.dhconfig = config;
14596             }else{
14597                 Roo.apply(bconfig, config);
14598             }
14599         }
14600         var fc = false;
14601         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14602             bconfig.position = Math.max(0, bconfig.position);
14603             fc = this.btnContainer.childNodes[bconfig.position];
14604         }
14605          
14606         var btn = new Roo.Button(
14607             fc ? 
14608                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14609                 : this.btnContainer.appendChild(document.createElement("td")),
14610             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14611             bconfig
14612         );
14613         this.syncBodyHeight();
14614         if(!this.buttons){
14615             /**
14616              * Array of all the buttons that have been added to this dialog via addButton
14617              * @type Array
14618              */
14619             this.buttons = [];
14620         }
14621         this.buttons.push(btn);
14622         return btn;
14623     },
14624
14625     /**
14626      * Sets the default button to be focused when the dialog is displayed.
14627      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14628      * @return {Roo.BasicDialog} this
14629      */
14630     setDefaultButton : function(btn){
14631         this.defaultButton = btn;
14632         return this;
14633     },
14634
14635     // private
14636     getHeaderFooterHeight : function(safe){
14637         var height = 0;
14638         if(this.header){
14639            height += this.header.getHeight();
14640         }
14641         if(this.footer){
14642            var fm = this.footer.getMargins();
14643             height += (this.footer.getHeight()+fm.top+fm.bottom);
14644         }
14645         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14646         height += this.centerBg.getPadding("tb");
14647         return height;
14648     },
14649
14650     // private
14651     syncBodyHeight : function(){
14652         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14653         var height = this.size.height - this.getHeaderFooterHeight(false);
14654         bd.setHeight(height-bd.getMargins("tb"));
14655         var hh = this.header.getHeight();
14656         var h = this.size.height-hh;
14657         cb.setHeight(h);
14658         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14659         bw.setHeight(h-cb.getPadding("tb"));
14660         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14661         bd.setWidth(bw.getWidth(true));
14662         if(this.tabs){
14663             this.tabs.syncHeight();
14664             if(Roo.isIE){
14665                 this.tabs.el.repaint();
14666             }
14667         }
14668     },
14669
14670     /**
14671      * Restores the previous state of the dialog if Roo.state is configured.
14672      * @return {Roo.BasicDialog} this
14673      */
14674     restoreState : function(){
14675         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14676         if(box && box.width){
14677             this.xy = [box.x, box.y];
14678             this.resizeTo(box.width, box.height);
14679         }
14680         return this;
14681     },
14682
14683     // private
14684     beforeShow : function(){
14685         this.expand();
14686         if(this.fixedcenter){
14687             this.xy = this.el.getCenterXY(true);
14688         }
14689         if(this.modal){
14690             Roo.get(document.body).addClass("x-body-masked");
14691             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14692             this.mask.show();
14693         }
14694         this.constrainXY();
14695     },
14696
14697     // private
14698     animShow : function(){
14699         var b = Roo.get(this.animateTarget).getBox();
14700         this.proxy.setSize(b.width, b.height);
14701         this.proxy.setLocation(b.x, b.y);
14702         this.proxy.show();
14703         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14704                     true, .35, this.showEl.createDelegate(this));
14705     },
14706
14707     /**
14708      * Shows the dialog.
14709      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14710      * @return {Roo.BasicDialog} this
14711      */
14712     show : function(animateTarget){
14713         if (this.fireEvent("beforeshow", this) === false){
14714             return;
14715         }
14716         if(this.syncHeightBeforeShow){
14717             this.syncBodyHeight();
14718         }else if(this.firstShow){
14719             this.firstShow = false;
14720             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14721         }
14722         this.animateTarget = animateTarget || this.animateTarget;
14723         if(!this.el.isVisible()){
14724             this.beforeShow();
14725             if(this.animateTarget && Roo.get(this.animateTarget)){
14726                 this.animShow();
14727             }else{
14728                 this.showEl();
14729             }
14730         }
14731         return this;
14732     },
14733
14734     // private
14735     showEl : function(){
14736         this.proxy.hide();
14737         this.el.setXY(this.xy);
14738         this.el.show();
14739         this.adjustAssets(true);
14740         this.toFront();
14741         this.focus();
14742         // IE peekaboo bug - fix found by Dave Fenwick
14743         if(Roo.isIE){
14744             this.el.repaint();
14745         }
14746         this.fireEvent("show", this);
14747     },
14748
14749     /**
14750      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14751      * dialog itself will receive focus.
14752      */
14753     focus : function(){
14754         if(this.defaultButton){
14755             this.defaultButton.focus();
14756         }else{
14757             this.focusEl.focus();
14758         }
14759     },
14760
14761     // private
14762     constrainXY : function(){
14763         if(this.constraintoviewport !== false){
14764             if(!this.viewSize){
14765                 if(this.container){
14766                     var s = this.container.getSize();
14767                     this.viewSize = [s.width, s.height];
14768                 }else{
14769                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14770                 }
14771             }
14772             var s = Roo.get(this.container||document).getScroll();
14773
14774             var x = this.xy[0], y = this.xy[1];
14775             var w = this.size.width, h = this.size.height;
14776             var vw = this.viewSize[0], vh = this.viewSize[1];
14777             // only move it if it needs it
14778             var moved = false;
14779             // first validate right/bottom
14780             if(x + w > vw+s.left){
14781                 x = vw - w;
14782                 moved = true;
14783             }
14784             if(y + h > vh+s.top){
14785                 y = vh - h;
14786                 moved = true;
14787             }
14788             // then make sure top/left isn't negative
14789             if(x < s.left){
14790                 x = s.left;
14791                 moved = true;
14792             }
14793             if(y < s.top){
14794                 y = s.top;
14795                 moved = true;
14796             }
14797             if(moved){
14798                 // cache xy
14799                 this.xy = [x, y];
14800                 if(this.isVisible()){
14801                     this.el.setLocation(x, y);
14802                     this.adjustAssets();
14803                 }
14804             }
14805         }
14806     },
14807
14808     // private
14809     onDrag : function(){
14810         if(!this.proxyDrag){
14811             this.xy = this.el.getXY();
14812             this.adjustAssets();
14813         }
14814     },
14815
14816     // private
14817     adjustAssets : function(doShow){
14818         var x = this.xy[0], y = this.xy[1];
14819         var w = this.size.width, h = this.size.height;
14820         if(doShow === true){
14821             if(this.shadow){
14822                 this.shadow.show(this.el);
14823             }
14824             if(this.shim){
14825                 this.shim.show();
14826             }
14827         }
14828         if(this.shadow && this.shadow.isVisible()){
14829             this.shadow.show(this.el);
14830         }
14831         if(this.shim && this.shim.isVisible()){
14832             this.shim.setBounds(x, y, w, h);
14833         }
14834     },
14835
14836     // private
14837     adjustViewport : function(w, h){
14838         if(!w || !h){
14839             w = Roo.lib.Dom.getViewWidth();
14840             h = Roo.lib.Dom.getViewHeight();
14841         }
14842         // cache the size
14843         this.viewSize = [w, h];
14844         if(this.modal && this.mask.isVisible()){
14845             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14846             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14847         }
14848         if(this.isVisible()){
14849             this.constrainXY();
14850         }
14851     },
14852
14853     /**
14854      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14855      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14856      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14857      */
14858     destroy : function(removeEl){
14859         if(this.isVisible()){
14860             this.animateTarget = null;
14861             this.hide();
14862         }
14863         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14864         if(this.tabs){
14865             this.tabs.destroy(removeEl);
14866         }
14867         Roo.destroy(
14868              this.shim,
14869              this.proxy,
14870              this.resizer,
14871              this.close,
14872              this.mask
14873         );
14874         if(this.dd){
14875             this.dd.unreg();
14876         }
14877         if(this.buttons){
14878            for(var i = 0, len = this.buttons.length; i < len; i++){
14879                this.buttons[i].destroy();
14880            }
14881         }
14882         this.el.removeAllListeners();
14883         if(removeEl === true){
14884             this.el.update("");
14885             this.el.remove();
14886         }
14887         Roo.DialogManager.unregister(this);
14888     },
14889
14890     // private
14891     startMove : function(){
14892         if(this.proxyDrag){
14893             this.proxy.show();
14894         }
14895         if(this.constraintoviewport !== false){
14896             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14897         }
14898     },
14899
14900     // private
14901     endMove : function(){
14902         if(!this.proxyDrag){
14903             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14904         }else{
14905             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14906             this.proxy.hide();
14907         }
14908         this.refreshSize();
14909         this.adjustAssets();
14910         this.focus();
14911         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14912     },
14913
14914     /**
14915      * Brings this dialog to the front of any other visible dialogs
14916      * @return {Roo.BasicDialog} this
14917      */
14918     toFront : function(){
14919         Roo.DialogManager.bringToFront(this);
14920         return this;
14921     },
14922
14923     /**
14924      * Sends this dialog to the back (under) of any other visible dialogs
14925      * @return {Roo.BasicDialog} this
14926      */
14927     toBack : function(){
14928         Roo.DialogManager.sendToBack(this);
14929         return this;
14930     },
14931
14932     /**
14933      * Centers this dialog in the viewport
14934      * @return {Roo.BasicDialog} this
14935      */
14936     center : function(){
14937         var xy = this.el.getCenterXY(true);
14938         this.moveTo(xy[0], xy[1]);
14939         return this;
14940     },
14941
14942     /**
14943      * Moves the dialog's top-left corner to the specified point
14944      * @param {Number} x
14945      * @param {Number} y
14946      * @return {Roo.BasicDialog} this
14947      */
14948     moveTo : function(x, y){
14949         this.xy = [x,y];
14950         if(this.isVisible()){
14951             this.el.setXY(this.xy);
14952             this.adjustAssets();
14953         }
14954         return this;
14955     },
14956
14957     /**
14958      * Aligns the dialog to the specified element
14959      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14960      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14961      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14962      * @return {Roo.BasicDialog} this
14963      */
14964     alignTo : function(element, position, offsets){
14965         this.xy = this.el.getAlignToXY(element, position, offsets);
14966         if(this.isVisible()){
14967             this.el.setXY(this.xy);
14968             this.adjustAssets();
14969         }
14970         return this;
14971     },
14972
14973     /**
14974      * Anchors an element to another element and realigns it when the window is resized.
14975      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14976      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14977      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14978      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14979      * is a number, it is used as the buffer delay (defaults to 50ms).
14980      * @return {Roo.BasicDialog} this
14981      */
14982     anchorTo : function(el, alignment, offsets, monitorScroll){
14983         var action = function(){
14984             this.alignTo(el, alignment, offsets);
14985         };
14986         Roo.EventManager.onWindowResize(action, this);
14987         var tm = typeof monitorScroll;
14988         if(tm != 'undefined'){
14989             Roo.EventManager.on(window, 'scroll', action, this,
14990                 {buffer: tm == 'number' ? monitorScroll : 50});
14991         }
14992         action.call(this);
14993         return this;
14994     },
14995
14996     /**
14997      * Returns true if the dialog is visible
14998      * @return {Boolean}
14999      */
15000     isVisible : function(){
15001         return this.el.isVisible();
15002     },
15003
15004     // private
15005     animHide : function(callback){
15006         var b = Roo.get(this.animateTarget).getBox();
15007         this.proxy.show();
15008         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15009         this.el.hide();
15010         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15011                     this.hideEl.createDelegate(this, [callback]));
15012     },
15013
15014     /**
15015      * Hides the dialog.
15016      * @param {Function} callback (optional) Function to call when the dialog is hidden
15017      * @return {Roo.BasicDialog} this
15018      */
15019     hide : function(callback){
15020         if (this.fireEvent("beforehide", this) === false){
15021             return;
15022         }
15023         if(this.shadow){
15024             this.shadow.hide();
15025         }
15026         if(this.shim) {
15027           this.shim.hide();
15028         }
15029         // sometimes animateTarget seems to get set.. causing problems...
15030         // this just double checks..
15031         if(this.animateTarget && Roo.get(this.animateTarget)) {
15032            this.animHide(callback);
15033         }else{
15034             this.el.hide();
15035             this.hideEl(callback);
15036         }
15037         return this;
15038     },
15039
15040     // private
15041     hideEl : function(callback){
15042         this.proxy.hide();
15043         if(this.modal){
15044             this.mask.hide();
15045             Roo.get(document.body).removeClass("x-body-masked");
15046         }
15047         this.fireEvent("hide", this);
15048         if(typeof callback == "function"){
15049             callback();
15050         }
15051     },
15052
15053     // private
15054     hideAction : function(){
15055         this.setLeft("-10000px");
15056         this.setTop("-10000px");
15057         this.setStyle("visibility", "hidden");
15058     },
15059
15060     // private
15061     refreshSize : function(){
15062         this.size = this.el.getSize();
15063         this.xy = this.el.getXY();
15064         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15065     },
15066
15067     // private
15068     // z-index is managed by the DialogManager and may be overwritten at any time
15069     setZIndex : function(index){
15070         if(this.modal){
15071             this.mask.setStyle("z-index", index);
15072         }
15073         if(this.shim){
15074             this.shim.setStyle("z-index", ++index);
15075         }
15076         if(this.shadow){
15077             this.shadow.setZIndex(++index);
15078         }
15079         this.el.setStyle("z-index", ++index);
15080         if(this.proxy){
15081             this.proxy.setStyle("z-index", ++index);
15082         }
15083         if(this.resizer){
15084             this.resizer.proxy.setStyle("z-index", ++index);
15085         }
15086
15087         this.lastZIndex = index;
15088     },
15089
15090     /**
15091      * Returns the element for this dialog
15092      * @return {Roo.Element} The underlying dialog Element
15093      */
15094     getEl : function(){
15095         return this.el;
15096     }
15097 });
15098
15099 /**
15100  * @class Roo.DialogManager
15101  * Provides global access to BasicDialogs that have been created and
15102  * support for z-indexing (layering) multiple open dialogs.
15103  */
15104 Roo.DialogManager = function(){
15105     var list = {};
15106     var accessList = [];
15107     var front = null;
15108
15109     // private
15110     var sortDialogs = function(d1, d2){
15111         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15112     };
15113
15114     // private
15115     var orderDialogs = function(){
15116         accessList.sort(sortDialogs);
15117         var seed = Roo.DialogManager.zseed;
15118         for(var i = 0, len = accessList.length; i < len; i++){
15119             var dlg = accessList[i];
15120             if(dlg){
15121                 dlg.setZIndex(seed + (i*10));
15122             }
15123         }
15124     };
15125
15126     return {
15127         /**
15128          * The starting z-index for BasicDialogs (defaults to 9000)
15129          * @type Number The z-index value
15130          */
15131         zseed : 9000,
15132
15133         // private
15134         register : function(dlg){
15135             list[dlg.id] = dlg;
15136             accessList.push(dlg);
15137         },
15138
15139         // private
15140         unregister : function(dlg){
15141             delete list[dlg.id];
15142             var i=0;
15143             var len=0;
15144             if(!accessList.indexOf){
15145                 for(  i = 0, len = accessList.length; i < len; i++){
15146                     if(accessList[i] == dlg){
15147                         accessList.splice(i, 1);
15148                         return;
15149                     }
15150                 }
15151             }else{
15152                  i = accessList.indexOf(dlg);
15153                 if(i != -1){
15154                     accessList.splice(i, 1);
15155                 }
15156             }
15157         },
15158
15159         /**
15160          * Gets a registered dialog by id
15161          * @param {String/Object} id The id of the dialog or a dialog
15162          * @return {Roo.BasicDialog} this
15163          */
15164         get : function(id){
15165             return typeof id == "object" ? id : list[id];
15166         },
15167
15168         /**
15169          * Brings the specified dialog to the front
15170          * @param {String/Object} dlg The id of the dialog or a dialog
15171          * @return {Roo.BasicDialog} this
15172          */
15173         bringToFront : function(dlg){
15174             dlg = this.get(dlg);
15175             if(dlg != front){
15176                 front = dlg;
15177                 dlg._lastAccess = new Date().getTime();
15178                 orderDialogs();
15179             }
15180             return dlg;
15181         },
15182
15183         /**
15184          * Sends the specified dialog to the back
15185          * @param {String/Object} dlg The id of the dialog or a dialog
15186          * @return {Roo.BasicDialog} this
15187          */
15188         sendToBack : function(dlg){
15189             dlg = this.get(dlg);
15190             dlg._lastAccess = -(new Date().getTime());
15191             orderDialogs();
15192             return dlg;
15193         },
15194
15195         /**
15196          * Hides all dialogs
15197          */
15198         hideAll : function(){
15199             for(var id in list){
15200                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15201                     list[id].hide();
15202                 }
15203             }
15204         }
15205     };
15206 }();
15207
15208 /**
15209  * @class Roo.LayoutDialog
15210  * @extends Roo.BasicDialog
15211  * Dialog which provides adjustments for working with a layout in a Dialog.
15212  * Add your necessary layout config options to the dialog's config.<br>
15213  * Example usage (including a nested layout):
15214  * <pre><code>
15215 if(!dialog){
15216     dialog = new Roo.LayoutDialog("download-dlg", {
15217         modal: true,
15218         width:600,
15219         height:450,
15220         shadow:true,
15221         minWidth:500,
15222         minHeight:350,
15223         autoTabs:true,
15224         proxyDrag:true,
15225         // layout config merges with the dialog config
15226         center:{
15227             tabPosition: "top",
15228             alwaysShowTabs: true
15229         }
15230     });
15231     dialog.addKeyListener(27, dialog.hide, dialog);
15232     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15233     dialog.addButton("Build It!", this.getDownload, this);
15234
15235     // we can even add nested layouts
15236     var innerLayout = new Roo.BorderLayout("dl-inner", {
15237         east: {
15238             initialSize: 200,
15239             autoScroll:true,
15240             split:true
15241         },
15242         center: {
15243             autoScroll:true
15244         }
15245     });
15246     innerLayout.beginUpdate();
15247     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15248     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15249     innerLayout.endUpdate(true);
15250
15251     var layout = dialog.getLayout();
15252     layout.beginUpdate();
15253     layout.add("center", new Roo.ContentPanel("standard-panel",
15254                         {title: "Download the Source", fitToFrame:true}));
15255     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15256                {title: "Build your own roo.js"}));
15257     layout.getRegion("center").showPanel(sp);
15258     layout.endUpdate();
15259 }
15260 </code></pre>
15261     * @constructor
15262     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15263     * @param {Object} config configuration options
15264   */
15265 Roo.LayoutDialog = function(el, cfg){
15266     
15267     var config=  cfg;
15268     if (typeof(cfg) == 'undefined') {
15269         config = Roo.apply({}, el);
15270         // not sure why we use documentElement here.. - it should always be body.
15271         // IE7 borks horribly if we use documentElement.
15272         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15273         //config.autoCreate = true;
15274     }
15275     
15276     
15277     config.autoTabs = false;
15278     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15279     this.body.setStyle({overflow:"hidden", position:"relative"});
15280     this.layout = new Roo.BorderLayout(this.body.dom, config);
15281     this.layout.monitorWindowResize = false;
15282     this.el.addClass("x-dlg-auto-layout");
15283     // fix case when center region overwrites center function
15284     this.center = Roo.BasicDialog.prototype.center;
15285     this.on("show", this.layout.layout, this.layout, true);
15286     if (config.items) {
15287         var xitems = config.items;
15288         delete config.items;
15289         Roo.each(xitems, this.addxtype, this);
15290     }
15291     
15292     
15293 };
15294 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15295     /**
15296      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15297      * @deprecated
15298      */
15299     endUpdate : function(){
15300         this.layout.endUpdate();
15301     },
15302
15303     /**
15304      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15305      *  @deprecated
15306      */
15307     beginUpdate : function(){
15308         this.layout.beginUpdate();
15309     },
15310
15311     /**
15312      * Get the BorderLayout for this dialog
15313      * @return {Roo.BorderLayout}
15314      */
15315     getLayout : function(){
15316         return this.layout;
15317     },
15318
15319     showEl : function(){
15320         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15321         if(Roo.isIE7){
15322             this.layout.layout();
15323         }
15324     },
15325
15326     // private
15327     // Use the syncHeightBeforeShow config option to control this automatically
15328     syncBodyHeight : function(){
15329         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15330         if(this.layout){this.layout.layout();}
15331     },
15332     
15333       /**
15334      * Add an xtype element (actually adds to the layout.)
15335      * @return {Object} xdata xtype object data.
15336      */
15337     
15338     addxtype : function(c) {
15339         return this.layout.addxtype(c);
15340     }
15341 });/*
15342  * Based on:
15343  * Ext JS Library 1.1.1
15344  * Copyright(c) 2006-2007, Ext JS, LLC.
15345  *
15346  * Originally Released Under LGPL - original licence link has changed is not relivant.
15347  *
15348  * Fork - LGPL
15349  * <script type="text/javascript">
15350  */
15351  
15352 /**
15353  * @class Roo.MessageBox
15354  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15355  * Example usage:
15356  *<pre><code>
15357 // Basic alert:
15358 Roo.Msg.alert('Status', 'Changes saved successfully.');
15359
15360 // Prompt for user data:
15361 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15362     if (btn == 'ok'){
15363         // process text value...
15364     }
15365 });
15366
15367 // Show a dialog using config options:
15368 Roo.Msg.show({
15369    title:'Save Changes?',
15370    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15371    buttons: Roo.Msg.YESNOCANCEL,
15372    fn: processResult,
15373    animEl: 'elId'
15374 });
15375 </code></pre>
15376  * @singleton
15377  */
15378 Roo.MessageBox = function(){
15379     var dlg, opt, mask, waitTimer;
15380     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15381     var buttons, activeTextEl, bwidth;
15382
15383     // private
15384     var handleButton = function(button){
15385         dlg.hide();
15386         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15387     };
15388
15389     // private
15390     var handleHide = function(){
15391         if(opt && opt.cls){
15392             dlg.el.removeClass(opt.cls);
15393         }
15394         if(waitTimer){
15395             Roo.TaskMgr.stop(waitTimer);
15396             waitTimer = null;
15397         }
15398     };
15399
15400     // private
15401     var updateButtons = function(b){
15402         var width = 0;
15403         if(!b){
15404             buttons["ok"].hide();
15405             buttons["cancel"].hide();
15406             buttons["yes"].hide();
15407             buttons["no"].hide();
15408             dlg.footer.dom.style.display = 'none';
15409             return width;
15410         }
15411         dlg.footer.dom.style.display = '';
15412         for(var k in buttons){
15413             if(typeof buttons[k] != "function"){
15414                 if(b[k]){
15415                     buttons[k].show();
15416                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15417                     width += buttons[k].el.getWidth()+15;
15418                 }else{
15419                     buttons[k].hide();
15420                 }
15421             }
15422         }
15423         return width;
15424     };
15425
15426     // private
15427     var handleEsc = function(d, k, e){
15428         if(opt && opt.closable !== false){
15429             dlg.hide();
15430         }
15431         if(e){
15432             e.stopEvent();
15433         }
15434     };
15435
15436     return {
15437         /**
15438          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15439          * @return {Roo.BasicDialog} The BasicDialog element
15440          */
15441         getDialog : function(){
15442            if(!dlg){
15443                 dlg = new Roo.BasicDialog("x-msg-box", {
15444                     autoCreate : true,
15445                     shadow: true,
15446                     draggable: true,
15447                     resizable:false,
15448                     constraintoviewport:false,
15449                     fixedcenter:true,
15450                     collapsible : false,
15451                     shim:true,
15452                     modal: true,
15453                     width:400, height:100,
15454                     buttonAlign:"center",
15455                     closeClick : function(){
15456                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15457                             handleButton("no");
15458                         }else{
15459                             handleButton("cancel");
15460                         }
15461                     }
15462                 });
15463                 dlg.on("hide", handleHide);
15464                 mask = dlg.mask;
15465                 dlg.addKeyListener(27, handleEsc);
15466                 buttons = {};
15467                 var bt = this.buttonText;
15468                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15469                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15470                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15471                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15472                 bodyEl = dlg.body.createChild({
15473
15474                     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>'
15475                 });
15476                 msgEl = bodyEl.dom.firstChild;
15477                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15478                 textboxEl.enableDisplayMode();
15479                 textboxEl.addKeyListener([10,13], function(){
15480                     if(dlg.isVisible() && opt && opt.buttons){
15481                         if(opt.buttons.ok){
15482                             handleButton("ok");
15483                         }else if(opt.buttons.yes){
15484                             handleButton("yes");
15485                         }
15486                     }
15487                 });
15488                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15489                 textareaEl.enableDisplayMode();
15490                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15491                 progressEl.enableDisplayMode();
15492                 var pf = progressEl.dom.firstChild;
15493                 if (pf) {
15494                     pp = Roo.get(pf.firstChild);
15495                     pp.setHeight(pf.offsetHeight);
15496                 }
15497                 
15498             }
15499             return dlg;
15500         },
15501
15502         /**
15503          * Updates the message box body text
15504          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15505          * the XHTML-compliant non-breaking space character '&amp;#160;')
15506          * @return {Roo.MessageBox} This message box
15507          */
15508         updateText : function(text){
15509             if(!dlg.isVisible() && !opt.width){
15510                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15511             }
15512             msgEl.innerHTML = text || '&#160;';
15513             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15514                         Math.max(opt.minWidth || this.minWidth, bwidth));
15515             if(opt.prompt){
15516                 activeTextEl.setWidth(w);
15517             }
15518             if(dlg.isVisible()){
15519                 dlg.fixedcenter = false;
15520             }
15521             dlg.setContentSize(w, bodyEl.getHeight());
15522             if(dlg.isVisible()){
15523                 dlg.fixedcenter = true;
15524             }
15525             return this;
15526         },
15527
15528         /**
15529          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15530          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15531          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15532          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15533          * @return {Roo.MessageBox} This message box
15534          */
15535         updateProgress : function(value, text){
15536             if(text){
15537                 this.updateText(text);
15538             }
15539             if (pp) { // weird bug on my firefox - for some reason this is not defined
15540                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15541             }
15542             return this;
15543         },        
15544
15545         /**
15546          * Returns true if the message box is currently displayed
15547          * @return {Boolean} True if the message box is visible, else false
15548          */
15549         isVisible : function(){
15550             return dlg && dlg.isVisible();  
15551         },
15552
15553         /**
15554          * Hides the message box if it is displayed
15555          */
15556         hide : function(){
15557             if(this.isVisible()){
15558                 dlg.hide();
15559             }  
15560         },
15561
15562         /**
15563          * Displays a new message box, or reinitializes an existing message box, based on the config options
15564          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15565          * The following config object properties are supported:
15566          * <pre>
15567 Property    Type             Description
15568 ----------  ---------------  ------------------------------------------------------------------------------------
15569 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15570                                    closes (defaults to undefined)
15571 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15572                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15573 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15574                                    progress and wait dialogs will ignore this property and always hide the
15575                                    close button as they can only be closed programmatically.
15576 cls               String           A custom CSS class to apply to the message box element
15577 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15578                                    displayed (defaults to 75)
15579 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15580                                    function will be btn (the name of the button that was clicked, if applicable,
15581                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15582                                    Progress and wait dialogs will ignore this option since they do not respond to
15583                                    user actions and can only be closed programmatically, so any required function
15584                                    should be called by the same code after it closes the dialog.
15585 icon              String           A CSS class that provides a background image to be used as an icon for
15586                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15587 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15588 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15589 modal             Boolean          False to allow user interaction with the page while the message box is
15590                                    displayed (defaults to true)
15591 msg               String           A string that will replace the existing message box body text (defaults
15592                                    to the XHTML-compliant non-breaking space character '&#160;')
15593 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15594 progress          Boolean          True to display a progress bar (defaults to false)
15595 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15596 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15597 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15598 title             String           The title text
15599 value             String           The string value to set into the active textbox element if displayed
15600 wait              Boolean          True to display a progress bar (defaults to false)
15601 width             Number           The width of the dialog in pixels
15602 </pre>
15603          *
15604          * Example usage:
15605          * <pre><code>
15606 Roo.Msg.show({
15607    title: 'Address',
15608    msg: 'Please enter your address:',
15609    width: 300,
15610    buttons: Roo.MessageBox.OKCANCEL,
15611    multiline: true,
15612    fn: saveAddress,
15613    animEl: 'addAddressBtn'
15614 });
15615 </code></pre>
15616          * @param {Object} config Configuration options
15617          * @return {Roo.MessageBox} This message box
15618          */
15619         show : function(options){
15620             if(this.isVisible()){
15621                 this.hide();
15622             }
15623             var d = this.getDialog();
15624             opt = options;
15625             d.setTitle(opt.title || "&#160;");
15626             d.close.setDisplayed(opt.closable !== false);
15627             activeTextEl = textboxEl;
15628             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15629             if(opt.prompt){
15630                 if(opt.multiline){
15631                     textboxEl.hide();
15632                     textareaEl.show();
15633                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15634                         opt.multiline : this.defaultTextHeight);
15635                     activeTextEl = textareaEl;
15636                 }else{
15637                     textboxEl.show();
15638                     textareaEl.hide();
15639                 }
15640             }else{
15641                 textboxEl.hide();
15642                 textareaEl.hide();
15643             }
15644             progressEl.setDisplayed(opt.progress === true);
15645             this.updateProgress(0);
15646             activeTextEl.dom.value = opt.value || "";
15647             if(opt.prompt){
15648                 dlg.setDefaultButton(activeTextEl);
15649             }else{
15650                 var bs = opt.buttons;
15651                 var db = null;
15652                 if(bs && bs.ok){
15653                     db = buttons["ok"];
15654                 }else if(bs && bs.yes){
15655                     db = buttons["yes"];
15656                 }
15657                 dlg.setDefaultButton(db);
15658             }
15659             bwidth = updateButtons(opt.buttons);
15660             this.updateText(opt.msg);
15661             if(opt.cls){
15662                 d.el.addClass(opt.cls);
15663             }
15664             d.proxyDrag = opt.proxyDrag === true;
15665             d.modal = opt.modal !== false;
15666             d.mask = opt.modal !== false ? mask : false;
15667             if(!d.isVisible()){
15668                 // force it to the end of the z-index stack so it gets a cursor in FF
15669                 document.body.appendChild(dlg.el.dom);
15670                 d.animateTarget = null;
15671                 d.show(options.animEl);
15672             }
15673             return this;
15674         },
15675
15676         /**
15677          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15678          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15679          * and closing the message box when the process is complete.
15680          * @param {String} title The title bar text
15681          * @param {String} msg The message box body text
15682          * @return {Roo.MessageBox} This message box
15683          */
15684         progress : function(title, msg){
15685             this.show({
15686                 title : title,
15687                 msg : msg,
15688                 buttons: false,
15689                 progress:true,
15690                 closable:false,
15691                 minWidth: this.minProgressWidth,
15692                 modal : true
15693             });
15694             return this;
15695         },
15696
15697         /**
15698          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15699          * If a callback function is passed it will be called after the user clicks the button, and the
15700          * id of the button that was clicked will be passed as the only parameter to the callback
15701          * (could also be the top-right close button).
15702          * @param {String} title The title bar text
15703          * @param {String} msg The message box body text
15704          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15705          * @param {Object} scope (optional) The scope of the callback function
15706          * @return {Roo.MessageBox} This message box
15707          */
15708         alert : function(title, msg, fn, scope){
15709             this.show({
15710                 title : title,
15711                 msg : msg,
15712                 buttons: this.OK,
15713                 fn: fn,
15714                 scope : scope,
15715                 modal : true
15716             });
15717             return this;
15718         },
15719
15720         /**
15721          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15722          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15723          * You are responsible for closing the message box when the process is complete.
15724          * @param {String} msg The message box body text
15725          * @param {String} title (optional) The title bar text
15726          * @return {Roo.MessageBox} This message box
15727          */
15728         wait : function(msg, title){
15729             this.show({
15730                 title : title,
15731                 msg : msg,
15732                 buttons: false,
15733                 closable:false,
15734                 progress:true,
15735                 modal:true,
15736                 width:300,
15737                 wait:true
15738             });
15739             waitTimer = Roo.TaskMgr.start({
15740                 run: function(i){
15741                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15742                 },
15743                 interval: 1000
15744             });
15745             return this;
15746         },
15747
15748         /**
15749          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15750          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15751          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15752          * @param {String} title The title bar text
15753          * @param {String} msg The message box body text
15754          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15755          * @param {Object} scope (optional) The scope of the callback function
15756          * @return {Roo.MessageBox} This message box
15757          */
15758         confirm : function(title, msg, fn, scope){
15759             this.show({
15760                 title : title,
15761                 msg : msg,
15762                 buttons: this.YESNO,
15763                 fn: fn,
15764                 scope : scope,
15765                 modal : true
15766             });
15767             return this;
15768         },
15769
15770         /**
15771          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15772          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15773          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15774          * (could also be the top-right close button) and the text that was entered will be passed as the two
15775          * parameters to the callback.
15776          * @param {String} title The title bar text
15777          * @param {String} msg The message box body text
15778          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15779          * @param {Object} scope (optional) The scope of the callback function
15780          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15781          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15782          * @return {Roo.MessageBox} This message box
15783          */
15784         prompt : function(title, msg, fn, scope, multiline){
15785             this.show({
15786                 title : title,
15787                 msg : msg,
15788                 buttons: this.OKCANCEL,
15789                 fn: fn,
15790                 minWidth:250,
15791                 scope : scope,
15792                 prompt:true,
15793                 multiline: multiline,
15794                 modal : true
15795             });
15796             return this;
15797         },
15798
15799         /**
15800          * Button config that displays a single OK button
15801          * @type Object
15802          */
15803         OK : {ok:true},
15804         /**
15805          * Button config that displays Yes and No buttons
15806          * @type Object
15807          */
15808         YESNO : {yes:true, no:true},
15809         /**
15810          * Button config that displays OK and Cancel buttons
15811          * @type Object
15812          */
15813         OKCANCEL : {ok:true, cancel:true},
15814         /**
15815          * Button config that displays Yes, No and Cancel buttons
15816          * @type Object
15817          */
15818         YESNOCANCEL : {yes:true, no:true, cancel:true},
15819
15820         /**
15821          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15822          * @type Number
15823          */
15824         defaultTextHeight : 75,
15825         /**
15826          * The maximum width in pixels of the message box (defaults to 600)
15827          * @type Number
15828          */
15829         maxWidth : 600,
15830         /**
15831          * The minimum width in pixels of the message box (defaults to 100)
15832          * @type Number
15833          */
15834         minWidth : 100,
15835         /**
15836          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15837          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15838          * @type Number
15839          */
15840         minProgressWidth : 250,
15841         /**
15842          * An object containing the default button text strings that can be overriden for localized language support.
15843          * Supported properties are: ok, cancel, yes and no.
15844          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15845          * @type Object
15846          */
15847         buttonText : {
15848             ok : "OK",
15849             cancel : "Cancel",
15850             yes : "Yes",
15851             no : "No"
15852         }
15853     };
15854 }();
15855
15856 /**
15857  * Shorthand for {@link Roo.MessageBox}
15858  */
15859 Roo.Msg = Roo.MessageBox;/*
15860  * Based on:
15861  * Ext JS Library 1.1.1
15862  * Copyright(c) 2006-2007, Ext JS, LLC.
15863  *
15864  * Originally Released Under LGPL - original licence link has changed is not relivant.
15865  *
15866  * Fork - LGPL
15867  * <script type="text/javascript">
15868  */
15869 /**
15870  * @class Roo.QuickTips
15871  * Provides attractive and customizable tooltips for any element.
15872  * @singleton
15873  */
15874 Roo.QuickTips = function(){
15875     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15876     var ce, bd, xy, dd;
15877     var visible = false, disabled = true, inited = false;
15878     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15879     
15880     var onOver = function(e){
15881         if(disabled){
15882             return;
15883         }
15884         var t = e.getTarget();
15885         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15886             return;
15887         }
15888         if(ce && t == ce.el){
15889             clearTimeout(hideProc);
15890             return;
15891         }
15892         if(t && tagEls[t.id]){
15893             tagEls[t.id].el = t;
15894             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15895             return;
15896         }
15897         var ttp, et = Roo.fly(t);
15898         var ns = cfg.namespace;
15899         if(tm.interceptTitles && t.title){
15900             ttp = t.title;
15901             t.qtip = ttp;
15902             t.removeAttribute("title");
15903             e.preventDefault();
15904         }else{
15905             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15906         }
15907         if(ttp){
15908             showProc = show.defer(tm.showDelay, tm, [{
15909                 el: t, 
15910                 text: ttp, 
15911                 width: et.getAttributeNS(ns, cfg.width),
15912                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15913                 title: et.getAttributeNS(ns, cfg.title),
15914                     cls: et.getAttributeNS(ns, cfg.cls)
15915             }]);
15916         }
15917     };
15918     
15919     var onOut = function(e){
15920         clearTimeout(showProc);
15921         var t = e.getTarget();
15922         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15923             hideProc = setTimeout(hide, tm.hideDelay);
15924         }
15925     };
15926     
15927     var onMove = function(e){
15928         if(disabled){
15929             return;
15930         }
15931         xy = e.getXY();
15932         xy[1] += 18;
15933         if(tm.trackMouse && ce){
15934             el.setXY(xy);
15935         }
15936     };
15937     
15938     var onDown = function(e){
15939         clearTimeout(showProc);
15940         clearTimeout(hideProc);
15941         if(!e.within(el)){
15942             if(tm.hideOnClick){
15943                 hide();
15944                 tm.disable();
15945                 tm.enable.defer(100, tm);
15946             }
15947         }
15948     };
15949     
15950     var getPad = function(){
15951         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15952     };
15953
15954     var show = function(o){
15955         if(disabled){
15956             return;
15957         }
15958         clearTimeout(dismissProc);
15959         ce = o;
15960         if(removeCls){ // in case manually hidden
15961             el.removeClass(removeCls);
15962             removeCls = null;
15963         }
15964         if(ce.cls){
15965             el.addClass(ce.cls);
15966             removeCls = ce.cls;
15967         }
15968         if(ce.title){
15969             tipTitle.update(ce.title);
15970             tipTitle.show();
15971         }else{
15972             tipTitle.update('');
15973             tipTitle.hide();
15974         }
15975         el.dom.style.width  = tm.maxWidth+'px';
15976         //tipBody.dom.style.width = '';
15977         tipBodyText.update(o.text);
15978         var p = getPad(), w = ce.width;
15979         if(!w){
15980             var td = tipBodyText.dom;
15981             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15982             if(aw > tm.maxWidth){
15983                 w = tm.maxWidth;
15984             }else if(aw < tm.minWidth){
15985                 w = tm.minWidth;
15986             }else{
15987                 w = aw;
15988             }
15989         }
15990         //tipBody.setWidth(w);
15991         el.setWidth(parseInt(w, 10) + p);
15992         if(ce.autoHide === false){
15993             close.setDisplayed(true);
15994             if(dd){
15995                 dd.unlock();
15996             }
15997         }else{
15998             close.setDisplayed(false);
15999             if(dd){
16000                 dd.lock();
16001             }
16002         }
16003         if(xy){
16004             el.avoidY = xy[1]-18;
16005             el.setXY(xy);
16006         }
16007         if(tm.animate){
16008             el.setOpacity(.1);
16009             el.setStyle("visibility", "visible");
16010             el.fadeIn({callback: afterShow});
16011         }else{
16012             afterShow();
16013         }
16014     };
16015     
16016     var afterShow = function(){
16017         if(ce){
16018             el.show();
16019             esc.enable();
16020             if(tm.autoDismiss && ce.autoHide !== false){
16021                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16022             }
16023         }
16024     };
16025     
16026     var hide = function(noanim){
16027         clearTimeout(dismissProc);
16028         clearTimeout(hideProc);
16029         ce = null;
16030         if(el.isVisible()){
16031             esc.disable();
16032             if(noanim !== true && tm.animate){
16033                 el.fadeOut({callback: afterHide});
16034             }else{
16035                 afterHide();
16036             } 
16037         }
16038     };
16039     
16040     var afterHide = function(){
16041         el.hide();
16042         if(removeCls){
16043             el.removeClass(removeCls);
16044             removeCls = null;
16045         }
16046     };
16047     
16048     return {
16049         /**
16050         * @cfg {Number} minWidth
16051         * The minimum width of the quick tip (defaults to 40)
16052         */
16053        minWidth : 40,
16054         /**
16055         * @cfg {Number} maxWidth
16056         * The maximum width of the quick tip (defaults to 300)
16057         */
16058        maxWidth : 300,
16059         /**
16060         * @cfg {Boolean} interceptTitles
16061         * True to automatically use the element's DOM title value if available (defaults to false)
16062         */
16063        interceptTitles : false,
16064         /**
16065         * @cfg {Boolean} trackMouse
16066         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16067         */
16068        trackMouse : false,
16069         /**
16070         * @cfg {Boolean} hideOnClick
16071         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16072         */
16073        hideOnClick : true,
16074         /**
16075         * @cfg {Number} showDelay
16076         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16077         */
16078        showDelay : 500,
16079         /**
16080         * @cfg {Number} hideDelay
16081         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16082         */
16083        hideDelay : 200,
16084         /**
16085         * @cfg {Boolean} autoHide
16086         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16087         * Used in conjunction with hideDelay.
16088         */
16089        autoHide : true,
16090         /**
16091         * @cfg {Boolean}
16092         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16093         * (defaults to true).  Used in conjunction with autoDismissDelay.
16094         */
16095        autoDismiss : true,
16096         /**
16097         * @cfg {Number}
16098         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16099         */
16100        autoDismissDelay : 5000,
16101        /**
16102         * @cfg {Boolean} animate
16103         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16104         */
16105        animate : false,
16106
16107        /**
16108         * @cfg {String} title
16109         * Title text to display (defaults to '').  This can be any valid HTML markup.
16110         */
16111         title: '',
16112        /**
16113         * @cfg {String} text
16114         * Body text to display (defaults to '').  This can be any valid HTML markup.
16115         */
16116         text : '',
16117        /**
16118         * @cfg {String} cls
16119         * A CSS class to apply to the base quick tip element (defaults to '').
16120         */
16121         cls : '',
16122        /**
16123         * @cfg {Number} width
16124         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16125         * minWidth or maxWidth.
16126         */
16127         width : null,
16128
16129     /**
16130      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16131      * or display QuickTips in a page.
16132      */
16133        init : function(){
16134           tm = Roo.QuickTips;
16135           cfg = tm.tagConfig;
16136           if(!inited){
16137               if(!Roo.isReady){ // allow calling of init() before onReady
16138                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16139                   return;
16140               }
16141               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16142               el.fxDefaults = {stopFx: true};
16143               // maximum custom styling
16144               //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>');
16145               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>');              
16146               tipTitle = el.child('h3');
16147               tipTitle.enableDisplayMode("block");
16148               tipBody = el.child('div.x-tip-bd');
16149               tipBodyText = el.child('div.x-tip-bd-inner');
16150               //bdLeft = el.child('div.x-tip-bd-left');
16151               //bdRight = el.child('div.x-tip-bd-right');
16152               close = el.child('div.x-tip-close');
16153               close.enableDisplayMode("block");
16154               close.on("click", hide);
16155               var d = Roo.get(document);
16156               d.on("mousedown", onDown);
16157               d.on("mouseover", onOver);
16158               d.on("mouseout", onOut);
16159               d.on("mousemove", onMove);
16160               esc = d.addKeyListener(27, hide);
16161               esc.disable();
16162               if(Roo.dd.DD){
16163                   dd = el.initDD("default", null, {
16164                       onDrag : function(){
16165                           el.sync();  
16166                       }
16167                   });
16168                   dd.setHandleElId(tipTitle.id);
16169                   dd.lock();
16170               }
16171               inited = true;
16172           }
16173           this.enable(); 
16174        },
16175
16176     /**
16177      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16178      * are supported:
16179      * <pre>
16180 Property    Type                   Description
16181 ----------  ---------------------  ------------------------------------------------------------------------
16182 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16183      * </ul>
16184      * @param {Object} config The config object
16185      */
16186        register : function(config){
16187            var cs = config instanceof Array ? config : arguments;
16188            for(var i = 0, len = cs.length; i < len; i++) {
16189                var c = cs[i];
16190                var target = c.target;
16191                if(target){
16192                    if(target instanceof Array){
16193                        for(var j = 0, jlen = target.length; j < jlen; j++){
16194                            tagEls[target[j]] = c;
16195                        }
16196                    }else{
16197                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16198                    }
16199                }
16200            }
16201        },
16202
16203     /**
16204      * Removes this quick tip from its element and destroys it.
16205      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16206      */
16207        unregister : function(el){
16208            delete tagEls[Roo.id(el)];
16209        },
16210
16211     /**
16212      * Enable this quick tip.
16213      */
16214        enable : function(){
16215            if(inited && disabled){
16216                locks.pop();
16217                if(locks.length < 1){
16218                    disabled = false;
16219                }
16220            }
16221        },
16222
16223     /**
16224      * Disable this quick tip.
16225      */
16226        disable : function(){
16227           disabled = true;
16228           clearTimeout(showProc);
16229           clearTimeout(hideProc);
16230           clearTimeout(dismissProc);
16231           if(ce){
16232               hide(true);
16233           }
16234           locks.push(1);
16235        },
16236
16237     /**
16238      * Returns true if the quick tip is enabled, else false.
16239      */
16240        isEnabled : function(){
16241             return !disabled;
16242        },
16243
16244         // private
16245        tagConfig : {
16246            namespace : "ext",
16247            attribute : "qtip",
16248            width : "width",
16249            target : "target",
16250            title : "qtitle",
16251            hide : "hide",
16252            cls : "qclass"
16253        }
16254    };
16255 }();
16256
16257 // backwards compat
16258 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16259  * Based on:
16260  * Ext JS Library 1.1.1
16261  * Copyright(c) 2006-2007, Ext JS, LLC.
16262  *
16263  * Originally Released Under LGPL - original licence link has changed is not relivant.
16264  *
16265  * Fork - LGPL
16266  * <script type="text/javascript">
16267  */
16268  
16269
16270 /**
16271  * @class Roo.tree.TreePanel
16272  * @extends Roo.data.Tree
16273
16274  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16275  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16276  * @cfg {Boolean} enableDD true to enable drag and drop
16277  * @cfg {Boolean} enableDrag true to enable just drag
16278  * @cfg {Boolean} enableDrop true to enable just drop
16279  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16280  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16281  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16282  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16283  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16284  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16285  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16286  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16287  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16288  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16289  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16290  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16291  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16292  * @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>
16293  * @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>
16294  * 
16295  * @constructor
16296  * @param {String/HTMLElement/Element} el The container element
16297  * @param {Object} config
16298  */
16299 Roo.tree.TreePanel = function(el, config){
16300     var root = false;
16301     var loader = false;
16302     if (config.root) {
16303         root = config.root;
16304         delete config.root;
16305     }
16306     if (config.loader) {
16307         loader = config.loader;
16308         delete config.loader;
16309     }
16310     
16311     Roo.apply(this, config);
16312     Roo.tree.TreePanel.superclass.constructor.call(this);
16313     this.el = Roo.get(el);
16314     this.el.addClass('x-tree');
16315     //console.log(root);
16316     if (root) {
16317         this.setRootNode( Roo.factory(root, Roo.tree));
16318     }
16319     if (loader) {
16320         this.loader = Roo.factory(loader, Roo.tree);
16321     }
16322    /**
16323     * Read-only. The id of the container element becomes this TreePanel's id.
16324     */
16325    this.id = this.el.id;
16326    this.addEvents({
16327         /**
16328         * @event beforeload
16329         * Fires before a node is loaded, return false to cancel
16330         * @param {Node} node The node being loaded
16331         */
16332         "beforeload" : true,
16333         /**
16334         * @event load
16335         * Fires when a node is loaded
16336         * @param {Node} node The node that was loaded
16337         */
16338         "load" : true,
16339         /**
16340         * @event textchange
16341         * Fires when the text for a node is changed
16342         * @param {Node} node The node
16343         * @param {String} text The new text
16344         * @param {String} oldText The old text
16345         */
16346         "textchange" : true,
16347         /**
16348         * @event beforeexpand
16349         * Fires before a node is expanded, return false to cancel.
16350         * @param {Node} node The node
16351         * @param {Boolean} deep
16352         * @param {Boolean} anim
16353         */
16354         "beforeexpand" : true,
16355         /**
16356         * @event beforecollapse
16357         * Fires before a node is collapsed, return false to cancel.
16358         * @param {Node} node The node
16359         * @param {Boolean} deep
16360         * @param {Boolean} anim
16361         */
16362         "beforecollapse" : true,
16363         /**
16364         * @event expand
16365         * Fires when a node is expanded
16366         * @param {Node} node The node
16367         */
16368         "expand" : true,
16369         /**
16370         * @event disabledchange
16371         * Fires when the disabled status of a node changes
16372         * @param {Node} node The node
16373         * @param {Boolean} disabled
16374         */
16375         "disabledchange" : true,
16376         /**
16377         * @event collapse
16378         * Fires when a node is collapsed
16379         * @param {Node} node The node
16380         */
16381         "collapse" : true,
16382         /**
16383         * @event beforeclick
16384         * Fires before click processing on a node. Return false to cancel the default action.
16385         * @param {Node} node The node
16386         * @param {Roo.EventObject} e The event object
16387         */
16388         "beforeclick":true,
16389         /**
16390         * @event checkchange
16391         * Fires when a node with a checkbox's checked property changes
16392         * @param {Node} this This node
16393         * @param {Boolean} checked
16394         */
16395         "checkchange":true,
16396         /**
16397         * @event click
16398         * Fires when a node is clicked
16399         * @param {Node} node The node
16400         * @param {Roo.EventObject} e The event object
16401         */
16402         "click":true,
16403         /**
16404         * @event dblclick
16405         * Fires when a node is double clicked
16406         * @param {Node} node The node
16407         * @param {Roo.EventObject} e The event object
16408         */
16409         "dblclick":true,
16410         /**
16411         * @event contextmenu
16412         * Fires when a node is right clicked
16413         * @param {Node} node The node
16414         * @param {Roo.EventObject} e The event object
16415         */
16416         "contextmenu":true,
16417         /**
16418         * @event beforechildrenrendered
16419         * Fires right before the child nodes for a node are rendered
16420         * @param {Node} node The node
16421         */
16422         "beforechildrenrendered":true,
16423        /**
16424              * @event startdrag
16425              * Fires when a node starts being dragged
16426              * @param {Roo.tree.TreePanel} this
16427              * @param {Roo.tree.TreeNode} node
16428              * @param {event} e The raw browser event
16429              */ 
16430             "startdrag" : true,
16431             /**
16432              * @event enddrag
16433              * Fires when a drag operation is complete
16434              * @param {Roo.tree.TreePanel} this
16435              * @param {Roo.tree.TreeNode} node
16436              * @param {event} e The raw browser event
16437              */
16438             "enddrag" : true,
16439             /**
16440              * @event dragdrop
16441              * Fires when a dragged node is dropped on a valid DD target
16442              * @param {Roo.tree.TreePanel} this
16443              * @param {Roo.tree.TreeNode} node
16444              * @param {DD} dd The dd it was dropped on
16445              * @param {event} e The raw browser event
16446              */
16447             "dragdrop" : true,
16448             /**
16449              * @event beforenodedrop
16450              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16451              * passed to handlers has the following properties:<br />
16452              * <ul style="padding:5px;padding-left:16px;">
16453              * <li>tree - The TreePanel</li>
16454              * <li>target - The node being targeted for the drop</li>
16455              * <li>data - The drag data from the drag source</li>
16456              * <li>point - The point of the drop - append, above or below</li>
16457              * <li>source - The drag source</li>
16458              * <li>rawEvent - Raw mouse event</li>
16459              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16460              * to be inserted by setting them on this object.</li>
16461              * <li>cancel - Set this to true to cancel the drop.</li>
16462              * </ul>
16463              * @param {Object} dropEvent
16464              */
16465             "beforenodedrop" : true,
16466             /**
16467              * @event nodedrop
16468              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16469              * passed to handlers has the following properties:<br />
16470              * <ul style="padding:5px;padding-left:16px;">
16471              * <li>tree - The TreePanel</li>
16472              * <li>target - The node being targeted for the drop</li>
16473              * <li>data - The drag data from the drag source</li>
16474              * <li>point - The point of the drop - append, above or below</li>
16475              * <li>source - The drag source</li>
16476              * <li>rawEvent - Raw mouse event</li>
16477              * <li>dropNode - Dropped node(s).</li>
16478              * </ul>
16479              * @param {Object} dropEvent
16480              */
16481             "nodedrop" : true,
16482              /**
16483              * @event nodedragover
16484              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16485              * passed to handlers has the following properties:<br />
16486              * <ul style="padding:5px;padding-left:16px;">
16487              * <li>tree - The TreePanel</li>
16488              * <li>target - The node being targeted for the drop</li>
16489              * <li>data - The drag data from the drag source</li>
16490              * <li>point - The point of the drop - append, above or below</li>
16491              * <li>source - The drag source</li>
16492              * <li>rawEvent - Raw mouse event</li>
16493              * <li>dropNode - Drop node(s) provided by the source.</li>
16494              * <li>cancel - Set this to true to signal drop not allowed.</li>
16495              * </ul>
16496              * @param {Object} dragOverEvent
16497              */
16498             "nodedragover" : true
16499         
16500    });
16501    if(this.singleExpand){
16502        this.on("beforeexpand", this.restrictExpand, this);
16503    }
16504 };
16505 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16506     rootVisible : true,
16507     animate: Roo.enableFx,
16508     lines : true,
16509     enableDD : false,
16510     hlDrop : Roo.enableFx,
16511   
16512     renderer: false,
16513     
16514     rendererTip: false,
16515     // private
16516     restrictExpand : function(node){
16517         var p = node.parentNode;
16518         if(p){
16519             if(p.expandedChild && p.expandedChild.parentNode == p){
16520                 p.expandedChild.collapse();
16521             }
16522             p.expandedChild = node;
16523         }
16524     },
16525
16526     // private override
16527     setRootNode : function(node){
16528         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16529         if(!this.rootVisible){
16530             node.ui = new Roo.tree.RootTreeNodeUI(node);
16531         }
16532         return node;
16533     },
16534
16535     /**
16536      * Returns the container element for this TreePanel
16537      */
16538     getEl : function(){
16539         return this.el;
16540     },
16541
16542     /**
16543      * Returns the default TreeLoader for this TreePanel
16544      */
16545     getLoader : function(){
16546         return this.loader;
16547     },
16548
16549     /**
16550      * Expand all nodes
16551      */
16552     expandAll : function(){
16553         this.root.expand(true);
16554     },
16555
16556     /**
16557      * Collapse all nodes
16558      */
16559     collapseAll : function(){
16560         this.root.collapse(true);
16561     },
16562
16563     /**
16564      * Returns the selection model used by this TreePanel
16565      */
16566     getSelectionModel : function(){
16567         if(!this.selModel){
16568             this.selModel = new Roo.tree.DefaultSelectionModel();
16569         }
16570         return this.selModel;
16571     },
16572
16573     /**
16574      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16575      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16576      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16577      * @return {Array}
16578      */
16579     getChecked : function(a, startNode){
16580         startNode = startNode || this.root;
16581         var r = [];
16582         var f = function(){
16583             if(this.attributes.checked){
16584                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16585             }
16586         }
16587         startNode.cascade(f);
16588         return r;
16589     },
16590
16591     /**
16592      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16593      * @param {String} path
16594      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16595      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16596      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16597      */
16598     expandPath : function(path, attr, callback){
16599         attr = attr || "id";
16600         var keys = path.split(this.pathSeparator);
16601         var curNode = this.root;
16602         if(curNode.attributes[attr] != keys[1]){ // invalid root
16603             if(callback){
16604                 callback(false, null);
16605             }
16606             return;
16607         }
16608         var index = 1;
16609         var f = function(){
16610             if(++index == keys.length){
16611                 if(callback){
16612                     callback(true, curNode);
16613                 }
16614                 return;
16615             }
16616             var c = curNode.findChild(attr, keys[index]);
16617             if(!c){
16618                 if(callback){
16619                     callback(false, curNode);
16620                 }
16621                 return;
16622             }
16623             curNode = c;
16624             c.expand(false, false, f);
16625         };
16626         curNode.expand(false, false, f);
16627     },
16628
16629     /**
16630      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16631      * @param {String} path
16632      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16633      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16634      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16635      */
16636     selectPath : function(path, attr, callback){
16637         attr = attr || "id";
16638         var keys = path.split(this.pathSeparator);
16639         var v = keys.pop();
16640         if(keys.length > 0){
16641             var f = function(success, node){
16642                 if(success && node){
16643                     var n = node.findChild(attr, v);
16644                     if(n){
16645                         n.select();
16646                         if(callback){
16647                             callback(true, n);
16648                         }
16649                     }else if(callback){
16650                         callback(false, n);
16651                     }
16652                 }else{
16653                     if(callback){
16654                         callback(false, n);
16655                     }
16656                 }
16657             };
16658             this.expandPath(keys.join(this.pathSeparator), attr, f);
16659         }else{
16660             this.root.select();
16661             if(callback){
16662                 callback(true, this.root);
16663             }
16664         }
16665     },
16666
16667     getTreeEl : function(){
16668         return this.el;
16669     },
16670
16671     /**
16672      * Trigger rendering of this TreePanel
16673      */
16674     render : function(){
16675         if (this.innerCt) {
16676             return this; // stop it rendering more than once!!
16677         }
16678         
16679         this.innerCt = this.el.createChild({tag:"ul",
16680                cls:"x-tree-root-ct " +
16681                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16682
16683         if(this.containerScroll){
16684             Roo.dd.ScrollManager.register(this.el);
16685         }
16686         if((this.enableDD || this.enableDrop) && !this.dropZone){
16687            /**
16688             * The dropZone used by this tree if drop is enabled
16689             * @type Roo.tree.TreeDropZone
16690             */
16691              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16692                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16693            });
16694         }
16695         if((this.enableDD || this.enableDrag) && !this.dragZone){
16696            /**
16697             * The dragZone used by this tree if drag is enabled
16698             * @type Roo.tree.TreeDragZone
16699             */
16700             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16701                ddGroup: this.ddGroup || "TreeDD",
16702                scroll: this.ddScroll
16703            });
16704         }
16705         this.getSelectionModel().init(this);
16706         if (!this.root) {
16707             console.log("ROOT not set in tree");
16708             return;
16709         }
16710         this.root.render();
16711         if(!this.rootVisible){
16712             this.root.renderChildren();
16713         }
16714         return this;
16715     }
16716 });/*
16717  * Based on:
16718  * Ext JS Library 1.1.1
16719  * Copyright(c) 2006-2007, Ext JS, LLC.
16720  *
16721  * Originally Released Under LGPL - original licence link has changed is not relivant.
16722  *
16723  * Fork - LGPL
16724  * <script type="text/javascript">
16725  */
16726  
16727
16728 /**
16729  * @class Roo.tree.DefaultSelectionModel
16730  * @extends Roo.util.Observable
16731  * The default single selection for a TreePanel.
16732  */
16733 Roo.tree.DefaultSelectionModel = function(){
16734    this.selNode = null;
16735    
16736    this.addEvents({
16737        /**
16738         * @event selectionchange
16739         * Fires when the selected node changes
16740         * @param {DefaultSelectionModel} this
16741         * @param {TreeNode} node the new selection
16742         */
16743        "selectionchange" : true,
16744
16745        /**
16746         * @event beforeselect
16747         * Fires before the selected node changes, return false to cancel the change
16748         * @param {DefaultSelectionModel} this
16749         * @param {TreeNode} node the new selection
16750         * @param {TreeNode} node the old selection
16751         */
16752        "beforeselect" : true
16753    });
16754 };
16755
16756 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16757     init : function(tree){
16758         this.tree = tree;
16759         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16760         tree.on("click", this.onNodeClick, this);
16761     },
16762     
16763     onNodeClick : function(node, e){
16764         if (e.ctrlKey && this.selNode == node)  {
16765             this.unselect(node);
16766             return;
16767         }
16768         this.select(node);
16769     },
16770     
16771     /**
16772      * Select a node.
16773      * @param {TreeNode} node The node to select
16774      * @return {TreeNode} The selected node
16775      */
16776     select : function(node){
16777         var last = this.selNode;
16778         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16779             if(last){
16780                 last.ui.onSelectedChange(false);
16781             }
16782             this.selNode = node;
16783             node.ui.onSelectedChange(true);
16784             this.fireEvent("selectionchange", this, node, last);
16785         }
16786         return node;
16787     },
16788     
16789     /**
16790      * Deselect a node.
16791      * @param {TreeNode} node The node to unselect
16792      */
16793     unselect : function(node){
16794         if(this.selNode == node){
16795             this.clearSelections();
16796         }    
16797     },
16798     
16799     /**
16800      * Clear all selections
16801      */
16802     clearSelections : function(){
16803         var n = this.selNode;
16804         if(n){
16805             n.ui.onSelectedChange(false);
16806             this.selNode = null;
16807             this.fireEvent("selectionchange", this, null);
16808         }
16809         return n;
16810     },
16811     
16812     /**
16813      * Get the selected node
16814      * @return {TreeNode} The selected node
16815      */
16816     getSelectedNode : function(){
16817         return this.selNode;    
16818     },
16819     
16820     /**
16821      * Returns true if the node is selected
16822      * @param {TreeNode} node The node to check
16823      * @return {Boolean}
16824      */
16825     isSelected : function(node){
16826         return this.selNode == node;  
16827     },
16828
16829     /**
16830      * Selects the node above the selected node in the tree, intelligently walking the nodes
16831      * @return TreeNode The new selection
16832      */
16833     selectPrevious : function(){
16834         var s = this.selNode || this.lastSelNode;
16835         if(!s){
16836             return null;
16837         }
16838         var ps = s.previousSibling;
16839         if(ps){
16840             if(!ps.isExpanded() || ps.childNodes.length < 1){
16841                 return this.select(ps);
16842             } else{
16843                 var lc = ps.lastChild;
16844                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16845                     lc = lc.lastChild;
16846                 }
16847                 return this.select(lc);
16848             }
16849         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16850             return this.select(s.parentNode);
16851         }
16852         return null;
16853     },
16854
16855     /**
16856      * Selects the node above the selected node in the tree, intelligently walking the nodes
16857      * @return TreeNode The new selection
16858      */
16859     selectNext : function(){
16860         var s = this.selNode || this.lastSelNode;
16861         if(!s){
16862             return null;
16863         }
16864         if(s.firstChild && s.isExpanded()){
16865              return this.select(s.firstChild);
16866          }else if(s.nextSibling){
16867              return this.select(s.nextSibling);
16868          }else if(s.parentNode){
16869             var newS = null;
16870             s.parentNode.bubble(function(){
16871                 if(this.nextSibling){
16872                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16873                     return false;
16874                 }
16875             });
16876             return newS;
16877          }
16878         return null;
16879     },
16880
16881     onKeyDown : function(e){
16882         var s = this.selNode || this.lastSelNode;
16883         // undesirable, but required
16884         var sm = this;
16885         if(!s){
16886             return;
16887         }
16888         var k = e.getKey();
16889         switch(k){
16890              case e.DOWN:
16891                  e.stopEvent();
16892                  this.selectNext();
16893              break;
16894              case e.UP:
16895                  e.stopEvent();
16896                  this.selectPrevious();
16897              break;
16898              case e.RIGHT:
16899                  e.preventDefault();
16900                  if(s.hasChildNodes()){
16901                      if(!s.isExpanded()){
16902                          s.expand();
16903                      }else if(s.firstChild){
16904                          this.select(s.firstChild, e);
16905                      }
16906                  }
16907              break;
16908              case e.LEFT:
16909                  e.preventDefault();
16910                  if(s.hasChildNodes() && s.isExpanded()){
16911                      s.collapse();
16912                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16913                      this.select(s.parentNode, e);
16914                  }
16915              break;
16916         };
16917     }
16918 });
16919
16920 /**
16921  * @class Roo.tree.MultiSelectionModel
16922  * @extends Roo.util.Observable
16923  * Multi selection for a TreePanel.
16924  */
16925 Roo.tree.MultiSelectionModel = function(){
16926    this.selNodes = [];
16927    this.selMap = {};
16928    this.addEvents({
16929        /**
16930         * @event selectionchange
16931         * Fires when the selected nodes change
16932         * @param {MultiSelectionModel} this
16933         * @param {Array} nodes Array of the selected nodes
16934         */
16935        "selectionchange" : true
16936    });
16937 };
16938
16939 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16940     init : function(tree){
16941         this.tree = tree;
16942         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16943         tree.on("click", this.onNodeClick, this);
16944     },
16945     
16946     onNodeClick : function(node, e){
16947         this.select(node, e, e.ctrlKey);
16948     },
16949     
16950     /**
16951      * Select a node.
16952      * @param {TreeNode} node The node to select
16953      * @param {EventObject} e (optional) An event associated with the selection
16954      * @param {Boolean} keepExisting True to retain existing selections
16955      * @return {TreeNode} The selected node
16956      */
16957     select : function(node, e, keepExisting){
16958         if(keepExisting !== true){
16959             this.clearSelections(true);
16960         }
16961         if(this.isSelected(node)){
16962             this.lastSelNode = node;
16963             return node;
16964         }
16965         this.selNodes.push(node);
16966         this.selMap[node.id] = node;
16967         this.lastSelNode = node;
16968         node.ui.onSelectedChange(true);
16969         this.fireEvent("selectionchange", this, this.selNodes);
16970         return node;
16971     },
16972     
16973     /**
16974      * Deselect a node.
16975      * @param {TreeNode} node The node to unselect
16976      */
16977     unselect : function(node){
16978         if(this.selMap[node.id]){
16979             node.ui.onSelectedChange(false);
16980             var sn = this.selNodes;
16981             var index = -1;
16982             if(sn.indexOf){
16983                 index = sn.indexOf(node);
16984             }else{
16985                 for(var i = 0, len = sn.length; i < len; i++){
16986                     if(sn[i] == node){
16987                         index = i;
16988                         break;
16989                     }
16990                 }
16991             }
16992             if(index != -1){
16993                 this.selNodes.splice(index, 1);
16994             }
16995             delete this.selMap[node.id];
16996             this.fireEvent("selectionchange", this, this.selNodes);
16997         }
16998     },
16999     
17000     /**
17001      * Clear all selections
17002      */
17003     clearSelections : function(suppressEvent){
17004         var sn = this.selNodes;
17005         if(sn.length > 0){
17006             for(var i = 0, len = sn.length; i < len; i++){
17007                 sn[i].ui.onSelectedChange(false);
17008             }
17009             this.selNodes = [];
17010             this.selMap = {};
17011             if(suppressEvent !== true){
17012                 this.fireEvent("selectionchange", this, this.selNodes);
17013             }
17014         }
17015     },
17016     
17017     /**
17018      * Returns true if the node is selected
17019      * @param {TreeNode} node The node to check
17020      * @return {Boolean}
17021      */
17022     isSelected : function(node){
17023         return this.selMap[node.id] ? true : false;  
17024     },
17025     
17026     /**
17027      * Returns an array of the selected nodes
17028      * @return {Array}
17029      */
17030     getSelectedNodes : function(){
17031         return this.selNodes;    
17032     },
17033
17034     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17035
17036     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17037
17038     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17039 });/*
17040  * Based on:
17041  * Ext JS Library 1.1.1
17042  * Copyright(c) 2006-2007, Ext JS, LLC.
17043  *
17044  * Originally Released Under LGPL - original licence link has changed is not relivant.
17045  *
17046  * Fork - LGPL
17047  * <script type="text/javascript">
17048  */
17049  
17050 /**
17051  * @class Roo.tree.TreeNode
17052  * @extends Roo.data.Node
17053  * @cfg {String} text The text for this node
17054  * @cfg {Boolean} expanded true to start the node expanded
17055  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17056  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17057  * @cfg {Boolean} disabled true to start the node disabled
17058  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17059  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17060  * @cfg {String} cls A css class to be added to the node
17061  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17062  * @cfg {String} href URL of the link used for the node (defaults to #)
17063  * @cfg {String} hrefTarget target frame for the link
17064  * @cfg {String} qtip An Ext QuickTip for the node
17065  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17066  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17067  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17068  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17069  * (defaults to undefined with no checkbox rendered)
17070  * @constructor
17071  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17072  */
17073 Roo.tree.TreeNode = function(attributes){
17074     attributes = attributes || {};
17075     if(typeof attributes == "string"){
17076         attributes = {text: attributes};
17077     }
17078     this.childrenRendered = false;
17079     this.rendered = false;
17080     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17081     this.expanded = attributes.expanded === true;
17082     this.isTarget = attributes.isTarget !== false;
17083     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17084     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17085
17086     /**
17087      * Read-only. The text for this node. To change it use setText().
17088      * @type String
17089      */
17090     this.text = attributes.text;
17091     /**
17092      * True if this node is disabled.
17093      * @type Boolean
17094      */
17095     this.disabled = attributes.disabled === true;
17096
17097     this.addEvents({
17098         /**
17099         * @event textchange
17100         * Fires when the text for this node is changed
17101         * @param {Node} this This node
17102         * @param {String} text The new text
17103         * @param {String} oldText The old text
17104         */
17105         "textchange" : true,
17106         /**
17107         * @event beforeexpand
17108         * Fires before this node is expanded, return false to cancel.
17109         * @param {Node} this This node
17110         * @param {Boolean} deep
17111         * @param {Boolean} anim
17112         */
17113         "beforeexpand" : true,
17114         /**
17115         * @event beforecollapse
17116         * Fires before this node is collapsed, return false to cancel.
17117         * @param {Node} this This node
17118         * @param {Boolean} deep
17119         * @param {Boolean} anim
17120         */
17121         "beforecollapse" : true,
17122         /**
17123         * @event expand
17124         * Fires when this node is expanded
17125         * @param {Node} this This node
17126         */
17127         "expand" : true,
17128         /**
17129         * @event disabledchange
17130         * Fires when the disabled status of this node changes
17131         * @param {Node} this This node
17132         * @param {Boolean} disabled
17133         */
17134         "disabledchange" : true,
17135         /**
17136         * @event collapse
17137         * Fires when this node is collapsed
17138         * @param {Node} this This node
17139         */
17140         "collapse" : true,
17141         /**
17142         * @event beforeclick
17143         * Fires before click processing. Return false to cancel the default action.
17144         * @param {Node} this This node
17145         * @param {Roo.EventObject} e The event object
17146         */
17147         "beforeclick":true,
17148         /**
17149         * @event checkchange
17150         * Fires when a node with a checkbox's checked property changes
17151         * @param {Node} this This node
17152         * @param {Boolean} checked
17153         */
17154         "checkchange":true,
17155         /**
17156         * @event click
17157         * Fires when this node is clicked
17158         * @param {Node} this This node
17159         * @param {Roo.EventObject} e The event object
17160         */
17161         "click":true,
17162         /**
17163         * @event dblclick
17164         * Fires when this node is double clicked
17165         * @param {Node} this This node
17166         * @param {Roo.EventObject} e The event object
17167         */
17168         "dblclick":true,
17169         /**
17170         * @event contextmenu
17171         * Fires when this node is right clicked
17172         * @param {Node} this This node
17173         * @param {Roo.EventObject} e The event object
17174         */
17175         "contextmenu":true,
17176         /**
17177         * @event beforechildrenrendered
17178         * Fires right before the child nodes for this node are rendered
17179         * @param {Node} this This node
17180         */
17181         "beforechildrenrendered":true
17182     });
17183
17184     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17185
17186     /**
17187      * Read-only. The UI for this node
17188      * @type TreeNodeUI
17189      */
17190     this.ui = new uiClass(this);
17191 };
17192 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17193     preventHScroll: true,
17194     /**
17195      * Returns true if this node is expanded
17196      * @return {Boolean}
17197      */
17198     isExpanded : function(){
17199         return this.expanded;
17200     },
17201
17202     /**
17203      * Returns the UI object for this node
17204      * @return {TreeNodeUI}
17205      */
17206     getUI : function(){
17207         return this.ui;
17208     },
17209
17210     // private override
17211     setFirstChild : function(node){
17212         var of = this.firstChild;
17213         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17214         if(this.childrenRendered && of && node != of){
17215             of.renderIndent(true, true);
17216         }
17217         if(this.rendered){
17218             this.renderIndent(true, true);
17219         }
17220     },
17221
17222     // private override
17223     setLastChild : function(node){
17224         var ol = this.lastChild;
17225         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17226         if(this.childrenRendered && ol && node != ol){
17227             ol.renderIndent(true, true);
17228         }
17229         if(this.rendered){
17230             this.renderIndent(true, true);
17231         }
17232     },
17233
17234     // these methods are overridden to provide lazy rendering support
17235     // private override
17236     appendChild : function(){
17237         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17238         if(node && this.childrenRendered){
17239             node.render();
17240         }
17241         this.ui.updateExpandIcon();
17242         return node;
17243     },
17244
17245     // private override
17246     removeChild : function(node){
17247         this.ownerTree.getSelectionModel().unselect(node);
17248         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17249         // if it's been rendered remove dom node
17250         if(this.childrenRendered){
17251             node.ui.remove();
17252         }
17253         if(this.childNodes.length < 1){
17254             this.collapse(false, false);
17255         }else{
17256             this.ui.updateExpandIcon();
17257         }
17258         if(!this.firstChild) {
17259             this.childrenRendered = false;
17260         }
17261         return node;
17262     },
17263
17264     // private override
17265     insertBefore : function(node, refNode){
17266         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17267         if(newNode && refNode && this.childrenRendered){
17268             node.render();
17269         }
17270         this.ui.updateExpandIcon();
17271         return newNode;
17272     },
17273
17274     /**
17275      * Sets the text for this node
17276      * @param {String} text
17277      */
17278     setText : function(text){
17279         var oldText = this.text;
17280         this.text = text;
17281         this.attributes.text = text;
17282         if(this.rendered){ // event without subscribing
17283             this.ui.onTextChange(this, text, oldText);
17284         }
17285         this.fireEvent("textchange", this, text, oldText);
17286     },
17287
17288     /**
17289      * Triggers selection of this node
17290      */
17291     select : function(){
17292         this.getOwnerTree().getSelectionModel().select(this);
17293     },
17294
17295     /**
17296      * Triggers deselection of this node
17297      */
17298     unselect : function(){
17299         this.getOwnerTree().getSelectionModel().unselect(this);
17300     },
17301
17302     /**
17303      * Returns true if this node is selected
17304      * @return {Boolean}
17305      */
17306     isSelected : function(){
17307         return this.getOwnerTree().getSelectionModel().isSelected(this);
17308     },
17309
17310     /**
17311      * Expand this node.
17312      * @param {Boolean} deep (optional) True to expand all children as well
17313      * @param {Boolean} anim (optional) false to cancel the default animation
17314      * @param {Function} callback (optional) A callback to be called when
17315      * expanding this node completes (does not wait for deep expand to complete).
17316      * Called with 1 parameter, this node.
17317      */
17318     expand : function(deep, anim, callback){
17319         if(!this.expanded){
17320             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17321                 return;
17322             }
17323             if(!this.childrenRendered){
17324                 this.renderChildren();
17325             }
17326             this.expanded = true;
17327             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17328                 this.ui.animExpand(function(){
17329                     this.fireEvent("expand", this);
17330                     if(typeof callback == "function"){
17331                         callback(this);
17332                     }
17333                     if(deep === true){
17334                         this.expandChildNodes(true);
17335                     }
17336                 }.createDelegate(this));
17337                 return;
17338             }else{
17339                 this.ui.expand();
17340                 this.fireEvent("expand", this);
17341                 if(typeof callback == "function"){
17342                     callback(this);
17343                 }
17344             }
17345         }else{
17346            if(typeof callback == "function"){
17347                callback(this);
17348            }
17349         }
17350         if(deep === true){
17351             this.expandChildNodes(true);
17352         }
17353     },
17354
17355     isHiddenRoot : function(){
17356         return this.isRoot && !this.getOwnerTree().rootVisible;
17357     },
17358
17359     /**
17360      * Collapse this node.
17361      * @param {Boolean} deep (optional) True to collapse all children as well
17362      * @param {Boolean} anim (optional) false to cancel the default animation
17363      */
17364     collapse : function(deep, anim){
17365         if(this.expanded && !this.isHiddenRoot()){
17366             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17367                 return;
17368             }
17369             this.expanded = false;
17370             if((this.getOwnerTree().animate && anim !== false) || anim){
17371                 this.ui.animCollapse(function(){
17372                     this.fireEvent("collapse", this);
17373                     if(deep === true){
17374                         this.collapseChildNodes(true);
17375                     }
17376                 }.createDelegate(this));
17377                 return;
17378             }else{
17379                 this.ui.collapse();
17380                 this.fireEvent("collapse", this);
17381             }
17382         }
17383         if(deep === true){
17384             var cs = this.childNodes;
17385             for(var i = 0, len = cs.length; i < len; i++) {
17386                 cs[i].collapse(true, false);
17387             }
17388         }
17389     },
17390
17391     // private
17392     delayedExpand : function(delay){
17393         if(!this.expandProcId){
17394             this.expandProcId = this.expand.defer(delay, this);
17395         }
17396     },
17397
17398     // private
17399     cancelExpand : function(){
17400         if(this.expandProcId){
17401             clearTimeout(this.expandProcId);
17402         }
17403         this.expandProcId = false;
17404     },
17405
17406     /**
17407      * Toggles expanded/collapsed state of the node
17408      */
17409     toggle : function(){
17410         if(this.expanded){
17411             this.collapse();
17412         }else{
17413             this.expand();
17414         }
17415     },
17416
17417     /**
17418      * Ensures all parent nodes are expanded
17419      */
17420     ensureVisible : function(callback){
17421         var tree = this.getOwnerTree();
17422         tree.expandPath(this.parentNode.getPath(), false, function(){
17423             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17424             Roo.callback(callback);
17425         }.createDelegate(this));
17426     },
17427
17428     /**
17429      * Expand all child nodes
17430      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17431      */
17432     expandChildNodes : function(deep){
17433         var cs = this.childNodes;
17434         for(var i = 0, len = cs.length; i < len; i++) {
17435                 cs[i].expand(deep);
17436         }
17437     },
17438
17439     /**
17440      * Collapse all child nodes
17441      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17442      */
17443     collapseChildNodes : function(deep){
17444         var cs = this.childNodes;
17445         for(var i = 0, len = cs.length; i < len; i++) {
17446                 cs[i].collapse(deep);
17447         }
17448     },
17449
17450     /**
17451      * Disables this node
17452      */
17453     disable : function(){
17454         this.disabled = true;
17455         this.unselect();
17456         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17457             this.ui.onDisableChange(this, true);
17458         }
17459         this.fireEvent("disabledchange", this, true);
17460     },
17461
17462     /**
17463      * Enables this node
17464      */
17465     enable : function(){
17466         this.disabled = false;
17467         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17468             this.ui.onDisableChange(this, false);
17469         }
17470         this.fireEvent("disabledchange", this, false);
17471     },
17472
17473     // private
17474     renderChildren : function(suppressEvent){
17475         if(suppressEvent !== false){
17476             this.fireEvent("beforechildrenrendered", this);
17477         }
17478         var cs = this.childNodes;
17479         for(var i = 0, len = cs.length; i < len; i++){
17480             cs[i].render(true);
17481         }
17482         this.childrenRendered = true;
17483     },
17484
17485     // private
17486     sort : function(fn, scope){
17487         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17488         if(this.childrenRendered){
17489             var cs = this.childNodes;
17490             for(var i = 0, len = cs.length; i < len; i++){
17491                 cs[i].render(true);
17492             }
17493         }
17494     },
17495
17496     // private
17497     render : function(bulkRender){
17498         this.ui.render(bulkRender);
17499         if(!this.rendered){
17500             this.rendered = true;
17501             if(this.expanded){
17502                 this.expanded = false;
17503                 this.expand(false, false);
17504             }
17505         }
17506     },
17507
17508     // private
17509     renderIndent : function(deep, refresh){
17510         if(refresh){
17511             this.ui.childIndent = null;
17512         }
17513         this.ui.renderIndent();
17514         if(deep === true && this.childrenRendered){
17515             var cs = this.childNodes;
17516             for(var i = 0, len = cs.length; i < len; i++){
17517                 cs[i].renderIndent(true, refresh);
17518             }
17519         }
17520     }
17521 });/*
17522  * Based on:
17523  * Ext JS Library 1.1.1
17524  * Copyright(c) 2006-2007, Ext JS, LLC.
17525  *
17526  * Originally Released Under LGPL - original licence link has changed is not relivant.
17527  *
17528  * Fork - LGPL
17529  * <script type="text/javascript">
17530  */
17531  
17532 /**
17533  * @class Roo.tree.AsyncTreeNode
17534  * @extends Roo.tree.TreeNode
17535  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17536  * @constructor
17537  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17538  */
17539  Roo.tree.AsyncTreeNode = function(config){
17540     this.loaded = false;
17541     this.loading = false;
17542     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17543     /**
17544     * @event beforeload
17545     * Fires before this node is loaded, return false to cancel
17546     * @param {Node} this This node
17547     */
17548     this.addEvents({'beforeload':true, 'load': true});
17549     /**
17550     * @event load
17551     * Fires when this node is loaded
17552     * @param {Node} this This node
17553     */
17554     /**
17555      * The loader used by this node (defaults to using the tree's defined loader)
17556      * @type TreeLoader
17557      * @property loader
17558      */
17559 };
17560 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17561     expand : function(deep, anim, callback){
17562         if(this.loading){ // if an async load is already running, waiting til it's done
17563             var timer;
17564             var f = function(){
17565                 if(!this.loading){ // done loading
17566                     clearInterval(timer);
17567                     this.expand(deep, anim, callback);
17568                 }
17569             }.createDelegate(this);
17570             timer = setInterval(f, 200);
17571             return;
17572         }
17573         if(!this.loaded){
17574             if(this.fireEvent("beforeload", this) === false){
17575                 return;
17576             }
17577             this.loading = true;
17578             this.ui.beforeLoad(this);
17579             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17580             if(loader){
17581                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17582                 return;
17583             }
17584         }
17585         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17586     },
17587     
17588     /**
17589      * Returns true if this node is currently loading
17590      * @return {Boolean}
17591      */
17592     isLoading : function(){
17593         return this.loading;  
17594     },
17595     
17596     loadComplete : function(deep, anim, callback){
17597         this.loading = false;
17598         this.loaded = true;
17599         this.ui.afterLoad(this);
17600         this.fireEvent("load", this);
17601         this.expand(deep, anim, callback);
17602     },
17603     
17604     /**
17605      * Returns true if this node has been loaded
17606      * @return {Boolean}
17607      */
17608     isLoaded : function(){
17609         return this.loaded;
17610     },
17611     
17612     hasChildNodes : function(){
17613         if(!this.isLeaf() && !this.loaded){
17614             return true;
17615         }else{
17616             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17617         }
17618     },
17619
17620     /**
17621      * Trigger a reload for this node
17622      * @param {Function} callback
17623      */
17624     reload : function(callback){
17625         this.collapse(false, false);
17626         while(this.firstChild){
17627             this.removeChild(this.firstChild);
17628         }
17629         this.childrenRendered = false;
17630         this.loaded = false;
17631         if(this.isHiddenRoot()){
17632             this.expanded = false;
17633         }
17634         this.expand(false, false, callback);
17635     }
17636 });/*
17637  * Based on:
17638  * Ext JS Library 1.1.1
17639  * Copyright(c) 2006-2007, Ext JS, LLC.
17640  *
17641  * Originally Released Under LGPL - original licence link has changed is not relivant.
17642  *
17643  * Fork - LGPL
17644  * <script type="text/javascript">
17645  */
17646  
17647 /**
17648  * @class Roo.tree.TreeNodeUI
17649  * @constructor
17650  * @param {Object} node The node to render
17651  * The TreeNode UI implementation is separate from the
17652  * tree implementation. Unless you are customizing the tree UI,
17653  * you should never have to use this directly.
17654  */
17655 Roo.tree.TreeNodeUI = function(node){
17656     this.node = node;
17657     this.rendered = false;
17658     this.animating = false;
17659     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17660 };
17661
17662 Roo.tree.TreeNodeUI.prototype = {
17663     removeChild : function(node){
17664         if(this.rendered){
17665             this.ctNode.removeChild(node.ui.getEl());
17666         }
17667     },
17668
17669     beforeLoad : function(){
17670          this.addClass("x-tree-node-loading");
17671     },
17672
17673     afterLoad : function(){
17674          this.removeClass("x-tree-node-loading");
17675     },
17676
17677     onTextChange : function(node, text, oldText){
17678         if(this.rendered){
17679             this.textNode.innerHTML = text;
17680         }
17681     },
17682
17683     onDisableChange : function(node, state){
17684         this.disabled = state;
17685         if(state){
17686             this.addClass("x-tree-node-disabled");
17687         }else{
17688             this.removeClass("x-tree-node-disabled");
17689         }
17690     },
17691
17692     onSelectedChange : function(state){
17693         if(state){
17694             this.focus();
17695             this.addClass("x-tree-selected");
17696         }else{
17697             //this.blur();
17698             this.removeClass("x-tree-selected");
17699         }
17700     },
17701
17702     onMove : function(tree, node, oldParent, newParent, index, refNode){
17703         this.childIndent = null;
17704         if(this.rendered){
17705             var targetNode = newParent.ui.getContainer();
17706             if(!targetNode){//target not rendered
17707                 this.holder = document.createElement("div");
17708                 this.holder.appendChild(this.wrap);
17709                 return;
17710             }
17711             var insertBefore = refNode ? refNode.ui.getEl() : null;
17712             if(insertBefore){
17713                 targetNode.insertBefore(this.wrap, insertBefore);
17714             }else{
17715                 targetNode.appendChild(this.wrap);
17716             }
17717             this.node.renderIndent(true);
17718         }
17719     },
17720
17721     addClass : function(cls){
17722         if(this.elNode){
17723             Roo.fly(this.elNode).addClass(cls);
17724         }
17725     },
17726
17727     removeClass : function(cls){
17728         if(this.elNode){
17729             Roo.fly(this.elNode).removeClass(cls);
17730         }
17731     },
17732
17733     remove : function(){
17734         if(this.rendered){
17735             this.holder = document.createElement("div");
17736             this.holder.appendChild(this.wrap);
17737         }
17738     },
17739
17740     fireEvent : function(){
17741         return this.node.fireEvent.apply(this.node, arguments);
17742     },
17743
17744     initEvents : function(){
17745         this.node.on("move", this.onMove, this);
17746         var E = Roo.EventManager;
17747         var a = this.anchor;
17748
17749         var el = Roo.fly(a, '_treeui');
17750
17751         if(Roo.isOpera){ // opera render bug ignores the CSS
17752             el.setStyle("text-decoration", "none");
17753         }
17754
17755         el.on("click", this.onClick, this);
17756         el.on("dblclick", this.onDblClick, this);
17757
17758         if(this.checkbox){
17759             Roo.EventManager.on(this.checkbox,
17760                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17761         }
17762
17763         el.on("contextmenu", this.onContextMenu, this);
17764
17765         var icon = Roo.fly(this.iconNode);
17766         icon.on("click", this.onClick, this);
17767         icon.on("dblclick", this.onDblClick, this);
17768         icon.on("contextmenu", this.onContextMenu, this);
17769         E.on(this.ecNode, "click", this.ecClick, this, true);
17770
17771         if(this.node.disabled){
17772             this.addClass("x-tree-node-disabled");
17773         }
17774         if(this.node.hidden){
17775             this.addClass("x-tree-node-disabled");
17776         }
17777         var ot = this.node.getOwnerTree();
17778         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17779         if(dd && (!this.node.isRoot || ot.rootVisible)){
17780             Roo.dd.Registry.register(this.elNode, {
17781                 node: this.node,
17782                 handles: this.getDDHandles(),
17783                 isHandle: false
17784             });
17785         }
17786     },
17787
17788     getDDHandles : function(){
17789         return [this.iconNode, this.textNode];
17790     },
17791
17792     hide : function(){
17793         if(this.rendered){
17794             this.wrap.style.display = "none";
17795         }
17796     },
17797
17798     show : function(){
17799         if(this.rendered){
17800             this.wrap.style.display = "";
17801         }
17802     },
17803
17804     onContextMenu : function(e){
17805         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17806             e.preventDefault();
17807             this.focus();
17808             this.fireEvent("contextmenu", this.node, e);
17809         }
17810     },
17811
17812     onClick : function(e){
17813         if(this.dropping){
17814             e.stopEvent();
17815             return;
17816         }
17817         if(this.fireEvent("beforeclick", this.node, e) !== false){
17818             if(!this.disabled && this.node.attributes.href){
17819                 this.fireEvent("click", this.node, e);
17820                 return;
17821             }
17822             e.preventDefault();
17823             if(this.disabled){
17824                 return;
17825             }
17826
17827             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17828                 this.node.toggle();
17829             }
17830
17831             this.fireEvent("click", this.node, e);
17832         }else{
17833             e.stopEvent();
17834         }
17835     },
17836
17837     onDblClick : function(e){
17838         e.preventDefault();
17839         if(this.disabled){
17840             return;
17841         }
17842         if(this.checkbox){
17843             this.toggleCheck();
17844         }
17845         if(!this.animating && this.node.hasChildNodes()){
17846             this.node.toggle();
17847         }
17848         this.fireEvent("dblclick", this.node, e);
17849     },
17850
17851     onCheckChange : function(){
17852         var checked = this.checkbox.checked;
17853         this.node.attributes.checked = checked;
17854         this.fireEvent('checkchange', this.node, checked);
17855     },
17856
17857     ecClick : function(e){
17858         if(!this.animating && this.node.hasChildNodes()){
17859             this.node.toggle();
17860         }
17861     },
17862
17863     startDrop : function(){
17864         this.dropping = true;
17865     },
17866
17867     // delayed drop so the click event doesn't get fired on a drop
17868     endDrop : function(){
17869        setTimeout(function(){
17870            this.dropping = false;
17871        }.createDelegate(this), 50);
17872     },
17873
17874     expand : function(){
17875         this.updateExpandIcon();
17876         this.ctNode.style.display = "";
17877     },
17878
17879     focus : function(){
17880         if(!this.node.preventHScroll){
17881             try{this.anchor.focus();
17882             }catch(e){}
17883         }else if(!Roo.isIE){
17884             try{
17885                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17886                 var l = noscroll.scrollLeft;
17887                 this.anchor.focus();
17888                 noscroll.scrollLeft = l;
17889             }catch(e){}
17890         }
17891     },
17892
17893     toggleCheck : function(value){
17894         var cb = this.checkbox;
17895         if(cb){
17896             cb.checked = (value === undefined ? !cb.checked : value);
17897         }
17898     },
17899
17900     blur : function(){
17901         try{
17902             this.anchor.blur();
17903         }catch(e){}
17904     },
17905
17906     animExpand : function(callback){
17907         var ct = Roo.get(this.ctNode);
17908         ct.stopFx();
17909         if(!this.node.hasChildNodes()){
17910             this.updateExpandIcon();
17911             this.ctNode.style.display = "";
17912             Roo.callback(callback);
17913             return;
17914         }
17915         this.animating = true;
17916         this.updateExpandIcon();
17917
17918         ct.slideIn('t', {
17919            callback : function(){
17920                this.animating = false;
17921                Roo.callback(callback);
17922             },
17923             scope: this,
17924             duration: this.node.ownerTree.duration || .25
17925         });
17926     },
17927
17928     highlight : function(){
17929         var tree = this.node.getOwnerTree();
17930         Roo.fly(this.wrap).highlight(
17931             tree.hlColor || "C3DAF9",
17932             {endColor: tree.hlBaseColor}
17933         );
17934     },
17935
17936     collapse : function(){
17937         this.updateExpandIcon();
17938         this.ctNode.style.display = "none";
17939     },
17940
17941     animCollapse : function(callback){
17942         var ct = Roo.get(this.ctNode);
17943         ct.enableDisplayMode('block');
17944         ct.stopFx();
17945
17946         this.animating = true;
17947         this.updateExpandIcon();
17948
17949         ct.slideOut('t', {
17950             callback : function(){
17951                this.animating = false;
17952                Roo.callback(callback);
17953             },
17954             scope: this,
17955             duration: this.node.ownerTree.duration || .25
17956         });
17957     },
17958
17959     getContainer : function(){
17960         return this.ctNode;
17961     },
17962
17963     getEl : function(){
17964         return this.wrap;
17965     },
17966
17967     appendDDGhost : function(ghostNode){
17968         ghostNode.appendChild(this.elNode.cloneNode(true));
17969     },
17970
17971     getDDRepairXY : function(){
17972         return Roo.lib.Dom.getXY(this.iconNode);
17973     },
17974
17975     onRender : function(){
17976         this.render();
17977     },
17978
17979     render : function(bulkRender){
17980         var n = this.node, a = n.attributes;
17981         var targetNode = n.parentNode ?
17982               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17983
17984         if(!this.rendered){
17985             this.rendered = true;
17986
17987             this.renderElements(n, a, targetNode, bulkRender);
17988
17989             if(a.qtip){
17990                if(this.textNode.setAttributeNS){
17991                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17992                    if(a.qtipTitle){
17993                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17994                    }
17995                }else{
17996                    this.textNode.setAttribute("ext:qtip", a.qtip);
17997                    if(a.qtipTitle){
17998                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17999                    }
18000                }
18001             }else if(a.qtipCfg){
18002                 a.qtipCfg.target = Roo.id(this.textNode);
18003                 Roo.QuickTips.register(a.qtipCfg);
18004             }
18005             this.initEvents();
18006             if(!this.node.expanded){
18007                 this.updateExpandIcon();
18008             }
18009         }else{
18010             if(bulkRender === true) {
18011                 targetNode.appendChild(this.wrap);
18012             }
18013         }
18014     },
18015
18016     renderElements : function(n, a, targetNode, bulkRender){
18017         // add some indent caching, this helps performance when rendering a large tree
18018         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18019         var t = n.getOwnerTree();
18020         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18021         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18022         var cb = typeof a.checked == 'boolean';
18023         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18024         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18025             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18026             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18027             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18028             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18029             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18030              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18031                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18032             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18033             "</li>"];
18034
18035         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18036             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18037                                 n.nextSibling.ui.getEl(), buf.join(""));
18038         }else{
18039             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18040         }
18041
18042         this.elNode = this.wrap.childNodes[0];
18043         this.ctNode = this.wrap.childNodes[1];
18044         var cs = this.elNode.childNodes;
18045         this.indentNode = cs[0];
18046         this.ecNode = cs[1];
18047         this.iconNode = cs[2];
18048         var index = 3;
18049         if(cb){
18050             this.checkbox = cs[3];
18051             index++;
18052         }
18053         this.anchor = cs[index];
18054         this.textNode = cs[index].firstChild;
18055     },
18056
18057     getAnchor : function(){
18058         return this.anchor;
18059     },
18060
18061     getTextEl : function(){
18062         return this.textNode;
18063     },
18064
18065     getIconEl : function(){
18066         return this.iconNode;
18067     },
18068
18069     isChecked : function(){
18070         return this.checkbox ? this.checkbox.checked : false;
18071     },
18072
18073     updateExpandIcon : function(){
18074         if(this.rendered){
18075             var n = this.node, c1, c2;
18076             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18077             var hasChild = n.hasChildNodes();
18078             if(hasChild){
18079                 if(n.expanded){
18080                     cls += "-minus";
18081                     c1 = "x-tree-node-collapsed";
18082                     c2 = "x-tree-node-expanded";
18083                 }else{
18084                     cls += "-plus";
18085                     c1 = "x-tree-node-expanded";
18086                     c2 = "x-tree-node-collapsed";
18087                 }
18088                 if(this.wasLeaf){
18089                     this.removeClass("x-tree-node-leaf");
18090                     this.wasLeaf = false;
18091                 }
18092                 if(this.c1 != c1 || this.c2 != c2){
18093                     Roo.fly(this.elNode).replaceClass(c1, c2);
18094                     this.c1 = c1; this.c2 = c2;
18095                 }
18096             }else{
18097                 if(!this.wasLeaf){
18098                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18099                     delete this.c1;
18100                     delete this.c2;
18101                     this.wasLeaf = true;
18102                 }
18103             }
18104             var ecc = "x-tree-ec-icon "+cls;
18105             if(this.ecc != ecc){
18106                 this.ecNode.className = ecc;
18107                 this.ecc = ecc;
18108             }
18109         }
18110     },
18111
18112     getChildIndent : function(){
18113         if(!this.childIndent){
18114             var buf = [];
18115             var p = this.node;
18116             while(p){
18117                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18118                     if(!p.isLast()) {
18119                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18120                     } else {
18121                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18122                     }
18123                 }
18124                 p = p.parentNode;
18125             }
18126             this.childIndent = buf.join("");
18127         }
18128         return this.childIndent;
18129     },
18130
18131     renderIndent : function(){
18132         if(this.rendered){
18133             var indent = "";
18134             var p = this.node.parentNode;
18135             if(p){
18136                 indent = p.ui.getChildIndent();
18137             }
18138             if(this.indentMarkup != indent){ // don't rerender if not required
18139                 this.indentNode.innerHTML = indent;
18140                 this.indentMarkup = indent;
18141             }
18142             this.updateExpandIcon();
18143         }
18144     }
18145 };
18146
18147 Roo.tree.RootTreeNodeUI = function(){
18148     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18149 };
18150 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18151     render : function(){
18152         if(!this.rendered){
18153             var targetNode = this.node.ownerTree.innerCt.dom;
18154             this.node.expanded = true;
18155             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18156             this.wrap = this.ctNode = targetNode.firstChild;
18157         }
18158     },
18159     collapse : function(){
18160     },
18161     expand : function(){
18162     }
18163 });/*
18164  * Based on:
18165  * Ext JS Library 1.1.1
18166  * Copyright(c) 2006-2007, Ext JS, LLC.
18167  *
18168  * Originally Released Under LGPL - original licence link has changed is not relivant.
18169  *
18170  * Fork - LGPL
18171  * <script type="text/javascript">
18172  */
18173 /**
18174  * @class Roo.tree.TreeLoader
18175  * @extends Roo.util.Observable
18176  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18177  * nodes from a specified URL. The response must be a javascript Array definition
18178  * who's elements are node definition objects. eg:
18179  * <pre><code>
18180    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18181     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18182 </code></pre>
18183  * <br><br>
18184  * A server request is sent, and child nodes are loaded only when a node is expanded.
18185  * The loading node's id is passed to the server under the parameter name "node" to
18186  * enable the server to produce the correct child nodes.
18187  * <br><br>
18188  * To pass extra parameters, an event handler may be attached to the "beforeload"
18189  * event, and the parameters specified in the TreeLoader's baseParams property:
18190  * <pre><code>
18191     myTreeLoader.on("beforeload", function(treeLoader, node) {
18192         this.baseParams.category = node.attributes.category;
18193     }, this);
18194 </code></pre><
18195  * This would pass an HTTP parameter called "category" to the server containing
18196  * the value of the Node's "category" attribute.
18197  * @constructor
18198  * Creates a new Treeloader.
18199  * @param {Object} config A config object containing config properties.
18200  */
18201 Roo.tree.TreeLoader = function(config){
18202     this.baseParams = {};
18203     this.requestMethod = "POST";
18204     Roo.apply(this, config);
18205
18206     this.addEvents({
18207     
18208         /**
18209          * @event beforeload
18210          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18211          * @param {Object} This TreeLoader object.
18212          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18213          * @param {Object} callback The callback function specified in the {@link #load} call.
18214          */
18215         beforeload : true,
18216         /**
18217          * @event load
18218          * Fires when the node has been successfuly loaded.
18219          * @param {Object} This TreeLoader object.
18220          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18221          * @param {Object} response The response object containing the data from the server.
18222          */
18223         load : true,
18224         /**
18225          * @event loadexception
18226          * Fires if the network request failed.
18227          * @param {Object} This TreeLoader object.
18228          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18229          * @param {Object} response The response object containing the data from the server.
18230          */
18231         loadexception : true,
18232         /**
18233          * @event create
18234          * Fires before a node is created, enabling you to return custom Node types 
18235          * @param {Object} This TreeLoader object.
18236          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18237          */
18238         create : true
18239     });
18240
18241     Roo.tree.TreeLoader.superclass.constructor.call(this);
18242 };
18243
18244 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18245     /**
18246     * @cfg {String} dataUrl The URL from which to request a Json string which
18247     * specifies an array of node definition object representing the child nodes
18248     * to be loaded.
18249     */
18250     /**
18251     * @cfg {Object} baseParams (optional) An object containing properties which
18252     * specify HTTP parameters to be passed to each request for child nodes.
18253     */
18254     /**
18255     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18256     * created by this loader. If the attributes sent by the server have an attribute in this object,
18257     * they take priority.
18258     */
18259     /**
18260     * @cfg {Object} uiProviders (optional) An object containing properties which
18261     * 
18262     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18263     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18264     * <i>uiProvider</i> attribute of a returned child node is a string rather
18265     * than a reference to a TreeNodeUI implementation, this that string value
18266     * is used as a property name in the uiProviders object. You can define the provider named
18267     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18268     */
18269     uiProviders : {},
18270
18271     /**
18272     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18273     * child nodes before loading.
18274     */
18275     clearOnLoad : true,
18276
18277     /**
18278     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18279     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18280     * Grid query { data : [ .....] }
18281     */
18282     
18283     root : false,
18284      /**
18285     * @cfg {String} queryParam (optional) 
18286     * Name of the query as it will be passed on the querystring (defaults to 'node')
18287     * eg. the request will be ?node=[id]
18288     */
18289     
18290     
18291     queryParam: false,
18292     
18293     /**
18294      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18295      * This is called automatically when a node is expanded, but may be used to reload
18296      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18297      * @param {Roo.tree.TreeNode} node
18298      * @param {Function} callback
18299      */
18300     load : function(node, callback){
18301         if(this.clearOnLoad){
18302             while(node.firstChild){
18303                 node.removeChild(node.firstChild);
18304             }
18305         }
18306         if(node.attributes.children){ // preloaded json children
18307             var cs = node.attributes.children;
18308             for(var i = 0, len = cs.length; i < len; i++){
18309                 node.appendChild(this.createNode(cs[i]));
18310             }
18311             if(typeof callback == "function"){
18312                 callback();
18313             }
18314         }else if(this.dataUrl){
18315             this.requestData(node, callback);
18316         }
18317     },
18318
18319     getParams: function(node){
18320         var buf = [], bp = this.baseParams;
18321         for(var key in bp){
18322             if(typeof bp[key] != "function"){
18323                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18324             }
18325         }
18326         var n = this.queryParam === false ? 'node' : this.queryParam;
18327         buf.push(n + "=", encodeURIComponent(node.id));
18328         return buf.join("");
18329     },
18330
18331     requestData : function(node, callback){
18332         if(this.fireEvent("beforeload", this, node, callback) !== false){
18333             this.transId = Roo.Ajax.request({
18334                 method:this.requestMethod,
18335                 url: this.dataUrl||this.url,
18336                 success: this.handleResponse,
18337                 failure: this.handleFailure,
18338                 scope: this,
18339                 argument: {callback: callback, node: node},
18340                 params: this.getParams(node)
18341             });
18342         }else{
18343             // if the load is cancelled, make sure we notify
18344             // the node that we are done
18345             if(typeof callback == "function"){
18346                 callback();
18347             }
18348         }
18349     },
18350
18351     isLoading : function(){
18352         return this.transId ? true : false;
18353     },
18354
18355     abort : function(){
18356         if(this.isLoading()){
18357             Roo.Ajax.abort(this.transId);
18358         }
18359     },
18360
18361     // private
18362     createNode : function(attr){
18363         // apply baseAttrs, nice idea Corey!
18364         if(this.baseAttrs){
18365             Roo.applyIf(attr, this.baseAttrs);
18366         }
18367         if(this.applyLoader !== false){
18368             attr.loader = this;
18369         }
18370         // uiProvider = depreciated..
18371         
18372         if(typeof(attr.uiProvider) == 'string'){
18373            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18374                 /**  eval:var:attr */ eval(attr.uiProvider);
18375         }
18376         if(typeof(this.uiProviders['default']) != 'undefined') {
18377             attr.uiProvider = this.uiProviders['default'];
18378         }
18379         
18380         this.fireEvent('create', this, attr);
18381         
18382         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18383         return(attr.leaf ?
18384                         new Roo.tree.TreeNode(attr) :
18385                         new Roo.tree.AsyncTreeNode(attr));
18386     },
18387
18388     processResponse : function(response, node, callback){
18389         var json = response.responseText;
18390         try {
18391             
18392             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18393             if (this.root !== false) {
18394                 o = o[this.root];
18395             }
18396             
18397             for(var i = 0, len = o.length; i < len; i++){
18398                 var n = this.createNode(o[i]);
18399                 if(n){
18400                     node.appendChild(n);
18401                 }
18402             }
18403             if(typeof callback == "function"){
18404                 callback(this, node);
18405             }
18406         }catch(e){
18407             this.handleFailure(response);
18408         }
18409     },
18410
18411     handleResponse : function(response){
18412         this.transId = false;
18413         var a = response.argument;
18414         this.processResponse(response, a.node, a.callback);
18415         this.fireEvent("load", this, a.node, response);
18416     },
18417
18418     handleFailure : function(response){
18419         this.transId = false;
18420         var a = response.argument;
18421         this.fireEvent("loadexception", this, a.node, response);
18422         if(typeof a.callback == "function"){
18423             a.callback(this, a.node);
18424         }
18425     }
18426 });/*
18427  * Based on:
18428  * Ext JS Library 1.1.1
18429  * Copyright(c) 2006-2007, Ext JS, LLC.
18430  *
18431  * Originally Released Under LGPL - original licence link has changed is not relivant.
18432  *
18433  * Fork - LGPL
18434  * <script type="text/javascript">
18435  */
18436
18437 /**
18438 * @class Roo.tree.TreeFilter
18439 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18440 * @param {TreePanel} tree
18441 * @param {Object} config (optional)
18442  */
18443 Roo.tree.TreeFilter = function(tree, config){
18444     this.tree = tree;
18445     this.filtered = {};
18446     Roo.apply(this, config);
18447 };
18448
18449 Roo.tree.TreeFilter.prototype = {
18450     clearBlank:false,
18451     reverse:false,
18452     autoClear:false,
18453     remove:false,
18454
18455      /**
18456      * Filter the data by a specific attribute.
18457      * @param {String/RegExp} value Either string that the attribute value
18458      * should start with or a RegExp to test against the attribute
18459      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18460      * @param {TreeNode} startNode (optional) The node to start the filter at.
18461      */
18462     filter : function(value, attr, startNode){
18463         attr = attr || "text";
18464         var f;
18465         if(typeof value == "string"){
18466             var vlen = value.length;
18467             // auto clear empty filter
18468             if(vlen == 0 && this.clearBlank){
18469                 this.clear();
18470                 return;
18471             }
18472             value = value.toLowerCase();
18473             f = function(n){
18474                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18475             };
18476         }else if(value.exec){ // regex?
18477             f = function(n){
18478                 return value.test(n.attributes[attr]);
18479             };
18480         }else{
18481             throw 'Illegal filter type, must be string or regex';
18482         }
18483         this.filterBy(f, null, startNode);
18484         },
18485
18486     /**
18487      * Filter by a function. The passed function will be called with each
18488      * node in the tree (or from the startNode). If the function returns true, the node is kept
18489      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18490      * @param {Function} fn The filter function
18491      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18492      */
18493     filterBy : function(fn, scope, startNode){
18494         startNode = startNode || this.tree.root;
18495         if(this.autoClear){
18496             this.clear();
18497         }
18498         var af = this.filtered, rv = this.reverse;
18499         var f = function(n){
18500             if(n == startNode){
18501                 return true;
18502             }
18503             if(af[n.id]){
18504                 return false;
18505             }
18506             var m = fn.call(scope || n, n);
18507             if(!m || rv){
18508                 af[n.id] = n;
18509                 n.ui.hide();
18510                 return false;
18511             }
18512             return true;
18513         };
18514         startNode.cascade(f);
18515         if(this.remove){
18516            for(var id in af){
18517                if(typeof id != "function"){
18518                    var n = af[id];
18519                    if(n && n.parentNode){
18520                        n.parentNode.removeChild(n);
18521                    }
18522                }
18523            }
18524         }
18525     },
18526
18527     /**
18528      * Clears the current filter. Note: with the "remove" option
18529      * set a filter cannot be cleared.
18530      */
18531     clear : function(){
18532         var t = this.tree;
18533         var af = this.filtered;
18534         for(var id in af){
18535             if(typeof id != "function"){
18536                 var n = af[id];
18537                 if(n){
18538                     n.ui.show();
18539                 }
18540             }
18541         }
18542         this.filtered = {};
18543     }
18544 };
18545 /*
18546  * Based on:
18547  * Ext JS Library 1.1.1
18548  * Copyright(c) 2006-2007, Ext JS, LLC.
18549  *
18550  * Originally Released Under LGPL - original licence link has changed is not relivant.
18551  *
18552  * Fork - LGPL
18553  * <script type="text/javascript">
18554  */
18555  
18556
18557 /**
18558  * @class Roo.tree.TreeSorter
18559  * Provides sorting of nodes in a TreePanel
18560  * 
18561  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18562  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18563  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18564  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18565  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18566  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18567  * @constructor
18568  * @param {TreePanel} tree
18569  * @param {Object} config
18570  */
18571 Roo.tree.TreeSorter = function(tree, config){
18572     Roo.apply(this, config);
18573     tree.on("beforechildrenrendered", this.doSort, this);
18574     tree.on("append", this.updateSort, this);
18575     tree.on("insert", this.updateSort, this);
18576     
18577     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18578     var p = this.property || "text";
18579     var sortType = this.sortType;
18580     var fs = this.folderSort;
18581     var cs = this.caseSensitive === true;
18582     var leafAttr = this.leafAttr || 'leaf';
18583
18584     this.sortFn = function(n1, n2){
18585         if(fs){
18586             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18587                 return 1;
18588             }
18589             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18590                 return -1;
18591             }
18592         }
18593         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18594         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18595         if(v1 < v2){
18596                         return dsc ? +1 : -1;
18597                 }else if(v1 > v2){
18598                         return dsc ? -1 : +1;
18599         }else{
18600                 return 0;
18601         }
18602     };
18603 };
18604
18605 Roo.tree.TreeSorter.prototype = {
18606     doSort : function(node){
18607         node.sort(this.sortFn);
18608     },
18609     
18610     compareNodes : function(n1, n2){
18611         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18612     },
18613     
18614     updateSort : function(tree, node){
18615         if(node.childrenRendered){
18616             this.doSort.defer(1, this, [node]);
18617         }
18618     }
18619 };/*
18620  * Based on:
18621  * Ext JS Library 1.1.1
18622  * Copyright(c) 2006-2007, Ext JS, LLC.
18623  *
18624  * Originally Released Under LGPL - original licence link has changed is not relivant.
18625  *
18626  * Fork - LGPL
18627  * <script type="text/javascript">
18628  */
18629
18630 if(Roo.dd.DropZone){
18631     
18632 Roo.tree.TreeDropZone = function(tree, config){
18633     this.allowParentInsert = false;
18634     this.allowContainerDrop = false;
18635     this.appendOnly = false;
18636     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18637     this.tree = tree;
18638     this.lastInsertClass = "x-tree-no-status";
18639     this.dragOverData = {};
18640 };
18641
18642 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18643     ddGroup : "TreeDD",
18644     
18645     expandDelay : 1000,
18646     
18647     expandNode : function(node){
18648         if(node.hasChildNodes() && !node.isExpanded()){
18649             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18650         }
18651     },
18652     
18653     queueExpand : function(node){
18654         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18655     },
18656     
18657     cancelExpand : function(){
18658         if(this.expandProcId){
18659             clearTimeout(this.expandProcId);
18660             this.expandProcId = false;
18661         }
18662     },
18663     
18664     isValidDropPoint : function(n, pt, dd, e, data){
18665         if(!n || !data){ return false; }
18666         var targetNode = n.node;
18667         var dropNode = data.node;
18668         // default drop rules
18669         if(!(targetNode && targetNode.isTarget && pt)){
18670             return false;
18671         }
18672         if(pt == "append" && targetNode.allowChildren === false){
18673             return false;
18674         }
18675         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18676             return false;
18677         }
18678         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18679             return false;
18680         }
18681         // reuse the object
18682         var overEvent = this.dragOverData;
18683         overEvent.tree = this.tree;
18684         overEvent.target = targetNode;
18685         overEvent.data = data;
18686         overEvent.point = pt;
18687         overEvent.source = dd;
18688         overEvent.rawEvent = e;
18689         overEvent.dropNode = dropNode;
18690         overEvent.cancel = false;  
18691         var result = this.tree.fireEvent("nodedragover", overEvent);
18692         return overEvent.cancel === false && result !== false;
18693     },
18694     
18695     getDropPoint : function(e, n, dd){
18696         var tn = n.node;
18697         if(tn.isRoot){
18698             return tn.allowChildren !== false ? "append" : false; // always append for root
18699         }
18700         var dragEl = n.ddel;
18701         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18702         var y = Roo.lib.Event.getPageY(e);
18703         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18704         
18705         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18706         var noAppend = tn.allowChildren === false;
18707         if(this.appendOnly || tn.parentNode.allowChildren === false){
18708             return noAppend ? false : "append";
18709         }
18710         var noBelow = false;
18711         if(!this.allowParentInsert){
18712             noBelow = tn.hasChildNodes() && tn.isExpanded();
18713         }
18714         var q = (b - t) / (noAppend ? 2 : 3);
18715         if(y >= t && y < (t + q)){
18716             return "above";
18717         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18718             return "below";
18719         }else{
18720             return "append";
18721         }
18722     },
18723     
18724     onNodeEnter : function(n, dd, e, data){
18725         this.cancelExpand();
18726     },
18727     
18728     onNodeOver : function(n, dd, e, data){
18729         var pt = this.getDropPoint(e, n, dd);
18730         var node = n.node;
18731         
18732         // auto node expand check
18733         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18734             this.queueExpand(node);
18735         }else if(pt != "append"){
18736             this.cancelExpand();
18737         }
18738         
18739         // set the insert point style on the target node
18740         var returnCls = this.dropNotAllowed;
18741         if(this.isValidDropPoint(n, pt, dd, e, data)){
18742            if(pt){
18743                var el = n.ddel;
18744                var cls;
18745                if(pt == "above"){
18746                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18747                    cls = "x-tree-drag-insert-above";
18748                }else if(pt == "below"){
18749                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18750                    cls = "x-tree-drag-insert-below";
18751                }else{
18752                    returnCls = "x-tree-drop-ok-append";
18753                    cls = "x-tree-drag-append";
18754                }
18755                if(this.lastInsertClass != cls){
18756                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18757                    this.lastInsertClass = cls;
18758                }
18759            }
18760        }
18761        return returnCls;
18762     },
18763     
18764     onNodeOut : function(n, dd, e, data){
18765         this.cancelExpand();
18766         this.removeDropIndicators(n);
18767     },
18768     
18769     onNodeDrop : function(n, dd, e, data){
18770         var point = this.getDropPoint(e, n, dd);
18771         var targetNode = n.node;
18772         targetNode.ui.startDrop();
18773         if(!this.isValidDropPoint(n, point, dd, e, data)){
18774             targetNode.ui.endDrop();
18775             return false;
18776         }
18777         // first try to find the drop node
18778         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18779         var dropEvent = {
18780             tree : this.tree,
18781             target: targetNode,
18782             data: data,
18783             point: point,
18784             source: dd,
18785             rawEvent: e,
18786             dropNode: dropNode,
18787             cancel: !dropNode   
18788         };
18789         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18790         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18791             targetNode.ui.endDrop();
18792             return false;
18793         }
18794         // allow target changing
18795         targetNode = dropEvent.target;
18796         if(point == "append" && !targetNode.isExpanded()){
18797             targetNode.expand(false, null, function(){
18798                 this.completeDrop(dropEvent);
18799             }.createDelegate(this));
18800         }else{
18801             this.completeDrop(dropEvent);
18802         }
18803         return true;
18804     },
18805     
18806     completeDrop : function(de){
18807         var ns = de.dropNode, p = de.point, t = de.target;
18808         if(!(ns instanceof Array)){
18809             ns = [ns];
18810         }
18811         var n;
18812         for(var i = 0, len = ns.length; i < len; i++){
18813             n = ns[i];
18814             if(p == "above"){
18815                 t.parentNode.insertBefore(n, t);
18816             }else if(p == "below"){
18817                 t.parentNode.insertBefore(n, t.nextSibling);
18818             }else{
18819                 t.appendChild(n);
18820             }
18821         }
18822         n.ui.focus();
18823         if(this.tree.hlDrop){
18824             n.ui.highlight();
18825         }
18826         t.ui.endDrop();
18827         this.tree.fireEvent("nodedrop", de);
18828     },
18829     
18830     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18831         if(this.tree.hlDrop){
18832             dropNode.ui.focus();
18833             dropNode.ui.highlight();
18834         }
18835         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18836     },
18837     
18838     getTree : function(){
18839         return this.tree;
18840     },
18841     
18842     removeDropIndicators : function(n){
18843         if(n && n.ddel){
18844             var el = n.ddel;
18845             Roo.fly(el).removeClass([
18846                     "x-tree-drag-insert-above",
18847                     "x-tree-drag-insert-below",
18848                     "x-tree-drag-append"]);
18849             this.lastInsertClass = "_noclass";
18850         }
18851     },
18852     
18853     beforeDragDrop : function(target, e, id){
18854         this.cancelExpand();
18855         return true;
18856     },
18857     
18858     afterRepair : function(data){
18859         if(data && Roo.enableFx){
18860             data.node.ui.highlight();
18861         }
18862         this.hideProxy();
18863     }    
18864 });
18865
18866 }
18867 /*
18868  * Based on:
18869  * Ext JS Library 1.1.1
18870  * Copyright(c) 2006-2007, Ext JS, LLC.
18871  *
18872  * Originally Released Under LGPL - original licence link has changed is not relivant.
18873  *
18874  * Fork - LGPL
18875  * <script type="text/javascript">
18876  */
18877  
18878
18879 if(Roo.dd.DragZone){
18880 Roo.tree.TreeDragZone = function(tree, config){
18881     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18882     this.tree = tree;
18883 };
18884
18885 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18886     ddGroup : "TreeDD",
18887     
18888     onBeforeDrag : function(data, e){
18889         var n = data.node;
18890         return n && n.draggable && !n.disabled;
18891     },
18892     
18893     onInitDrag : function(e){
18894         var data = this.dragData;
18895         this.tree.getSelectionModel().select(data.node);
18896         this.proxy.update("");
18897         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18898         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18899     },
18900     
18901     getRepairXY : function(e, data){
18902         return data.node.ui.getDDRepairXY();
18903     },
18904     
18905     onEndDrag : function(data, e){
18906         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18907     },
18908     
18909     onValidDrop : function(dd, e, id){
18910         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18911         this.hideProxy();
18912     },
18913     
18914     beforeInvalidDrop : function(e, id){
18915         // this scrolls the original position back into view
18916         var sm = this.tree.getSelectionModel();
18917         sm.clearSelections();
18918         sm.select(this.dragData.node);
18919     }
18920 });
18921 }/*
18922  * Based on:
18923  * Ext JS Library 1.1.1
18924  * Copyright(c) 2006-2007, Ext JS, LLC.
18925  *
18926  * Originally Released Under LGPL - original licence link has changed is not relivant.
18927  *
18928  * Fork - LGPL
18929  * <script type="text/javascript">
18930  */
18931 /**
18932  * @class Roo.tree.TreeEditor
18933  * @extends Roo.Editor
18934  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18935  * as the editor field.
18936  * @constructor
18937  * @param {TreePanel} tree
18938  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18939  */
18940 Roo.tree.TreeEditor = function(tree, config){
18941     config = config || {};
18942     var field = config.events ? config : new Roo.form.TextField(config);
18943     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18944
18945     this.tree = tree;
18946
18947     tree.on('beforeclick', this.beforeNodeClick, this);
18948     tree.getTreeEl().on('mousedown', this.hide, this);
18949     this.on('complete', this.updateNode, this);
18950     this.on('beforestartedit', this.fitToTree, this);
18951     this.on('startedit', this.bindScroll, this, {delay:10});
18952     this.on('specialkey', this.onSpecialKey, this);
18953 };
18954
18955 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18956     /**
18957      * @cfg {String} alignment
18958      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18959      */
18960     alignment: "l-l",
18961     // inherit
18962     autoSize: false,
18963     /**
18964      * @cfg {Boolean} hideEl
18965      * True to hide the bound element while the editor is displayed (defaults to false)
18966      */
18967     hideEl : false,
18968     /**
18969      * @cfg {String} cls
18970      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18971      */
18972     cls: "x-small-editor x-tree-editor",
18973     /**
18974      * @cfg {Boolean} shim
18975      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18976      */
18977     shim:false,
18978     // inherit
18979     shadow:"frame",
18980     /**
18981      * @cfg {Number} maxWidth
18982      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18983      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18984      * scroll and client offsets into account prior to each edit.
18985      */
18986     maxWidth: 250,
18987
18988     editDelay : 350,
18989
18990     // private
18991     fitToTree : function(ed, el){
18992         var td = this.tree.getTreeEl().dom, nd = el.dom;
18993         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18994             td.scrollLeft = nd.offsetLeft;
18995         }
18996         var w = Math.min(
18997                 this.maxWidth,
18998                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18999         this.setSize(w, '');
19000     },
19001
19002     // private
19003     triggerEdit : function(node){
19004         this.completeEdit();
19005         this.editNode = node;
19006         this.startEdit(node.ui.textNode, node.text);
19007     },
19008
19009     // private
19010     bindScroll : function(){
19011         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19012     },
19013
19014     // private
19015     beforeNodeClick : function(node, e){
19016         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19017         this.lastClick = new Date();
19018         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19019             e.stopEvent();
19020             this.triggerEdit(node);
19021             return false;
19022         }
19023     },
19024
19025     // private
19026     updateNode : function(ed, value){
19027         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19028         this.editNode.setText(value);
19029     },
19030
19031     // private
19032     onHide : function(){
19033         Roo.tree.TreeEditor.superclass.onHide.call(this);
19034         if(this.editNode){
19035             this.editNode.ui.focus();
19036         }
19037     },
19038
19039     // private
19040     onSpecialKey : function(field, e){
19041         var k = e.getKey();
19042         if(k == e.ESC){
19043             e.stopEvent();
19044             this.cancelEdit();
19045         }else if(k == e.ENTER && !e.hasModifier()){
19046             e.stopEvent();
19047             this.completeEdit();
19048         }
19049     }
19050 });//<Script type="text/javascript">
19051 /*
19052  * Based on:
19053  * Ext JS Library 1.1.1
19054  * Copyright(c) 2006-2007, Ext JS, LLC.
19055  *
19056  * Originally Released Under LGPL - original licence link has changed is not relivant.
19057  *
19058  * Fork - LGPL
19059  * <script type="text/javascript">
19060  */
19061  
19062 /**
19063  * Not documented??? - probably should be...
19064  */
19065
19066 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19067     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19068     
19069     renderElements : function(n, a, targetNode, bulkRender){
19070         //consel.log("renderElements?");
19071         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19072
19073         var t = n.getOwnerTree();
19074         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19075         
19076         var cols = t.columns;
19077         var bw = t.borderWidth;
19078         var c = cols[0];
19079         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19080          var cb = typeof a.checked == "boolean";
19081         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19082         var colcls = 'x-t-' + tid + '-c0';
19083         var buf = [
19084             '<li class="x-tree-node">',
19085             
19086                 
19087                 '<div class="x-tree-node-el ', a.cls,'">',
19088                     // extran...
19089                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19090                 
19091                 
19092                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19093                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19094                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19095                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19096                            (a.iconCls ? ' '+a.iconCls : ''),
19097                            '" unselectable="on" />',
19098                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19099                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19100                              
19101                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19103                             '<span unselectable="on" qtip="' + tx + '">',
19104                              tx,
19105                              '</span></a>' ,
19106                     '</div>',
19107                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19108                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19109                  ];
19110         for(var i = 1, len = cols.length; i < len; i++){
19111             c = cols[i];
19112             colcls = 'x-t-' + tid + '-c' +i;
19113             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19114             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19115                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19116                       "</div>");
19117          }
19118          
19119          buf.push(
19120             '</a>',
19121             '<div class="x-clear"></div></div>',
19122             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19123             "</li>");
19124         
19125         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19126             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19127                                 n.nextSibling.ui.getEl(), buf.join(""));
19128         }else{
19129             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19130         }
19131         var el = this.wrap.firstChild;
19132         this.elRow = el;
19133         this.elNode = el.firstChild;
19134         this.ranchor = el.childNodes[1];
19135         this.ctNode = this.wrap.childNodes[1];
19136         var cs = el.firstChild.childNodes;
19137         this.indentNode = cs[0];
19138         this.ecNode = cs[1];
19139         this.iconNode = cs[2];
19140         var index = 3;
19141         if(cb){
19142             this.checkbox = cs[3];
19143             index++;
19144         }
19145         this.anchor = cs[index];
19146         
19147         this.textNode = cs[index].firstChild;
19148         
19149         //el.on("click", this.onClick, this);
19150         //el.on("dblclick", this.onDblClick, this);
19151         
19152         
19153        // console.log(this);
19154     },
19155     initEvents : function(){
19156         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19157         
19158             
19159         var a = this.ranchor;
19160
19161         var el = Roo.get(a);
19162
19163         if(Roo.isOpera){ // opera render bug ignores the CSS
19164             el.setStyle("text-decoration", "none");
19165         }
19166
19167         el.on("click", this.onClick, this);
19168         el.on("dblclick", this.onDblClick, this);
19169         el.on("contextmenu", this.onContextMenu, this);
19170         
19171     },
19172     
19173     /*onSelectedChange : function(state){
19174         if(state){
19175             this.focus();
19176             this.addClass("x-tree-selected");
19177         }else{
19178             //this.blur();
19179             this.removeClass("x-tree-selected");
19180         }
19181     },*/
19182     addClass : function(cls){
19183         if(this.elRow){
19184             Roo.fly(this.elRow).addClass(cls);
19185         }
19186         
19187     },
19188     
19189     
19190     removeClass : function(cls){
19191         if(this.elRow){
19192             Roo.fly(this.elRow).removeClass(cls);
19193         }
19194     }
19195
19196     
19197     
19198 });//<Script type="text/javascript">
19199
19200 /*
19201  * Based on:
19202  * Ext JS Library 1.1.1
19203  * Copyright(c) 2006-2007, Ext JS, LLC.
19204  *
19205  * Originally Released Under LGPL - original licence link has changed is not relivant.
19206  *
19207  * Fork - LGPL
19208  * <script type="text/javascript">
19209  */
19210  
19211
19212 /**
19213  * @class Roo.tree.ColumnTree
19214  * @extends Roo.data.TreePanel
19215  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19216  * @cfg {int} borderWidth  compined right/left border allowance
19217  * @constructor
19218  * @param {String/HTMLElement/Element} el The container element
19219  * @param {Object} config
19220  */
19221 Roo.tree.ColumnTree =  function(el, config)
19222 {
19223    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19224    this.addEvents({
19225         /**
19226         * @event resize
19227         * Fire this event on a container when it resizes
19228         * @param {int} w Width
19229         * @param {int} h Height
19230         */
19231        "resize" : true
19232     });
19233     this.on('resize', this.onResize, this);
19234 };
19235
19236 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19237     //lines:false,
19238     
19239     
19240     borderWidth: Roo.isBorderBox ? 0 : 2, 
19241     headEls : false,
19242     
19243     render : function(){
19244         // add the header.....
19245        
19246         Roo.tree.ColumnTree.superclass.render.apply(this);
19247         
19248         this.el.addClass('x-column-tree');
19249         
19250         this.headers = this.el.createChild(
19251             {cls:'x-tree-headers'},this.innerCt.dom);
19252    
19253         var cols = this.columns, c;
19254         var totalWidth = 0;
19255         this.headEls = [];
19256         var  len = cols.length;
19257         for(var i = 0; i < len; i++){
19258              c = cols[i];
19259              totalWidth += c.width;
19260             this.headEls.push(this.headers.createChild({
19261                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19262                  cn: {
19263                      cls:'x-tree-hd-text',
19264                      html: c.header
19265                  },
19266                  style:'width:'+(c.width-this.borderWidth)+'px;'
19267              }));
19268         }
19269         this.headers.createChild({cls:'x-clear'});
19270         // prevent floats from wrapping when clipped
19271         this.headers.setWidth(totalWidth);
19272         //this.innerCt.setWidth(totalWidth);
19273         this.innerCt.setStyle({ overflow: 'auto' });
19274         this.onResize(this.width, this.height);
19275              
19276         
19277     },
19278     onResize : function(w,h)
19279     {
19280         this.height = h;
19281         this.width = w;
19282         // resize cols..
19283         this.innerCt.setWidth(this.width);
19284         this.innerCt.setHeight(this.height-20);
19285         
19286         // headers...
19287         var cols = this.columns, c;
19288         var totalWidth = 0;
19289         var expEl = false;
19290         var len = cols.length;
19291         for(var i = 0; i < len; i++){
19292             c = cols[i];
19293             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19294                 // it's the expander..
19295                 expEl  = this.headEls[i];
19296                 continue;
19297             }
19298             totalWidth += c.width;
19299             
19300         }
19301         if (expEl) {
19302             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19303         }
19304         this.headers.setWidth(w-20);
19305
19306         
19307         
19308         
19309     }
19310 });
19311 /*
19312  * Based on:
19313  * Ext JS Library 1.1.1
19314  * Copyright(c) 2006-2007, Ext JS, LLC.
19315  *
19316  * Originally Released Under LGPL - original licence link has changed is not relivant.
19317  *
19318  * Fork - LGPL
19319  * <script type="text/javascript">
19320  */
19321  
19322 /**
19323  * @class Roo.menu.Menu
19324  * @extends Roo.util.Observable
19325  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19326  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19327  * @constructor
19328  * Creates a new Menu
19329  * @param {Object} config Configuration options
19330  */
19331 Roo.menu.Menu = function(config){
19332     Roo.apply(this, config);
19333     this.id = this.id || Roo.id();
19334     this.addEvents({
19335         /**
19336          * @event beforeshow
19337          * Fires before this menu is displayed
19338          * @param {Roo.menu.Menu} this
19339          */
19340         beforeshow : true,
19341         /**
19342          * @event beforehide
19343          * Fires before this menu is hidden
19344          * @param {Roo.menu.Menu} this
19345          */
19346         beforehide : true,
19347         /**
19348          * @event show
19349          * Fires after this menu is displayed
19350          * @param {Roo.menu.Menu} this
19351          */
19352         show : true,
19353         /**
19354          * @event hide
19355          * Fires after this menu is hidden
19356          * @param {Roo.menu.Menu} this
19357          */
19358         hide : true,
19359         /**
19360          * @event click
19361          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19362          * @param {Roo.menu.Menu} this
19363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19364          * @param {Roo.EventObject} e
19365          */
19366         click : true,
19367         /**
19368          * @event mouseover
19369          * Fires when the mouse is hovering over this menu
19370          * @param {Roo.menu.Menu} this
19371          * @param {Roo.EventObject} e
19372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19373          */
19374         mouseover : true,
19375         /**
19376          * @event mouseout
19377          * Fires when the mouse exits this menu
19378          * @param {Roo.menu.Menu} this
19379          * @param {Roo.EventObject} e
19380          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19381          */
19382         mouseout : true,
19383         /**
19384          * @event itemclick
19385          * Fires when a menu item contained in this menu is clicked
19386          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19387          * @param {Roo.EventObject} e
19388          */
19389         itemclick: true
19390     });
19391     if (this.registerMenu) {
19392         Roo.menu.MenuMgr.register(this);
19393     }
19394     
19395     var mis = this.items;
19396     this.items = new Roo.util.MixedCollection();
19397     if(mis){
19398         this.add.apply(this, mis);
19399     }
19400 };
19401
19402 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19403     /**
19404      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19405      */
19406     minWidth : 120,
19407     /**
19408      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19409      * for bottom-right shadow (defaults to "sides")
19410      */
19411     shadow : "sides",
19412     /**
19413      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19414      * this menu (defaults to "tl-tr?")
19415      */
19416     subMenuAlign : "tl-tr?",
19417     /**
19418      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19419      * relative to its element of origin (defaults to "tl-bl?")
19420      */
19421     defaultAlign : "tl-bl?",
19422     /**
19423      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19424      */
19425     allowOtherMenus : false,
19426     /**
19427      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19428      */
19429     registerMenu : true,
19430
19431     hidden:true,
19432
19433     // private
19434     render : function(){
19435         if(this.el){
19436             return;
19437         }
19438         var el = this.el = new Roo.Layer({
19439             cls: "x-menu",
19440             shadow:this.shadow,
19441             constrain: false,
19442             parentEl: this.parentEl || document.body,
19443             zindex:15000
19444         });
19445
19446         this.keyNav = new Roo.menu.MenuNav(this);
19447
19448         if(this.plain){
19449             el.addClass("x-menu-plain");
19450         }
19451         if(this.cls){
19452             el.addClass(this.cls);
19453         }
19454         // generic focus element
19455         this.focusEl = el.createChild({
19456             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19457         });
19458         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19459         ul.on("click", this.onClick, this);
19460         ul.on("mouseover", this.onMouseOver, this);
19461         ul.on("mouseout", this.onMouseOut, this);
19462         this.items.each(function(item){
19463             var li = document.createElement("li");
19464             li.className = "x-menu-list-item";
19465             ul.dom.appendChild(li);
19466             item.render(li, this);
19467         }, this);
19468         this.ul = ul;
19469         this.autoWidth();
19470     },
19471
19472     // private
19473     autoWidth : function(){
19474         var el = this.el, ul = this.ul;
19475         if(!el){
19476             return;
19477         }
19478         var w = this.width;
19479         if(w){
19480             el.setWidth(w);
19481         }else if(Roo.isIE){
19482             el.setWidth(this.minWidth);
19483             var t = el.dom.offsetWidth; // force recalc
19484             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19485         }
19486     },
19487
19488     // private
19489     delayAutoWidth : function(){
19490         if(this.rendered){
19491             if(!this.awTask){
19492                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19493             }
19494             this.awTask.delay(20);
19495         }
19496     },
19497
19498     // private
19499     findTargetItem : function(e){
19500         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19501         if(t && t.menuItemId){
19502             return this.items.get(t.menuItemId);
19503         }
19504     },
19505
19506     // private
19507     onClick : function(e){
19508         var t;
19509         if(t = this.findTargetItem(e)){
19510             t.onClick(e);
19511             this.fireEvent("click", this, t, e);
19512         }
19513     },
19514
19515     // private
19516     setActiveItem : function(item, autoExpand){
19517         if(item != this.activeItem){
19518             if(this.activeItem){
19519                 this.activeItem.deactivate();
19520             }
19521             this.activeItem = item;
19522             item.activate(autoExpand);
19523         }else if(autoExpand){
19524             item.expandMenu();
19525         }
19526     },
19527
19528     // private
19529     tryActivate : function(start, step){
19530         var items = this.items;
19531         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19532             var item = items.get(i);
19533             if(!item.disabled && item.canActivate){
19534                 this.setActiveItem(item, false);
19535                 return item;
19536             }
19537         }
19538         return false;
19539     },
19540
19541     // private
19542     onMouseOver : function(e){
19543         var t;
19544         if(t = this.findTargetItem(e)){
19545             if(t.canActivate && !t.disabled){
19546                 this.setActiveItem(t, true);
19547             }
19548         }
19549         this.fireEvent("mouseover", this, e, t);
19550     },
19551
19552     // private
19553     onMouseOut : function(e){
19554         var t;
19555         if(t = this.findTargetItem(e)){
19556             if(t == this.activeItem && t.shouldDeactivate(e)){
19557                 this.activeItem.deactivate();
19558                 delete this.activeItem;
19559             }
19560         }
19561         this.fireEvent("mouseout", this, e, t);
19562     },
19563
19564     /**
19565      * Read-only.  Returns true if the menu is currently displayed, else false.
19566      * @type Boolean
19567      */
19568     isVisible : function(){
19569         return this.el && !this.hidden;
19570     },
19571
19572     /**
19573      * Displays this menu relative to another element
19574      * @param {String/HTMLElement/Roo.Element} element The element to align to
19575      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19576      * the element (defaults to this.defaultAlign)
19577      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19578      */
19579     show : function(el, pos, parentMenu){
19580         this.parentMenu = parentMenu;
19581         if(!this.el){
19582             this.render();
19583         }
19584         this.fireEvent("beforeshow", this);
19585         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19586     },
19587
19588     /**
19589      * Displays this menu at a specific xy position
19590      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19591      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19592      */
19593     showAt : function(xy, parentMenu, /* private: */_e){
19594         this.parentMenu = parentMenu;
19595         if(!this.el){
19596             this.render();
19597         }
19598         if(_e !== false){
19599             this.fireEvent("beforeshow", this);
19600             xy = this.el.adjustForConstraints(xy);
19601         }
19602         this.el.setXY(xy);
19603         this.el.show();
19604         this.hidden = false;
19605         this.focus();
19606         this.fireEvent("show", this);
19607     },
19608
19609     focus : function(){
19610         if(!this.hidden){
19611             this.doFocus.defer(50, this);
19612         }
19613     },
19614
19615     doFocus : function(){
19616         if(!this.hidden){
19617             this.focusEl.focus();
19618         }
19619     },
19620
19621     /**
19622      * Hides this menu and optionally all parent menus
19623      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19624      */
19625     hide : function(deep){
19626         if(this.el && this.isVisible()){
19627             this.fireEvent("beforehide", this);
19628             if(this.activeItem){
19629                 this.activeItem.deactivate();
19630                 this.activeItem = null;
19631             }
19632             this.el.hide();
19633             this.hidden = true;
19634             this.fireEvent("hide", this);
19635         }
19636         if(deep === true && this.parentMenu){
19637             this.parentMenu.hide(true);
19638         }
19639     },
19640
19641     /**
19642      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19643      * Any of the following are valid:
19644      * <ul>
19645      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19646      * <li>An HTMLElement object which will be converted to a menu item</li>
19647      * <li>A menu item config object that will be created as a new menu item</li>
19648      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19649      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19650      * </ul>
19651      * Usage:
19652      * <pre><code>
19653 // Create the menu
19654 var menu = new Roo.menu.Menu();
19655
19656 // Create a menu item to add by reference
19657 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19658
19659 // Add a bunch of items at once using different methods.
19660 // Only the last item added will be returned.
19661 var item = menu.add(
19662     menuItem,                // add existing item by ref
19663     'Dynamic Item',          // new TextItem
19664     '-',                     // new separator
19665     { text: 'Config Item' }  // new item by config
19666 );
19667 </code></pre>
19668      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19669      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19670      */
19671     add : function(){
19672         var a = arguments, l = a.length, item;
19673         for(var i = 0; i < l; i++){
19674             var el = a[i];
19675             if ((typeof(el) == "object") && el.xtype && el.xns) {
19676                 el = Roo.factory(el, Roo.menu);
19677             }
19678             
19679             if(el.render){ // some kind of Item
19680                 item = this.addItem(el);
19681             }else if(typeof el == "string"){ // string
19682                 if(el == "separator" || el == "-"){
19683                     item = this.addSeparator();
19684                 }else{
19685                     item = this.addText(el);
19686                 }
19687             }else if(el.tagName || el.el){ // element
19688                 item = this.addElement(el);
19689             }else if(typeof el == "object"){ // must be menu item config?
19690                 item = this.addMenuItem(el);
19691             }
19692         }
19693         return item;
19694     },
19695
19696     /**
19697      * Returns this menu's underlying {@link Roo.Element} object
19698      * @return {Roo.Element} The element
19699      */
19700     getEl : function(){
19701         if(!this.el){
19702             this.render();
19703         }
19704         return this.el;
19705     },
19706
19707     /**
19708      * Adds a separator bar to the menu
19709      * @return {Roo.menu.Item} The menu item that was added
19710      */
19711     addSeparator : function(){
19712         return this.addItem(new Roo.menu.Separator());
19713     },
19714
19715     /**
19716      * Adds an {@link Roo.Element} object to the menu
19717      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19718      * @return {Roo.menu.Item} The menu item that was added
19719      */
19720     addElement : function(el){
19721         return this.addItem(new Roo.menu.BaseItem(el));
19722     },
19723
19724     /**
19725      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19726      * @param {Roo.menu.Item} item The menu item to add
19727      * @return {Roo.menu.Item} The menu item that was added
19728      */
19729     addItem : function(item){
19730         this.items.add(item);
19731         if(this.ul){
19732             var li = document.createElement("li");
19733             li.className = "x-menu-list-item";
19734             this.ul.dom.appendChild(li);
19735             item.render(li, this);
19736             this.delayAutoWidth();
19737         }
19738         return item;
19739     },
19740
19741     /**
19742      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19743      * @param {Object} config A MenuItem config object
19744      * @return {Roo.menu.Item} The menu item that was added
19745      */
19746     addMenuItem : function(config){
19747         if(!(config instanceof Roo.menu.Item)){
19748             if(typeof config.checked == "boolean"){ // must be check menu item config?
19749                 config = new Roo.menu.CheckItem(config);
19750             }else{
19751                 config = new Roo.menu.Item(config);
19752             }
19753         }
19754         return this.addItem(config);
19755     },
19756
19757     /**
19758      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19759      * @param {String} text The text to display in the menu item
19760      * @return {Roo.menu.Item} The menu item that was added
19761      */
19762     addText : function(text){
19763         return this.addItem(new Roo.menu.TextItem({ text : text }));
19764     },
19765
19766     /**
19767      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19768      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19769      * @param {Roo.menu.Item} item The menu item to add
19770      * @return {Roo.menu.Item} The menu item that was added
19771      */
19772     insert : function(index, item){
19773         this.items.insert(index, item);
19774         if(this.ul){
19775             var li = document.createElement("li");
19776             li.className = "x-menu-list-item";
19777             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19778             item.render(li, this);
19779             this.delayAutoWidth();
19780         }
19781         return item;
19782     },
19783
19784     /**
19785      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19786      * @param {Roo.menu.Item} item The menu item to remove
19787      */
19788     remove : function(item){
19789         this.items.removeKey(item.id);
19790         item.destroy();
19791     },
19792
19793     /**
19794      * Removes and destroys all items in the menu
19795      */
19796     removeAll : function(){
19797         var f;
19798         while(f = this.items.first()){
19799             this.remove(f);
19800         }
19801     }
19802 });
19803
19804 // MenuNav is a private utility class used internally by the Menu
19805 Roo.menu.MenuNav = function(menu){
19806     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19807     this.scope = this.menu = menu;
19808 };
19809
19810 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19811     doRelay : function(e, h){
19812         var k = e.getKey();
19813         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19814             this.menu.tryActivate(0, 1);
19815             return false;
19816         }
19817         return h.call(this.scope || this, e, this.menu);
19818     },
19819
19820     up : function(e, m){
19821         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19822             m.tryActivate(m.items.length-1, -1);
19823         }
19824     },
19825
19826     down : function(e, m){
19827         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19828             m.tryActivate(0, 1);
19829         }
19830     },
19831
19832     right : function(e, m){
19833         if(m.activeItem){
19834             m.activeItem.expandMenu(true);
19835         }
19836     },
19837
19838     left : function(e, m){
19839         m.hide();
19840         if(m.parentMenu && m.parentMenu.activeItem){
19841             m.parentMenu.activeItem.activate();
19842         }
19843     },
19844
19845     enter : function(e, m){
19846         if(m.activeItem){
19847             e.stopPropagation();
19848             m.activeItem.onClick(e);
19849             m.fireEvent("click", this, m.activeItem);
19850             return true;
19851         }
19852     }
19853 });/*
19854  * Based on:
19855  * Ext JS Library 1.1.1
19856  * Copyright(c) 2006-2007, Ext JS, LLC.
19857  *
19858  * Originally Released Under LGPL - original licence link has changed is not relivant.
19859  *
19860  * Fork - LGPL
19861  * <script type="text/javascript">
19862  */
19863  
19864 /**
19865  * @class Roo.menu.MenuMgr
19866  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19867  * @singleton
19868  */
19869 Roo.menu.MenuMgr = function(){
19870    var menus, active, groups = {}, attached = false, lastShow = new Date();
19871
19872    // private - called when first menu is created
19873    function init(){
19874        menus = {};
19875        active = new Roo.util.MixedCollection();
19876        Roo.get(document).addKeyListener(27, function(){
19877            if(active.length > 0){
19878                hideAll();
19879            }
19880        });
19881    }
19882
19883    // private
19884    function hideAll(){
19885        if(active && active.length > 0){
19886            var c = active.clone();
19887            c.each(function(m){
19888                m.hide();
19889            });
19890        }
19891    }
19892
19893    // private
19894    function onHide(m){
19895        active.remove(m);
19896        if(active.length < 1){
19897            Roo.get(document).un("mousedown", onMouseDown);
19898            attached = false;
19899        }
19900    }
19901
19902    // private
19903    function onShow(m){
19904        var last = active.last();
19905        lastShow = new Date();
19906        active.add(m);
19907        if(!attached){
19908            Roo.get(document).on("mousedown", onMouseDown);
19909            attached = true;
19910        }
19911        if(m.parentMenu){
19912           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19913           m.parentMenu.activeChild = m;
19914        }else if(last && last.isVisible()){
19915           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19916        }
19917    }
19918
19919    // private
19920    function onBeforeHide(m){
19921        if(m.activeChild){
19922            m.activeChild.hide();
19923        }
19924        if(m.autoHideTimer){
19925            clearTimeout(m.autoHideTimer);
19926            delete m.autoHideTimer;
19927        }
19928    }
19929
19930    // private
19931    function onBeforeShow(m){
19932        var pm = m.parentMenu;
19933        if(!pm && !m.allowOtherMenus){
19934            hideAll();
19935        }else if(pm && pm.activeChild && active != m){
19936            pm.activeChild.hide();
19937        }
19938    }
19939
19940    // private
19941    function onMouseDown(e){
19942        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19943            hideAll();
19944        }
19945    }
19946
19947    // private
19948    function onBeforeCheck(mi, state){
19949        if(state){
19950            var g = groups[mi.group];
19951            for(var i = 0, l = g.length; i < l; i++){
19952                if(g[i] != mi){
19953                    g[i].setChecked(false);
19954                }
19955            }
19956        }
19957    }
19958
19959    return {
19960
19961        /**
19962         * Hides all menus that are currently visible
19963         */
19964        hideAll : function(){
19965             hideAll();  
19966        },
19967
19968        // private
19969        register : function(menu){
19970            if(!menus){
19971                init();
19972            }
19973            menus[menu.id] = menu;
19974            menu.on("beforehide", onBeforeHide);
19975            menu.on("hide", onHide);
19976            menu.on("beforeshow", onBeforeShow);
19977            menu.on("show", onShow);
19978            var g = menu.group;
19979            if(g && menu.events["checkchange"]){
19980                if(!groups[g]){
19981                    groups[g] = [];
19982                }
19983                groups[g].push(menu);
19984                menu.on("checkchange", onCheck);
19985            }
19986        },
19987
19988         /**
19989          * Returns a {@link Roo.menu.Menu} object
19990          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19991          * be used to generate and return a new Menu instance.
19992          */
19993        get : function(menu){
19994            if(typeof menu == "string"){ // menu id
19995                return menus[menu];
19996            }else if(menu.events){  // menu instance
19997                return menu;
19998            }else if(typeof menu.length == 'number'){ // array of menu items?
19999                return new Roo.menu.Menu({items:menu});
20000            }else{ // otherwise, must be a config
20001                return new Roo.menu.Menu(menu);
20002            }
20003        },
20004
20005        // private
20006        unregister : function(menu){
20007            delete menus[menu.id];
20008            menu.un("beforehide", onBeforeHide);
20009            menu.un("hide", onHide);
20010            menu.un("beforeshow", onBeforeShow);
20011            menu.un("show", onShow);
20012            var g = menu.group;
20013            if(g && menu.events["checkchange"]){
20014                groups[g].remove(menu);
20015                menu.un("checkchange", onCheck);
20016            }
20017        },
20018
20019        // private
20020        registerCheckable : function(menuItem){
20021            var g = menuItem.group;
20022            if(g){
20023                if(!groups[g]){
20024                    groups[g] = [];
20025                }
20026                groups[g].push(menuItem);
20027                menuItem.on("beforecheckchange", onBeforeCheck);
20028            }
20029        },
20030
20031        // private
20032        unregisterCheckable : function(menuItem){
20033            var g = menuItem.group;
20034            if(g){
20035                groups[g].remove(menuItem);
20036                menuItem.un("beforecheckchange", onBeforeCheck);
20037            }
20038        }
20039    };
20040 }();/*
20041  * Based on:
20042  * Ext JS Library 1.1.1
20043  * Copyright(c) 2006-2007, Ext JS, LLC.
20044  *
20045  * Originally Released Under LGPL - original licence link has changed is not relivant.
20046  *
20047  * Fork - LGPL
20048  * <script type="text/javascript">
20049  */
20050  
20051
20052 /**
20053  * @class Roo.menu.BaseItem
20054  * @extends Roo.Component
20055  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20056  * management and base configuration options shared by all menu components.
20057  * @constructor
20058  * Creates a new BaseItem
20059  * @param {Object} config Configuration options
20060  */
20061 Roo.menu.BaseItem = function(config){
20062     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20063
20064     this.addEvents({
20065         /**
20066          * @event click
20067          * Fires when this item is clicked
20068          * @param {Roo.menu.BaseItem} this
20069          * @param {Roo.EventObject} e
20070          */
20071         click: true,
20072         /**
20073          * @event activate
20074          * Fires when this item is activated
20075          * @param {Roo.menu.BaseItem} this
20076          */
20077         activate : true,
20078         /**
20079          * @event deactivate
20080          * Fires when this item is deactivated
20081          * @param {Roo.menu.BaseItem} this
20082          */
20083         deactivate : true
20084     });
20085
20086     if(this.handler){
20087         this.on("click", this.handler, this.scope, true);
20088     }
20089 };
20090
20091 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20092     /**
20093      * @cfg {Function} handler
20094      * A function that will handle the click event of this menu item (defaults to undefined)
20095      */
20096     /**
20097      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20098      */
20099     canActivate : false,
20100     /**
20101      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20102      */
20103     activeClass : "x-menu-item-active",
20104     /**
20105      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20106      */
20107     hideOnClick : true,
20108     /**
20109      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20110      */
20111     hideDelay : 100,
20112
20113     // private
20114     ctype: "Roo.menu.BaseItem",
20115
20116     // private
20117     actionMode : "container",
20118
20119     // private
20120     render : function(container, parentMenu){
20121         this.parentMenu = parentMenu;
20122         Roo.menu.BaseItem.superclass.render.call(this, container);
20123         this.container.menuItemId = this.id;
20124     },
20125
20126     // private
20127     onRender : function(container, position){
20128         this.el = Roo.get(this.el);
20129         container.dom.appendChild(this.el.dom);
20130     },
20131
20132     // private
20133     onClick : function(e){
20134         if(!this.disabled && this.fireEvent("click", this, e) !== false
20135                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20136             this.handleClick(e);
20137         }else{
20138             e.stopEvent();
20139         }
20140     },
20141
20142     // private
20143     activate : function(){
20144         if(this.disabled){
20145             return false;
20146         }
20147         var li = this.container;
20148         li.addClass(this.activeClass);
20149         this.region = li.getRegion().adjust(2, 2, -2, -2);
20150         this.fireEvent("activate", this);
20151         return true;
20152     },
20153
20154     // private
20155     deactivate : function(){
20156         this.container.removeClass(this.activeClass);
20157         this.fireEvent("deactivate", this);
20158     },
20159
20160     // private
20161     shouldDeactivate : function(e){
20162         return !this.region || !this.region.contains(e.getPoint());
20163     },
20164
20165     // private
20166     handleClick : function(e){
20167         if(this.hideOnClick){
20168             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20169         }
20170     },
20171
20172     // private
20173     expandMenu : function(autoActivate){
20174         // do nothing
20175     },
20176
20177     // private
20178     hideMenu : function(){
20179         // do nothing
20180     }
20181 });/*
20182  * Based on:
20183  * Ext JS Library 1.1.1
20184  * Copyright(c) 2006-2007, Ext JS, LLC.
20185  *
20186  * Originally Released Under LGPL - original licence link has changed is not relivant.
20187  *
20188  * Fork - LGPL
20189  * <script type="text/javascript">
20190  */
20191  
20192 /**
20193  * @class Roo.menu.Adapter
20194  * @extends Roo.menu.BaseItem
20195  * 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.
20196  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20197  * @constructor
20198  * Creates a new Adapter
20199  * @param {Object} config Configuration options
20200  */
20201 Roo.menu.Adapter = function(component, config){
20202     Roo.menu.Adapter.superclass.constructor.call(this, config);
20203     this.component = component;
20204 };
20205 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20206     // private
20207     canActivate : true,
20208
20209     // private
20210     onRender : function(container, position){
20211         this.component.render(container);
20212         this.el = this.component.getEl();
20213     },
20214
20215     // private
20216     activate : function(){
20217         if(this.disabled){
20218             return false;
20219         }
20220         this.component.focus();
20221         this.fireEvent("activate", this);
20222         return true;
20223     },
20224
20225     // private
20226     deactivate : function(){
20227         this.fireEvent("deactivate", this);
20228     },
20229
20230     // private
20231     disable : function(){
20232         this.component.disable();
20233         Roo.menu.Adapter.superclass.disable.call(this);
20234     },
20235
20236     // private
20237     enable : function(){
20238         this.component.enable();
20239         Roo.menu.Adapter.superclass.enable.call(this);
20240     }
20241 });/*
20242  * Based on:
20243  * Ext JS Library 1.1.1
20244  * Copyright(c) 2006-2007, Ext JS, LLC.
20245  *
20246  * Originally Released Under LGPL - original licence link has changed is not relivant.
20247  *
20248  * Fork - LGPL
20249  * <script type="text/javascript">
20250  */
20251
20252 /**
20253  * @class Roo.menu.TextItem
20254  * @extends Roo.menu.BaseItem
20255  * Adds a static text string to a menu, usually used as either a heading or group separator.
20256  * Note: old style constructor with text is still supported.
20257  * 
20258  * @constructor
20259  * Creates a new TextItem
20260  * @param {Object} cfg Configuration
20261  */
20262 Roo.menu.TextItem = function(cfg){
20263     if (typeof(cfg) == 'string') {
20264         this.text = cfg;
20265     } else {
20266         Roo.apply(this,cfg);
20267     }
20268     
20269     Roo.menu.TextItem.superclass.constructor.call(this);
20270 };
20271
20272 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20273     /**
20274      * @cfg {Boolean} text Text to show on item.
20275      */
20276     text : '',
20277     
20278     /**
20279      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20280      */
20281     hideOnClick : false,
20282     /**
20283      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20284      */
20285     itemCls : "x-menu-text",
20286
20287     // private
20288     onRender : function(){
20289         var s = document.createElement("span");
20290         s.className = this.itemCls;
20291         s.innerHTML = this.text;
20292         this.el = s;
20293         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20294     }
20295 });/*
20296  * Based on:
20297  * Ext JS Library 1.1.1
20298  * Copyright(c) 2006-2007, Ext JS, LLC.
20299  *
20300  * Originally Released Under LGPL - original licence link has changed is not relivant.
20301  *
20302  * Fork - LGPL
20303  * <script type="text/javascript">
20304  */
20305
20306 /**
20307  * @class Roo.menu.Separator
20308  * @extends Roo.menu.BaseItem
20309  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20310  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20311  * @constructor
20312  * @param {Object} config Configuration options
20313  */
20314 Roo.menu.Separator = function(config){
20315     Roo.menu.Separator.superclass.constructor.call(this, config);
20316 };
20317
20318 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20319     /**
20320      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20321      */
20322     itemCls : "x-menu-sep",
20323     /**
20324      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20325      */
20326     hideOnClick : false,
20327
20328     // private
20329     onRender : function(li){
20330         var s = document.createElement("span");
20331         s.className = this.itemCls;
20332         s.innerHTML = "&#160;";
20333         this.el = s;
20334         li.addClass("x-menu-sep-li");
20335         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20336     }
20337 });/*
20338  * Based on:
20339  * Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  *
20342  * Originally Released Under LGPL - original licence link has changed is not relivant.
20343  *
20344  * Fork - LGPL
20345  * <script type="text/javascript">
20346  */
20347 /**
20348  * @class Roo.menu.Item
20349  * @extends Roo.menu.BaseItem
20350  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20351  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20352  * activation and click handling.
20353  * @constructor
20354  * Creates a new Item
20355  * @param {Object} config Configuration options
20356  */
20357 Roo.menu.Item = function(config){
20358     Roo.menu.Item.superclass.constructor.call(this, config);
20359     if(this.menu){
20360         this.menu = Roo.menu.MenuMgr.get(this.menu);
20361     }
20362 };
20363 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20364     
20365     /**
20366      * @cfg {String} text
20367      * The text to show on the menu item.
20368      */
20369     text: '',
20370      /**
20371      * @cfg {String} HTML to render in menu
20372      * The text to show on the menu item (HTML version).
20373      */
20374     html: '',
20375     /**
20376      * @cfg {String} icon
20377      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20378      */
20379     icon: undefined,
20380     /**
20381      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20382      */
20383     itemCls : "x-menu-item",
20384     /**
20385      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20386      */
20387     canActivate : true,
20388     /**
20389      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20390      */
20391     showDelay: 200,
20392     // doc'd in BaseItem
20393     hideDelay: 200,
20394
20395     // private
20396     ctype: "Roo.menu.Item",
20397     
20398     // private
20399     onRender : function(container, position){
20400         var el = document.createElement("a");
20401         el.hideFocus = true;
20402         el.unselectable = "on";
20403         el.href = this.href || "#";
20404         if(this.hrefTarget){
20405             el.target = this.hrefTarget;
20406         }
20407         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20408         
20409         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20410         
20411         el.innerHTML = String.format(
20412                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20413                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20414         this.el = el;
20415         Roo.menu.Item.superclass.onRender.call(this, container, position);
20416     },
20417
20418     /**
20419      * Sets the text to display in this menu item
20420      * @param {String} text The text to display
20421      * @param {Boolean} isHTML true to indicate text is pure html.
20422      */
20423     setText : function(text, isHTML){
20424         if (isHTML) {
20425             this.html = text;
20426         } else {
20427             this.text = text;
20428             this.html = '';
20429         }
20430         if(this.rendered){
20431             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20432      
20433             this.el.update(String.format(
20434                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20435                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20436             this.parentMenu.autoWidth();
20437         }
20438     },
20439
20440     // private
20441     handleClick : function(e){
20442         if(!this.href){ // if no link defined, stop the event automatically
20443             e.stopEvent();
20444         }
20445         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20446     },
20447
20448     // private
20449     activate : function(autoExpand){
20450         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20451             this.focus();
20452             if(autoExpand){
20453                 this.expandMenu();
20454             }
20455         }
20456         return true;
20457     },
20458
20459     // private
20460     shouldDeactivate : function(e){
20461         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20462             if(this.menu && this.menu.isVisible()){
20463                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20464             }
20465             return true;
20466         }
20467         return false;
20468     },
20469
20470     // private
20471     deactivate : function(){
20472         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20473         this.hideMenu();
20474     },
20475
20476     // private
20477     expandMenu : function(autoActivate){
20478         if(!this.disabled && this.menu){
20479             clearTimeout(this.hideTimer);
20480             delete this.hideTimer;
20481             if(!this.menu.isVisible() && !this.showTimer){
20482                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20483             }else if (this.menu.isVisible() && autoActivate){
20484                 this.menu.tryActivate(0, 1);
20485             }
20486         }
20487     },
20488
20489     // private
20490     deferExpand : function(autoActivate){
20491         delete this.showTimer;
20492         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20493         if(autoActivate){
20494             this.menu.tryActivate(0, 1);
20495         }
20496     },
20497
20498     // private
20499     hideMenu : function(){
20500         clearTimeout(this.showTimer);
20501         delete this.showTimer;
20502         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20503             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20504         }
20505     },
20506
20507     // private
20508     deferHide : function(){
20509         delete this.hideTimer;
20510         this.menu.hide();
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522  
20523 /**
20524  * @class Roo.menu.CheckItem
20525  * @extends Roo.menu.Item
20526  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20527  * @constructor
20528  * Creates a new CheckItem
20529  * @param {Object} config Configuration options
20530  */
20531 Roo.menu.CheckItem = function(config){
20532     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20533     this.addEvents({
20534         /**
20535          * @event beforecheckchange
20536          * Fires before the checked value is set, providing an opportunity to cancel if needed
20537          * @param {Roo.menu.CheckItem} this
20538          * @param {Boolean} checked The new checked value that will be set
20539          */
20540         "beforecheckchange" : true,
20541         /**
20542          * @event checkchange
20543          * Fires after the checked value has been set
20544          * @param {Roo.menu.CheckItem} this
20545          * @param {Boolean} checked The checked value that was set
20546          */
20547         "checkchange" : true
20548     });
20549     if(this.checkHandler){
20550         this.on('checkchange', this.checkHandler, this.scope);
20551     }
20552 };
20553 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20554     /**
20555      * @cfg {String} group
20556      * All check items with the same group name will automatically be grouped into a single-select
20557      * radio button group (defaults to '')
20558      */
20559     /**
20560      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20561      */
20562     itemCls : "x-menu-item x-menu-check-item",
20563     /**
20564      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20565      */
20566     groupClass : "x-menu-group-item",
20567
20568     /**
20569      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20570      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20571      * initialized with checked = true will be rendered as checked.
20572      */
20573     checked: false,
20574
20575     // private
20576     ctype: "Roo.menu.CheckItem",
20577
20578     // private
20579     onRender : function(c){
20580         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20581         if(this.group){
20582             this.el.addClass(this.groupClass);
20583         }
20584         Roo.menu.MenuMgr.registerCheckable(this);
20585         if(this.checked){
20586             this.checked = false;
20587             this.setChecked(true, true);
20588         }
20589     },
20590
20591     // private
20592     destroy : function(){
20593         if(this.rendered){
20594             Roo.menu.MenuMgr.unregisterCheckable(this);
20595         }
20596         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20597     },
20598
20599     /**
20600      * Set the checked state of this item
20601      * @param {Boolean} checked The new checked value
20602      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20603      */
20604     setChecked : function(state, suppressEvent){
20605         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20606             if(this.container){
20607                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20608             }
20609             this.checked = state;
20610             if(suppressEvent !== true){
20611                 this.fireEvent("checkchange", this, state);
20612             }
20613         }
20614     },
20615
20616     // private
20617     handleClick : function(e){
20618        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20619            this.setChecked(!this.checked);
20620        }
20621        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20622     }
20623 });/*
20624  * Based on:
20625  * Ext JS Library 1.1.1
20626  * Copyright(c) 2006-2007, Ext JS, LLC.
20627  *
20628  * Originally Released Under LGPL - original licence link has changed is not relivant.
20629  *
20630  * Fork - LGPL
20631  * <script type="text/javascript">
20632  */
20633  
20634 /**
20635  * @class Roo.menu.DateItem
20636  * @extends Roo.menu.Adapter
20637  * A menu item that wraps the {@link Roo.DatPicker} component.
20638  * @constructor
20639  * Creates a new DateItem
20640  * @param {Object} config Configuration options
20641  */
20642 Roo.menu.DateItem = function(config){
20643     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20644     /** The Roo.DatePicker object @type Roo.DatePicker */
20645     this.picker = this.component;
20646     this.addEvents({select: true});
20647     
20648     this.picker.on("render", function(picker){
20649         picker.getEl().swallowEvent("click");
20650         picker.container.addClass("x-menu-date-item");
20651     });
20652
20653     this.picker.on("select", this.onSelect, this);
20654 };
20655
20656 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20657     // private
20658     onSelect : function(picker, date){
20659         this.fireEvent("select", this, date, picker);
20660         Roo.menu.DateItem.superclass.handleClick.call(this);
20661     }
20662 });/*
20663  * Based on:
20664  * Ext JS Library 1.1.1
20665  * Copyright(c) 2006-2007, Ext JS, LLC.
20666  *
20667  * Originally Released Under LGPL - original licence link has changed is not relivant.
20668  *
20669  * Fork - LGPL
20670  * <script type="text/javascript">
20671  */
20672  
20673 /**
20674  * @class Roo.menu.ColorItem
20675  * @extends Roo.menu.Adapter
20676  * A menu item that wraps the {@link Roo.ColorPalette} component.
20677  * @constructor
20678  * Creates a new ColorItem
20679  * @param {Object} config Configuration options
20680  */
20681 Roo.menu.ColorItem = function(config){
20682     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20683     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20684     this.palette = this.component;
20685     this.relayEvents(this.palette, ["select"]);
20686     if(this.selectHandler){
20687         this.on('select', this.selectHandler, this.scope);
20688     }
20689 };
20690 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20691  * Based on:
20692  * Ext JS Library 1.1.1
20693  * Copyright(c) 2006-2007, Ext JS, LLC.
20694  *
20695  * Originally Released Under LGPL - original licence link has changed is not relivant.
20696  *
20697  * Fork - LGPL
20698  * <script type="text/javascript">
20699  */
20700  
20701
20702 /**
20703  * @class Roo.menu.DateMenu
20704  * @extends Roo.menu.Menu
20705  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20706  * @constructor
20707  * Creates a new DateMenu
20708  * @param {Object} config Configuration options
20709  */
20710 Roo.menu.DateMenu = function(config){
20711     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20712     this.plain = true;
20713     var di = new Roo.menu.DateItem(config);
20714     this.add(di);
20715     /**
20716      * The {@link Roo.DatePicker} instance for this DateMenu
20717      * @type DatePicker
20718      */
20719     this.picker = di.picker;
20720     /**
20721      * @event select
20722      * @param {DatePicker} picker
20723      * @param {Date} date
20724      */
20725     this.relayEvents(di, ["select"]);
20726
20727     this.on('beforeshow', function(){
20728         if(this.picker){
20729             this.picker.hideMonthPicker(true);
20730         }
20731     }, this);
20732 };
20733 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20734     cls:'x-date-menu'
20735 });/*
20736  * Based on:
20737  * Ext JS Library 1.1.1
20738  * Copyright(c) 2006-2007, Ext JS, LLC.
20739  *
20740  * Originally Released Under LGPL - original licence link has changed is not relivant.
20741  *
20742  * Fork - LGPL
20743  * <script type="text/javascript">
20744  */
20745  
20746
20747 /**
20748  * @class Roo.menu.ColorMenu
20749  * @extends Roo.menu.Menu
20750  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20751  * @constructor
20752  * Creates a new ColorMenu
20753  * @param {Object} config Configuration options
20754  */
20755 Roo.menu.ColorMenu = function(config){
20756     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20757     this.plain = true;
20758     var ci = new Roo.menu.ColorItem(config);
20759     this.add(ci);
20760     /**
20761      * The {@link Roo.ColorPalette} instance for this ColorMenu
20762      * @type ColorPalette
20763      */
20764     this.palette = ci.palette;
20765     /**
20766      * @event select
20767      * @param {ColorPalette} palette
20768      * @param {String} color
20769      */
20770     this.relayEvents(ci, ["select"]);
20771 };
20772 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20773  * Based on:
20774  * Ext JS Library 1.1.1
20775  * Copyright(c) 2006-2007, Ext JS, LLC.
20776  *
20777  * Originally Released Under LGPL - original licence link has changed is not relivant.
20778  *
20779  * Fork - LGPL
20780  * <script type="text/javascript">
20781  */
20782  
20783 /**
20784  * @class Roo.form.Field
20785  * @extends Roo.BoxComponent
20786  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20787  * @constructor
20788  * Creates a new Field
20789  * @param {Object} config Configuration options
20790  */
20791 Roo.form.Field = function(config){
20792     Roo.form.Field.superclass.constructor.call(this, config);
20793 };
20794
20795 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20796     /**
20797      * @cfg {String} fieldLabel Label to use when rendering a form.
20798      */
20799        /**
20800      * @cfg {String} qtip Mouse over tip
20801      */
20802      
20803     /**
20804      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20805      */
20806     invalidClass : "x-form-invalid",
20807     /**
20808      * @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")
20809      */
20810     invalidText : "The value in this field is invalid",
20811     /**
20812      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20813      */
20814     focusClass : "x-form-focus",
20815     /**
20816      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20817       automatic validation (defaults to "keyup").
20818      */
20819     validationEvent : "keyup",
20820     /**
20821      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20822      */
20823     validateOnBlur : true,
20824     /**
20825      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20826      */
20827     validationDelay : 250,
20828     /**
20829      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20830      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20831      */
20832     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20833     /**
20834      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20835      */
20836     fieldClass : "x-form-field",
20837     /**
20838      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20839      *<pre>
20840 Value         Description
20841 -----------   ----------------------------------------------------------------------
20842 qtip          Display a quick tip when the user hovers over the field
20843 title         Display a default browser title attribute popup
20844 under         Add a block div beneath the field containing the error text
20845 side          Add an error icon to the right of the field with a popup on hover
20846 [element id]  Add the error text directly to the innerHTML of the specified element
20847 </pre>
20848      */
20849     msgTarget : 'qtip',
20850     /**
20851      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20852      */
20853     msgFx : 'normal',
20854
20855     /**
20856      * @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.
20857      */
20858     readOnly : false,
20859
20860     /**
20861      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20862      */
20863     disabled : false,
20864
20865     /**
20866      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20867      */
20868     inputType : undefined,
20869     
20870     /**
20871      * @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).
20872          */
20873         tabIndex : undefined,
20874         
20875     // private
20876     isFormField : true,
20877
20878     // private
20879     hasFocus : false,
20880     /**
20881      * @property {Roo.Element} fieldEl
20882      * Element Containing the rendered Field (with label etc.)
20883      */
20884     /**
20885      * @cfg {Mixed} value A value to initialize this field with.
20886      */
20887     value : undefined,
20888
20889     /**
20890      * @cfg {String} name The field's HTML name attribute.
20891      */
20892     /**
20893      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20894      */
20895
20896         // private ??
20897         initComponent : function(){
20898         Roo.form.Field.superclass.initComponent.call(this);
20899         this.addEvents({
20900             /**
20901              * @event focus
20902              * Fires when this field receives input focus.
20903              * @param {Roo.form.Field} this
20904              */
20905             focus : true,
20906             /**
20907              * @event blur
20908              * Fires when this field loses input focus.
20909              * @param {Roo.form.Field} this
20910              */
20911             blur : true,
20912             /**
20913              * @event specialkey
20914              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20915              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20916              * @param {Roo.form.Field} this
20917              * @param {Roo.EventObject} e The event object
20918              */
20919             specialkey : true,
20920             /**
20921              * @event change
20922              * Fires just before the field blurs if the field value has changed.
20923              * @param {Roo.form.Field} this
20924              * @param {Mixed} newValue The new value
20925              * @param {Mixed} oldValue The original value
20926              */
20927             change : true,
20928             /**
20929              * @event invalid
20930              * Fires after the field has been marked as invalid.
20931              * @param {Roo.form.Field} this
20932              * @param {String} msg The validation message
20933              */
20934             invalid : true,
20935             /**
20936              * @event valid
20937              * Fires after the field has been validated with no errors.
20938              * @param {Roo.form.Field} this
20939              */
20940             valid : true,
20941              /**
20942              * @event keyup
20943              * Fires after the key up
20944              * @param {Roo.form.Field} this
20945              * @param {Roo.EventObject}  e The event Object
20946              */
20947             keyup : true
20948         });
20949     },
20950
20951     /**
20952      * Returns the name attribute of the field if available
20953      * @return {String} name The field name
20954      */
20955     getName: function(){
20956          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20957     },
20958
20959     // private
20960     onRender : function(ct, position){
20961         Roo.form.Field.superclass.onRender.call(this, ct, position);
20962         if(!this.el){
20963             var cfg = this.getAutoCreate();
20964             if(!cfg.name){
20965                 cfg.name = this.name || this.id;
20966             }
20967             if(this.inputType){
20968                 cfg.type = this.inputType;
20969             }
20970             this.el = ct.createChild(cfg, position);
20971         }
20972         var type = this.el.dom.type;
20973         if(type){
20974             if(type == 'password'){
20975                 type = 'text';
20976             }
20977             this.el.addClass('x-form-'+type);
20978         }
20979         if(this.readOnly){
20980             this.el.dom.readOnly = true;
20981         }
20982         if(this.tabIndex !== undefined){
20983             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20984         }
20985
20986         this.el.addClass([this.fieldClass, this.cls]);
20987         this.initValue();
20988     },
20989
20990     /**
20991      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20992      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20993      * @return {Roo.form.Field} this
20994      */
20995     applyTo : function(target){
20996         this.allowDomMove = false;
20997         this.el = Roo.get(target);
20998         this.render(this.el.dom.parentNode);
20999         return this;
21000     },
21001
21002     // private
21003     initValue : function(){
21004         if(this.value !== undefined){
21005             this.setValue(this.value);
21006         }else if(this.el.dom.value.length > 0){
21007             this.setValue(this.el.dom.value);
21008         }
21009     },
21010
21011     /**
21012      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21013      */
21014     isDirty : function() {
21015         if(this.disabled) {
21016             return false;
21017         }
21018         return String(this.getValue()) !== String(this.originalValue);
21019     },
21020
21021     // private
21022     afterRender : function(){
21023         Roo.form.Field.superclass.afterRender.call(this);
21024         this.initEvents();
21025     },
21026
21027     // private
21028     fireKey : function(e){
21029         //Roo.log('field ' + e.getKey());
21030         if(e.isNavKeyPress()){
21031             this.fireEvent("specialkey", this, e);
21032         }
21033     },
21034
21035     /**
21036      * Resets the current field value to the originally loaded value and clears any validation messages
21037      */
21038     reset : function(){
21039         this.setValue(this.originalValue);
21040         this.clearInvalid();
21041     },
21042
21043     // private
21044     initEvents : function(){
21045         // safari killled keypress - so keydown is now used..
21046         this.el.on("keydown" , this.fireKey,  this);
21047         this.el.on("focus", this.onFocus,  this);
21048         this.el.on("blur", this.onBlur,  this);
21049         this.el.relayEvent('keyup', this);
21050
21051         // reference to original value for reset
21052         this.originalValue = this.getValue();
21053     },
21054
21055     // private
21056     onFocus : function(){
21057         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21058             this.el.addClass(this.focusClass);
21059         }
21060         if(!this.hasFocus){
21061             this.hasFocus = true;
21062             this.startValue = this.getValue();
21063             this.fireEvent("focus", this);
21064         }
21065     },
21066
21067     beforeBlur : Roo.emptyFn,
21068
21069     // private
21070     onBlur : function(){
21071         this.beforeBlur();
21072         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21073             this.el.removeClass(this.focusClass);
21074         }
21075         this.hasFocus = false;
21076         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21077             this.validate();
21078         }
21079         var v = this.getValue();
21080         if(String(v) !== String(this.startValue)){
21081             this.fireEvent('change', this, v, this.startValue);
21082         }
21083         this.fireEvent("blur", this);
21084     },
21085
21086     /**
21087      * Returns whether or not the field value is currently valid
21088      * @param {Boolean} preventMark True to disable marking the field invalid
21089      * @return {Boolean} True if the value is valid, else false
21090      */
21091     isValid : function(preventMark){
21092         if(this.disabled){
21093             return true;
21094         }
21095         var restore = this.preventMark;
21096         this.preventMark = preventMark === true;
21097         var v = this.validateValue(this.processValue(this.getRawValue()));
21098         this.preventMark = restore;
21099         return v;
21100     },
21101
21102     /**
21103      * Validates the field value
21104      * @return {Boolean} True if the value is valid, else false
21105      */
21106     validate : function(){
21107         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21108             this.clearInvalid();
21109             return true;
21110         }
21111         return false;
21112     },
21113
21114     processValue : function(value){
21115         return value;
21116     },
21117
21118     // private
21119     // Subclasses should provide the validation implementation by overriding this
21120     validateValue : function(value){
21121         return true;
21122     },
21123
21124     /**
21125      * Mark this field as invalid
21126      * @param {String} msg The validation message
21127      */
21128     markInvalid : function(msg){
21129         if(!this.rendered || this.preventMark){ // not rendered
21130             return;
21131         }
21132         this.el.addClass(this.invalidClass);
21133         msg = msg || this.invalidText;
21134         switch(this.msgTarget){
21135             case 'qtip':
21136                 this.el.dom.qtip = msg;
21137                 this.el.dom.qclass = 'x-form-invalid-tip';
21138                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21139                     Roo.QuickTips.enable();
21140                 }
21141                 break;
21142             case 'title':
21143                 this.el.dom.title = msg;
21144                 break;
21145             case 'under':
21146                 if(!this.errorEl){
21147                     var elp = this.el.findParent('.x-form-element', 5, true);
21148                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21149                     this.errorEl.setWidth(elp.getWidth(true)-20);
21150                 }
21151                 this.errorEl.update(msg);
21152                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21153                 break;
21154             case 'side':
21155                 if(!this.errorIcon){
21156                     var elp = this.el.findParent('.x-form-element', 5, true);
21157                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21158                 }
21159                 this.alignErrorIcon();
21160                 this.errorIcon.dom.qtip = msg;
21161                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21162                 this.errorIcon.show();
21163                 this.on('resize', this.alignErrorIcon, this);
21164                 break;
21165             default:
21166                 var t = Roo.getDom(this.msgTarget);
21167                 t.innerHTML = msg;
21168                 t.style.display = this.msgDisplay;
21169                 break;
21170         }
21171         this.fireEvent('invalid', this, msg);
21172     },
21173
21174     // private
21175     alignErrorIcon : function(){
21176         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21177     },
21178
21179     /**
21180      * Clear any invalid styles/messages for this field
21181      */
21182     clearInvalid : function(){
21183         if(!this.rendered || this.preventMark){ // not rendered
21184             return;
21185         }
21186         this.el.removeClass(this.invalidClass);
21187         switch(this.msgTarget){
21188             case 'qtip':
21189                 this.el.dom.qtip = '';
21190                 break;
21191             case 'title':
21192                 this.el.dom.title = '';
21193                 break;
21194             case 'under':
21195                 if(this.errorEl){
21196                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21197                 }
21198                 break;
21199             case 'side':
21200                 if(this.errorIcon){
21201                     this.errorIcon.dom.qtip = '';
21202                     this.errorIcon.hide();
21203                     this.un('resize', this.alignErrorIcon, this);
21204                 }
21205                 break;
21206             default:
21207                 var t = Roo.getDom(this.msgTarget);
21208                 t.innerHTML = '';
21209                 t.style.display = 'none';
21210                 break;
21211         }
21212         this.fireEvent('valid', this);
21213     },
21214
21215     /**
21216      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21217      * @return {Mixed} value The field value
21218      */
21219     getRawValue : function(){
21220         var v = this.el.getValue();
21221         if(v === this.emptyText){
21222             v = '';
21223         }
21224         return v;
21225     },
21226
21227     /**
21228      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21229      * @return {Mixed} value The field value
21230      */
21231     getValue : function(){
21232         var v = this.el.getValue();
21233         if(v === this.emptyText || v === undefined){
21234             v = '';
21235         }
21236         return v;
21237     },
21238
21239     /**
21240      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21241      * @param {Mixed} value The value to set
21242      */
21243     setRawValue : function(v){
21244         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21245     },
21246
21247     /**
21248      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21249      * @param {Mixed} value The value to set
21250      */
21251     setValue : function(v){
21252         this.value = v;
21253         if(this.rendered){
21254             this.el.dom.value = (v === null || v === undefined ? '' : v);
21255             this.validate();
21256         }
21257     },
21258
21259     adjustSize : function(w, h){
21260         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21261         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21262         return s;
21263     },
21264
21265     adjustWidth : function(tag, w){
21266         tag = tag.toLowerCase();
21267         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21268             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21269                 if(tag == 'input'){
21270                     return w + 2;
21271                 }
21272                 if(tag = 'textarea'){
21273                     return w-2;
21274                 }
21275             }else if(Roo.isOpera){
21276                 if(tag == 'input'){
21277                     return w + 2;
21278                 }
21279                 if(tag = 'textarea'){
21280                     return w-2;
21281                 }
21282             }
21283         }
21284         return w;
21285     }
21286 });
21287
21288
21289 // anything other than normal should be considered experimental
21290 Roo.form.Field.msgFx = {
21291     normal : {
21292         show: function(msgEl, f){
21293             msgEl.setDisplayed('block');
21294         },
21295
21296         hide : function(msgEl, f){
21297             msgEl.setDisplayed(false).update('');
21298         }
21299     },
21300
21301     slide : {
21302         show: function(msgEl, f){
21303             msgEl.slideIn('t', {stopFx:true});
21304         },
21305
21306         hide : function(msgEl, f){
21307             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21308         }
21309     },
21310
21311     slideRight : {
21312         show: function(msgEl, f){
21313             msgEl.fixDisplay();
21314             msgEl.alignTo(f.el, 'tl-tr');
21315             msgEl.slideIn('l', {stopFx:true});
21316         },
21317
21318         hide : function(msgEl, f){
21319             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21320         }
21321     }
21322 };/*
21323  * Based on:
21324  * Ext JS Library 1.1.1
21325  * Copyright(c) 2006-2007, Ext JS, LLC.
21326  *
21327  * Originally Released Under LGPL - original licence link has changed is not relivant.
21328  *
21329  * Fork - LGPL
21330  * <script type="text/javascript">
21331  */
21332  
21333
21334 /**
21335  * @class Roo.form.TextField
21336  * @extends Roo.form.Field
21337  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21338  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21339  * @constructor
21340  * Creates a new TextField
21341  * @param {Object} config Configuration options
21342  */
21343 Roo.form.TextField = function(config){
21344     Roo.form.TextField.superclass.constructor.call(this, config);
21345     this.addEvents({
21346         /**
21347          * @event autosize
21348          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21349          * according to the default logic, but this event provides a hook for the developer to apply additional
21350          * logic at runtime to resize the field if needed.
21351              * @param {Roo.form.Field} this This text field
21352              * @param {Number} width The new field width
21353              */
21354         autosize : true
21355     });
21356 };
21357
21358 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21359     /**
21360      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21361      */
21362     grow : false,
21363     /**
21364      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21365      */
21366     growMin : 30,
21367     /**
21368      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21369      */
21370     growMax : 800,
21371     /**
21372      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21373      */
21374     vtype : null,
21375     /**
21376      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21377      */
21378     maskRe : null,
21379     /**
21380      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21381      */
21382     disableKeyFilter : false,
21383     /**
21384      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21385      */
21386     allowBlank : true,
21387     /**
21388      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21389      */
21390     minLength : 0,
21391     /**
21392      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21393      */
21394     maxLength : Number.MAX_VALUE,
21395     /**
21396      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21397      */
21398     minLengthText : "The minimum length for this field is {0}",
21399     /**
21400      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21401      */
21402     maxLengthText : "The maximum length for this field is {0}",
21403     /**
21404      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21405      */
21406     selectOnFocus : false,
21407     /**
21408      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21409      */
21410     blankText : "This field is required",
21411     /**
21412      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21413      * If available, this function will be called only after the basic validators all return true, and will be passed the
21414      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21415      */
21416     validator : null,
21417     /**
21418      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21419      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21420      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21421      */
21422     regex : null,
21423     /**
21424      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21425      */
21426     regexText : "",
21427     /**
21428      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21429      */
21430     emptyText : null,
21431     /**
21432      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21433      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21434      */
21435     emptyClass : 'x-form-empty-field',
21436
21437     // private
21438     initEvents : function(){
21439         Roo.form.TextField.superclass.initEvents.call(this);
21440         if(this.validationEvent == 'keyup'){
21441             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21442             this.el.on('keyup', this.filterValidation, this);
21443         }
21444         else if(this.validationEvent !== false){
21445             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21446         }
21447         if(this.selectOnFocus || this.emptyText){
21448             this.on("focus", this.preFocus, this);
21449             if(this.emptyText){
21450                 this.on('blur', this.postBlur, this);
21451                 this.applyEmptyText();
21452             }
21453         }
21454         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21455             this.el.on("keypress", this.filterKeys, this);
21456         }
21457         if(this.grow){
21458             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21459             this.el.on("click", this.autoSize,  this);
21460         }
21461     },
21462
21463     processValue : function(value){
21464         if(this.stripCharsRe){
21465             var newValue = value.replace(this.stripCharsRe, '');
21466             if(newValue !== value){
21467                 this.setRawValue(newValue);
21468                 return newValue;
21469             }
21470         }
21471         return value;
21472     },
21473
21474     filterValidation : function(e){
21475         if(!e.isNavKeyPress()){
21476             this.validationTask.delay(this.validationDelay);
21477         }
21478     },
21479
21480     // private
21481     onKeyUp : function(e){
21482         if(!e.isNavKeyPress()){
21483             this.autoSize();
21484         }
21485     },
21486
21487     /**
21488      * Resets the current field value to the originally-loaded value and clears any validation messages.
21489      * Also adds emptyText and emptyClass if the original value was blank.
21490      */
21491     reset : function(){
21492         Roo.form.TextField.superclass.reset.call(this);
21493         this.applyEmptyText();
21494     },
21495
21496     applyEmptyText : function(){
21497         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21498             this.setRawValue(this.emptyText);
21499             this.el.addClass(this.emptyClass);
21500         }
21501     },
21502
21503     // private
21504     preFocus : function(){
21505         if(this.emptyText){
21506             if(this.el.dom.value == this.emptyText){
21507                 this.setRawValue('');
21508             }
21509             this.el.removeClass(this.emptyClass);
21510         }
21511         if(this.selectOnFocus){
21512             this.el.dom.select();
21513         }
21514     },
21515
21516     // private
21517     postBlur : function(){
21518         this.applyEmptyText();
21519     },
21520
21521     // private
21522     filterKeys : function(e){
21523         var k = e.getKey();
21524         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21525             return;
21526         }
21527         var c = e.getCharCode(), cc = String.fromCharCode(c);
21528         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21529             return;
21530         }
21531         if(!this.maskRe.test(cc)){
21532             e.stopEvent();
21533         }
21534     },
21535
21536     setValue : function(v){
21537         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21538             this.el.removeClass(this.emptyClass);
21539         }
21540         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21541         this.applyEmptyText();
21542         this.autoSize();
21543     },
21544
21545     /**
21546      * Validates a value according to the field's validation rules and marks the field as invalid
21547      * if the validation fails
21548      * @param {Mixed} value The value to validate
21549      * @return {Boolean} True if the value is valid, else false
21550      */
21551     validateValue : function(value){
21552         if(value.length < 1 || value === this.emptyText){ // if it's blank
21553              if(this.allowBlank){
21554                 this.clearInvalid();
21555                 return true;
21556              }else{
21557                 this.markInvalid(this.blankText);
21558                 return false;
21559              }
21560         }
21561         if(value.length < this.minLength){
21562             this.markInvalid(String.format(this.minLengthText, this.minLength));
21563             return false;
21564         }
21565         if(value.length > this.maxLength){
21566             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21567             return false;
21568         }
21569         if(this.vtype){
21570             var vt = Roo.form.VTypes;
21571             if(!vt[this.vtype](value, this)){
21572                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21573                 return false;
21574             }
21575         }
21576         if(typeof this.validator == "function"){
21577             var msg = this.validator(value);
21578             if(msg !== true){
21579                 this.markInvalid(msg);
21580                 return false;
21581             }
21582         }
21583         if(this.regex && !this.regex.test(value)){
21584             this.markInvalid(this.regexText);
21585             return false;
21586         }
21587         return true;
21588     },
21589
21590     /**
21591      * Selects text in this field
21592      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21593      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21594      */
21595     selectText : function(start, end){
21596         var v = this.getRawValue();
21597         if(v.length > 0){
21598             start = start === undefined ? 0 : start;
21599             end = end === undefined ? v.length : end;
21600             var d = this.el.dom;
21601             if(d.setSelectionRange){
21602                 d.setSelectionRange(start, end);
21603             }else if(d.createTextRange){
21604                 var range = d.createTextRange();
21605                 range.moveStart("character", start);
21606                 range.moveEnd("character", v.length-end);
21607                 range.select();
21608             }
21609         }
21610     },
21611
21612     /**
21613      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21614      * This only takes effect if grow = true, and fires the autosize event.
21615      */
21616     autoSize : function(){
21617         if(!this.grow || !this.rendered){
21618             return;
21619         }
21620         if(!this.metrics){
21621             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21622         }
21623         var el = this.el;
21624         var v = el.dom.value;
21625         var d = document.createElement('div');
21626         d.appendChild(document.createTextNode(v));
21627         v = d.innerHTML;
21628         d = null;
21629         v += "&#160;";
21630         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21631         this.el.setWidth(w);
21632         this.fireEvent("autosize", this, w);
21633     }
21634 });/*
21635  * Based on:
21636  * Ext JS Library 1.1.1
21637  * Copyright(c) 2006-2007, Ext JS, LLC.
21638  *
21639  * Originally Released Under LGPL - original licence link has changed is not relivant.
21640  *
21641  * Fork - LGPL
21642  * <script type="text/javascript">
21643  */
21644  
21645 /**
21646  * @class Roo.form.Hidden
21647  * @extends Roo.form.TextField
21648  * Simple Hidden element used on forms 
21649  * 
21650  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21651  * 
21652  * @constructor
21653  * Creates a new Hidden form element.
21654  * @param {Object} config Configuration options
21655  */
21656
21657
21658
21659 // easy hidden field...
21660 Roo.form.Hidden = function(config){
21661     Roo.form.Hidden.superclass.constructor.call(this, config);
21662 };
21663   
21664 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21665     fieldLabel:      '',
21666     inputType:      'hidden',
21667     width:          50,
21668     allowBlank:     true,
21669     labelSeparator: '',
21670     hidden:         true,
21671     itemCls :       'x-form-item-display-none'
21672
21673
21674 });
21675
21676
21677 /*
21678  * Based on:
21679  * Ext JS Library 1.1.1
21680  * Copyright(c) 2006-2007, Ext JS, LLC.
21681  *
21682  * Originally Released Under LGPL - original licence link has changed is not relivant.
21683  *
21684  * Fork - LGPL
21685  * <script type="text/javascript">
21686  */
21687  
21688 /**
21689  * @class Roo.form.TriggerField
21690  * @extends Roo.form.TextField
21691  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21692  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21693  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21694  * for which you can provide a custom implementation.  For example:
21695  * <pre><code>
21696 var trigger = new Roo.form.TriggerField();
21697 trigger.onTriggerClick = myTriggerFn;
21698 trigger.applyTo('my-field');
21699 </code></pre>
21700  *
21701  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21702  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21703  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21704  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21705  * @constructor
21706  * Create a new TriggerField.
21707  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21708  * to the base TextField)
21709  */
21710 Roo.form.TriggerField = function(config){
21711     this.mimicing = false;
21712     Roo.form.TriggerField.superclass.constructor.call(this, config);
21713 };
21714
21715 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21716     /**
21717      * @cfg {String} triggerClass A CSS class to apply to the trigger
21718      */
21719     /**
21720      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21721      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21722      */
21723     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21724     /**
21725      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21726      */
21727     hideTrigger:false,
21728
21729     /** @cfg {Boolean} grow @hide */
21730     /** @cfg {Number} growMin @hide */
21731     /** @cfg {Number} growMax @hide */
21732
21733     /**
21734      * @hide 
21735      * @method
21736      */
21737     autoSize: Roo.emptyFn,
21738     // private
21739     monitorTab : true,
21740     // private
21741     deferHeight : true,
21742
21743     
21744     actionMode : 'wrap',
21745     // private
21746     onResize : function(w, h){
21747         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21748         if(typeof w == 'number'){
21749             var x = w - this.trigger.getWidth();
21750             this.el.setWidth(this.adjustWidth('input', x));
21751             this.trigger.setStyle('left', x+'px');
21752         }
21753     },
21754
21755     // private
21756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21757
21758     // private
21759     getResizeEl : function(){
21760         return this.wrap;
21761     },
21762
21763     // private
21764     getPositionEl : function(){
21765         return this.wrap;
21766     },
21767
21768     // private
21769     alignErrorIcon : function(){
21770         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21771     },
21772
21773     // private
21774     onRender : function(ct, position){
21775         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21776         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21777         this.trigger = this.wrap.createChild(this.triggerConfig ||
21778                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21779         if(this.hideTrigger){
21780             this.trigger.setDisplayed(false);
21781         }
21782         this.initTrigger();
21783         if(!this.width){
21784             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21785         }
21786     },
21787
21788     // private
21789     initTrigger : function(){
21790         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21791         this.trigger.addClassOnOver('x-form-trigger-over');
21792         this.trigger.addClassOnClick('x-form-trigger-click');
21793     },
21794
21795     // private
21796     onDestroy : function(){
21797         if(this.trigger){
21798             this.trigger.removeAllListeners();
21799             this.trigger.remove();
21800         }
21801         if(this.wrap){
21802             this.wrap.remove();
21803         }
21804         Roo.form.TriggerField.superclass.onDestroy.call(this);
21805     },
21806
21807     // private
21808     onFocus : function(){
21809         Roo.form.TriggerField.superclass.onFocus.call(this);
21810         if(!this.mimicing){
21811             this.wrap.addClass('x-trigger-wrap-focus');
21812             this.mimicing = true;
21813             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21814             if(this.monitorTab){
21815                 this.el.on("keydown", this.checkTab, this);
21816             }
21817         }
21818     },
21819
21820     // private
21821     checkTab : function(e){
21822         if(e.getKey() == e.TAB){
21823             this.triggerBlur();
21824         }
21825     },
21826
21827     // private
21828     onBlur : function(){
21829         // do nothing
21830     },
21831
21832     // private
21833     mimicBlur : function(e, t){
21834         if(!this.wrap.contains(t) && this.validateBlur()){
21835             this.triggerBlur();
21836         }
21837     },
21838
21839     // private
21840     triggerBlur : function(){
21841         this.mimicing = false;
21842         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21843         if(this.monitorTab){
21844             this.el.un("keydown", this.checkTab, this);
21845         }
21846         this.wrap.removeClass('x-trigger-wrap-focus');
21847         Roo.form.TriggerField.superclass.onBlur.call(this);
21848     },
21849
21850     // private
21851     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21852     validateBlur : function(e, t){
21853         return true;
21854     },
21855
21856     // private
21857     onDisable : function(){
21858         Roo.form.TriggerField.superclass.onDisable.call(this);
21859         if(this.wrap){
21860             this.wrap.addClass('x-item-disabled');
21861         }
21862     },
21863
21864     // private
21865     onEnable : function(){
21866         Roo.form.TriggerField.superclass.onEnable.call(this);
21867         if(this.wrap){
21868             this.wrap.removeClass('x-item-disabled');
21869         }
21870     },
21871
21872     // private
21873     onShow : function(){
21874         var ae = this.getActionEl();
21875         
21876         if(ae){
21877             ae.dom.style.display = '';
21878             ae.dom.style.visibility = 'visible';
21879         }
21880     },
21881
21882     // private
21883     
21884     onHide : function(){
21885         var ae = this.getActionEl();
21886         ae.dom.style.display = 'none';
21887     },
21888
21889     /**
21890      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21891      * by an implementing function.
21892      * @method
21893      * @param {EventObject} e
21894      */
21895     onTriggerClick : Roo.emptyFn
21896 });
21897
21898 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21899 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21900 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21901 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21902     initComponent : function(){
21903         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21904
21905         this.triggerConfig = {
21906             tag:'span', cls:'x-form-twin-triggers', cn:[
21907             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21908             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21909         ]};
21910     },
21911
21912     getTrigger : function(index){
21913         return this.triggers[index];
21914     },
21915
21916     initTrigger : function(){
21917         var ts = this.trigger.select('.x-form-trigger', true);
21918         this.wrap.setStyle('overflow', 'hidden');
21919         var triggerField = this;
21920         ts.each(function(t, all, index){
21921             t.hide = function(){
21922                 var w = triggerField.wrap.getWidth();
21923                 this.dom.style.display = 'none';
21924                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21925             };
21926             t.show = function(){
21927                 var w = triggerField.wrap.getWidth();
21928                 this.dom.style.display = '';
21929                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21930             };
21931             var triggerIndex = 'Trigger'+(index+1);
21932
21933             if(this['hide'+triggerIndex]){
21934                 t.dom.style.display = 'none';
21935             }
21936             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21937             t.addClassOnOver('x-form-trigger-over');
21938             t.addClassOnClick('x-form-trigger-click');
21939         }, this);
21940         this.triggers = ts.elements;
21941     },
21942
21943     onTrigger1Click : Roo.emptyFn,
21944     onTrigger2Click : Roo.emptyFn
21945 });/*
21946  * Based on:
21947  * Ext JS Library 1.1.1
21948  * Copyright(c) 2006-2007, Ext JS, LLC.
21949  *
21950  * Originally Released Under LGPL - original licence link has changed is not relivant.
21951  *
21952  * Fork - LGPL
21953  * <script type="text/javascript">
21954  */
21955  
21956 /**
21957  * @class Roo.form.TextArea
21958  * @extends Roo.form.TextField
21959  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21960  * support for auto-sizing.
21961  * @constructor
21962  * Creates a new TextArea
21963  * @param {Object} config Configuration options
21964  */
21965 Roo.form.TextArea = function(config){
21966     Roo.form.TextArea.superclass.constructor.call(this, config);
21967     // these are provided exchanges for backwards compat
21968     // minHeight/maxHeight were replaced by growMin/growMax to be
21969     // compatible with TextField growing config values
21970     if(this.minHeight !== undefined){
21971         this.growMin = this.minHeight;
21972     }
21973     if(this.maxHeight !== undefined){
21974         this.growMax = this.maxHeight;
21975     }
21976 };
21977
21978 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21979     /**
21980      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21981      */
21982     growMin : 60,
21983     /**
21984      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21985      */
21986     growMax: 1000,
21987     /**
21988      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21989      * in the field (equivalent to setting overflow: hidden, defaults to false)
21990      */
21991     preventScrollbars: false,
21992     /**
21993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21994      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21995      */
21996
21997     // private
21998     onRender : function(ct, position){
21999         if(!this.el){
22000             this.defaultAutoCreate = {
22001                 tag: "textarea",
22002                 style:"width:300px;height:60px;",
22003                 autocomplete: "off"
22004             };
22005         }
22006         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22007         if(this.grow){
22008             this.textSizeEl = Roo.DomHelper.append(document.body, {
22009                 tag: "pre", cls: "x-form-grow-sizer"
22010             });
22011             if(this.preventScrollbars){
22012                 this.el.setStyle("overflow", "hidden");
22013             }
22014             this.el.setHeight(this.growMin);
22015         }
22016     },
22017
22018     onDestroy : function(){
22019         if(this.textSizeEl){
22020             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22021         }
22022         Roo.form.TextArea.superclass.onDestroy.call(this);
22023     },
22024
22025     // private
22026     onKeyUp : function(e){
22027         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22028             this.autoSize();
22029         }
22030     },
22031
22032     /**
22033      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22034      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22035      */
22036     autoSize : function(){
22037         if(!this.grow || !this.textSizeEl){
22038             return;
22039         }
22040         var el = this.el;
22041         var v = el.dom.value;
22042         var ts = this.textSizeEl;
22043
22044         ts.innerHTML = '';
22045         ts.appendChild(document.createTextNode(v));
22046         v = ts.innerHTML;
22047
22048         Roo.fly(ts).setWidth(this.el.getWidth());
22049         if(v.length < 1){
22050             v = "&#160;&#160;";
22051         }else{
22052             if(Roo.isIE){
22053                 v = v.replace(/\n/g, '<p>&#160;</p>');
22054             }
22055             v += "&#160;\n&#160;";
22056         }
22057         ts.innerHTML = v;
22058         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22059         if(h != this.lastHeight){
22060             this.lastHeight = h;
22061             this.el.setHeight(h);
22062             this.fireEvent("autosize", this, h);
22063         }
22064     }
22065 });/*
22066  * Based on:
22067  * Ext JS Library 1.1.1
22068  * Copyright(c) 2006-2007, Ext JS, LLC.
22069  *
22070  * Originally Released Under LGPL - original licence link has changed is not relivant.
22071  *
22072  * Fork - LGPL
22073  * <script type="text/javascript">
22074  */
22075  
22076
22077 /**
22078  * @class Roo.form.NumberField
22079  * @extends Roo.form.TextField
22080  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22081  * @constructor
22082  * Creates a new NumberField
22083  * @param {Object} config Configuration options
22084  */
22085 Roo.form.NumberField = function(config){
22086     Roo.form.NumberField.superclass.constructor.call(this, config);
22087 };
22088
22089 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22090     /**
22091      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22092      */
22093     fieldClass: "x-form-field x-form-num-field",
22094     /**
22095      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22096      */
22097     allowDecimals : true,
22098     /**
22099      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22100      */
22101     decimalSeparator : ".",
22102     /**
22103      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22104      */
22105     decimalPrecision : 2,
22106     /**
22107      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22108      */
22109     allowNegative : true,
22110     /**
22111      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22112      */
22113     minValue : Number.NEGATIVE_INFINITY,
22114     /**
22115      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22116      */
22117     maxValue : Number.MAX_VALUE,
22118     /**
22119      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22120      */
22121     minText : "The minimum value for this field is {0}",
22122     /**
22123      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22124      */
22125     maxText : "The maximum value for this field is {0}",
22126     /**
22127      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22128      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22129      */
22130     nanText : "{0} is not a valid number",
22131
22132     // private
22133     initEvents : function(){
22134         Roo.form.NumberField.superclass.initEvents.call(this);
22135         var allowed = "0123456789";
22136         if(this.allowDecimals){
22137             allowed += this.decimalSeparator;
22138         }
22139         if(this.allowNegative){
22140             allowed += "-";
22141         }
22142         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22143         var keyPress = function(e){
22144             var k = e.getKey();
22145             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22146                 return;
22147             }
22148             var c = e.getCharCode();
22149             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22150                 e.stopEvent();
22151             }
22152         };
22153         this.el.on("keypress", keyPress, this);
22154     },
22155
22156     // private
22157     validateValue : function(value){
22158         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22159             return false;
22160         }
22161         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22162              return true;
22163         }
22164         var num = this.parseValue(value);
22165         if(isNaN(num)){
22166             this.markInvalid(String.format(this.nanText, value));
22167             return false;
22168         }
22169         if(num < this.minValue){
22170             this.markInvalid(String.format(this.minText, this.minValue));
22171             return false;
22172         }
22173         if(num > this.maxValue){
22174             this.markInvalid(String.format(this.maxText, this.maxValue));
22175             return false;
22176         }
22177         return true;
22178     },
22179
22180     getValue : function(){
22181         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22182     },
22183
22184     // private
22185     parseValue : function(value){
22186         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22187         return isNaN(value) ? '' : value;
22188     },
22189
22190     // private
22191     fixPrecision : function(value){
22192         var nan = isNaN(value);
22193         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22194             return nan ? '' : value;
22195         }
22196         return parseFloat(value).toFixed(this.decimalPrecision);
22197     },
22198
22199     setValue : function(v){
22200         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22201     },
22202
22203     // private
22204     decimalPrecisionFcn : function(v){
22205         return Math.floor(v);
22206     },
22207
22208     beforeBlur : function(){
22209         var v = this.parseValue(this.getRawValue());
22210         if(v){
22211             this.setValue(this.fixPrecision(v));
22212         }
22213     }
22214 });/*
22215  * Based on:
22216  * Ext JS Library 1.1.1
22217  * Copyright(c) 2006-2007, Ext JS, LLC.
22218  *
22219  * Originally Released Under LGPL - original licence link has changed is not relivant.
22220  *
22221  * Fork - LGPL
22222  * <script type="text/javascript">
22223  */
22224  
22225 /**
22226  * @class Roo.form.DateField
22227  * @extends Roo.form.TriggerField
22228  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22229 * @constructor
22230 * Create a new DateField
22231 * @param {Object} config
22232  */
22233 Roo.form.DateField = function(config){
22234     Roo.form.DateField.superclass.constructor.call(this, config);
22235     
22236       this.addEvents({
22237          
22238         /**
22239          * @event select
22240          * Fires when a date is selected
22241              * @param {Roo.form.DateField} combo This combo box
22242              * @param {Date} date The date selected
22243              */
22244         'select' : true
22245          
22246     });
22247     
22248     
22249     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22250     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22251     this.ddMatch = null;
22252     if(this.disabledDates){
22253         var dd = this.disabledDates;
22254         var re = "(?:";
22255         for(var i = 0; i < dd.length; i++){
22256             re += dd[i];
22257             if(i != dd.length-1) re += "|";
22258         }
22259         this.ddMatch = new RegExp(re + ")");
22260     }
22261 };
22262
22263 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22264     /**
22265      * @cfg {String} format
22266      * The default date format string which can be overriden for localization support.  The format must be
22267      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22268      */
22269     format : "m/d/y",
22270     /**
22271      * @cfg {String} altFormats
22272      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22273      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22274      */
22275     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22276     /**
22277      * @cfg {Array} disabledDays
22278      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22279      */
22280     disabledDays : null,
22281     /**
22282      * @cfg {String} disabledDaysText
22283      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22284      */
22285     disabledDaysText : "Disabled",
22286     /**
22287      * @cfg {Array} disabledDates
22288      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22289      * expression so they are very powerful. Some examples:
22290      * <ul>
22291      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22292      * <li>["03/08", "09/16"] would disable those days for every year</li>
22293      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22294      * <li>["03/../2006"] would disable every day in March 2006</li>
22295      * <li>["^03"] would disable every day in every March</li>
22296      * </ul>
22297      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22298      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22299      */
22300     disabledDates : null,
22301     /**
22302      * @cfg {String} disabledDatesText
22303      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22304      */
22305     disabledDatesText : "Disabled",
22306     /**
22307      * @cfg {Date/String} minValue
22308      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22309      * valid format (defaults to null).
22310      */
22311     minValue : null,
22312     /**
22313      * @cfg {Date/String} maxValue
22314      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22315      * valid format (defaults to null).
22316      */
22317     maxValue : null,
22318     /**
22319      * @cfg {String} minText
22320      * The error text to display when the date in the cell is before minValue (defaults to
22321      * 'The date in this field must be after {minValue}').
22322      */
22323     minText : "The date in this field must be equal to or after {0}",
22324     /**
22325      * @cfg {String} maxText
22326      * The error text to display when the date in the cell is after maxValue (defaults to
22327      * 'The date in this field must be before {maxValue}').
22328      */
22329     maxText : "The date in this field must be equal to or before {0}",
22330     /**
22331      * @cfg {String} invalidText
22332      * The error text to display when the date in the field is invalid (defaults to
22333      * '{value} is not a valid date - it must be in the format {format}').
22334      */
22335     invalidText : "{0} is not a valid date - it must be in the format {1}",
22336     /**
22337      * @cfg {String} triggerClass
22338      * An additional CSS class used to style the trigger button.  The trigger will always get the
22339      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22340      * which displays a calendar icon).
22341      */
22342     triggerClass : 'x-form-date-trigger',
22343     
22344
22345     /**
22346      * @cfg {bool} useIso
22347      * if enabled, then the date field will use a hidden field to store the 
22348      * real value as iso formated date. default (false)
22349      */ 
22350     useIso : false,
22351     /**
22352      * @cfg {String/Object} autoCreate
22353      * A DomHelper element spec, or true for a default element spec (defaults to
22354      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22355      */ 
22356     // private
22357     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22358     
22359     // private
22360     hiddenField: false,
22361     
22362     onRender : function(ct, position)
22363     {
22364         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22365         if (this.useIso) {
22366             this.el.dom.removeAttribute('name'); 
22367             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22368                     'before', true);
22369             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22370             // prevent input submission
22371             this.hiddenName = this.name;
22372         }
22373             
22374             
22375     },
22376     
22377     // private
22378     validateValue : function(value)
22379     {
22380         value = this.formatDate(value);
22381         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22382             return false;
22383         }
22384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22385              return true;
22386         }
22387         var svalue = value;
22388         value = this.parseDate(value);
22389         if(!value){
22390             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22391             return false;
22392         }
22393         var time = value.getTime();
22394         if(this.minValue && time < this.minValue.getTime()){
22395             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22396             return false;
22397         }
22398         if(this.maxValue && time > this.maxValue.getTime()){
22399             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22400             return false;
22401         }
22402         if(this.disabledDays){
22403             var day = value.getDay();
22404             for(var i = 0; i < this.disabledDays.length; i++) {
22405                 if(day === this.disabledDays[i]){
22406                     this.markInvalid(this.disabledDaysText);
22407                     return false;
22408                 }
22409             }
22410         }
22411         var fvalue = this.formatDate(value);
22412         if(this.ddMatch && this.ddMatch.test(fvalue)){
22413             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22414             return false;
22415         }
22416         return true;
22417     },
22418
22419     // private
22420     // Provides logic to override the default TriggerField.validateBlur which just returns true
22421     validateBlur : function(){
22422         return !this.menu || !this.menu.isVisible();
22423     },
22424
22425     /**
22426      * Returns the current date value of the date field.
22427      * @return {Date} The date value
22428      */
22429     getValue : function(){
22430         
22431         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22432     },
22433
22434     /**
22435      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22436      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22437      * (the default format used is "m/d/y").
22438      * <br />Usage:
22439      * <pre><code>
22440 //All of these calls set the same date value (May 4, 2006)
22441
22442 //Pass a date object:
22443 var dt = new Date('5/4/06');
22444 dateField.setValue(dt);
22445
22446 //Pass a date string (default format):
22447 dateField.setValue('5/4/06');
22448
22449 //Pass a date string (custom format):
22450 dateField.format = 'Y-m-d';
22451 dateField.setValue('2006-5-4');
22452 </code></pre>
22453      * @param {String/Date} date The date or valid date string
22454      */
22455     setValue : function(date){
22456         if (this.hiddenField) {
22457             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22458         }
22459         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22460     },
22461
22462     // private
22463     parseDate : function(value){
22464         if(!value || value instanceof Date){
22465             return value;
22466         }
22467         var v = Date.parseDate(value, this.format);
22468         if(!v && this.altFormats){
22469             if(!this.altFormatsArray){
22470                 this.altFormatsArray = this.altFormats.split("|");
22471             }
22472             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22473                 v = Date.parseDate(value, this.altFormatsArray[i]);
22474             }
22475         }
22476         return v;
22477     },
22478
22479     // private
22480     formatDate : function(date, fmt){
22481         return (!date || !(date instanceof Date)) ?
22482                date : date.dateFormat(fmt || this.format);
22483     },
22484
22485     // private
22486     menuListeners : {
22487         select: function(m, d){
22488             this.setValue(d);
22489             this.fireEvent('select', this, d);
22490         },
22491         show : function(){ // retain focus styling
22492             this.onFocus();
22493         },
22494         hide : function(){
22495             this.focus.defer(10, this);
22496             var ml = this.menuListeners;
22497             this.menu.un("select", ml.select,  this);
22498             this.menu.un("show", ml.show,  this);
22499             this.menu.un("hide", ml.hide,  this);
22500         }
22501     },
22502
22503     // private
22504     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22505     onTriggerClick : function(){
22506         if(this.disabled){
22507             return;
22508         }
22509         if(this.menu == null){
22510             this.menu = new Roo.menu.DateMenu();
22511         }
22512         Roo.apply(this.menu.picker,  {
22513             showClear: this.allowBlank,
22514             minDate : this.minValue,
22515             maxDate : this.maxValue,
22516             disabledDatesRE : this.ddMatch,
22517             disabledDatesText : this.disabledDatesText,
22518             disabledDays : this.disabledDays,
22519             disabledDaysText : this.disabledDaysText,
22520             format : this.format,
22521             minText : String.format(this.minText, this.formatDate(this.minValue)),
22522             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22523         });
22524         this.menu.on(Roo.apply({}, this.menuListeners, {
22525             scope:this
22526         }));
22527         this.menu.picker.setValue(this.getValue() || new Date());
22528         this.menu.show(this.el, "tl-bl?");
22529     },
22530
22531     beforeBlur : function(){
22532         var v = this.parseDate(this.getRawValue());
22533         if(v){
22534             this.setValue(v);
22535         }
22536     }
22537
22538     /** @cfg {Boolean} grow @hide */
22539     /** @cfg {Number} growMin @hide */
22540     /** @cfg {Number} growMax @hide */
22541     /**
22542      * @hide
22543      * @method autoSize
22544      */
22545 });/*
22546  * Based on:
22547  * Ext JS Library 1.1.1
22548  * Copyright(c) 2006-2007, Ext JS, LLC.
22549  *
22550  * Originally Released Under LGPL - original licence link has changed is not relivant.
22551  *
22552  * Fork - LGPL
22553  * <script type="text/javascript">
22554  */
22555  
22556
22557 /**
22558  * @class Roo.form.ComboBox
22559  * @extends Roo.form.TriggerField
22560  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22561  * @constructor
22562  * Create a new ComboBox.
22563  * @param {Object} config Configuration options
22564  */
22565 Roo.form.ComboBox = function(config){
22566     Roo.form.ComboBox.superclass.constructor.call(this, config);
22567     this.addEvents({
22568         /**
22569          * @event expand
22570          * Fires when the dropdown list is expanded
22571              * @param {Roo.form.ComboBox} combo This combo box
22572              */
22573         'expand' : true,
22574         /**
22575          * @event collapse
22576          * Fires when the dropdown list is collapsed
22577              * @param {Roo.form.ComboBox} combo This combo box
22578              */
22579         'collapse' : true,
22580         /**
22581          * @event beforeselect
22582          * Fires before a list item is selected. Return false to cancel the selection.
22583              * @param {Roo.form.ComboBox} combo This combo box
22584              * @param {Roo.data.Record} record The data record returned from the underlying store
22585              * @param {Number} index The index of the selected item in the dropdown list
22586              */
22587         'beforeselect' : true,
22588         /**
22589          * @event select
22590          * Fires when a list item is selected
22591              * @param {Roo.form.ComboBox} combo This combo box
22592              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22593              * @param {Number} index The index of the selected item in the dropdown list
22594              */
22595         'select' : true,
22596         /**
22597          * @event beforequery
22598          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22599          * The event object passed has these properties:
22600              * @param {Roo.form.ComboBox} combo This combo box
22601              * @param {String} query The query
22602              * @param {Boolean} forceAll true to force "all" query
22603              * @param {Boolean} cancel true to cancel the query
22604              * @param {Object} e The query event object
22605              */
22606         'beforequery': true,
22607          /**
22608          * @event add
22609          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22610              * @param {Roo.form.ComboBox} combo This combo box
22611              */
22612         'add' : true,
22613         /**
22614          * @event edit
22615          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22616              * @param {Roo.form.ComboBox} combo This combo box
22617              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22618              */
22619         'edit' : true
22620         
22621         
22622     });
22623     if(this.transform){
22624         this.allowDomMove = false;
22625         var s = Roo.getDom(this.transform);
22626         if(!this.hiddenName){
22627             this.hiddenName = s.name;
22628         }
22629         if(!this.store){
22630             this.mode = 'local';
22631             var d = [], opts = s.options;
22632             for(var i = 0, len = opts.length;i < len; i++){
22633                 var o = opts[i];
22634                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22635                 if(o.selected) {
22636                     this.value = value;
22637                 }
22638                 d.push([value, o.text]);
22639             }
22640             this.store = new Roo.data.SimpleStore({
22641                 'id': 0,
22642                 fields: ['value', 'text'],
22643                 data : d
22644             });
22645             this.valueField = 'value';
22646             this.displayField = 'text';
22647         }
22648         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22649         if(!this.lazyRender){
22650             this.target = true;
22651             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22652             s.parentNode.removeChild(s); // remove it
22653             this.render(this.el.parentNode);
22654         }else{
22655             s.parentNode.removeChild(s); // remove it
22656         }
22657
22658     }
22659     if (this.store) {
22660         this.store = Roo.factory(this.store, Roo.data);
22661     }
22662     
22663     this.selectedIndex = -1;
22664     if(this.mode == 'local'){
22665         if(config.queryDelay === undefined){
22666             this.queryDelay = 10;
22667         }
22668         if(config.minChars === undefined){
22669             this.minChars = 0;
22670         }
22671     }
22672 };
22673
22674 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22675     /**
22676      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22677      */
22678     /**
22679      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22680      * rendering into an Roo.Editor, defaults to false)
22681      */
22682     /**
22683      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22684      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22685      */
22686     /**
22687      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22688      */
22689     /**
22690      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22691      * the dropdown list (defaults to undefined, with no header element)
22692      */
22693
22694      /**
22695      * @cfg {String/Roo.Template} tpl The template to use to render the output
22696      */
22697      
22698     // private
22699     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22700     /**
22701      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22702      */
22703     listWidth: undefined,
22704     /**
22705      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22706      * mode = 'remote' or 'text' if mode = 'local')
22707      */
22708     displayField: undefined,
22709     /**
22710      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22711      * mode = 'remote' or 'value' if mode = 'local'). 
22712      * Note: use of a valueField requires the user make a selection
22713      * in order for a value to be mapped.
22714      */
22715     valueField: undefined,
22716     /**
22717      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22718      * field's data value (defaults to the underlying DOM element's name)
22719      */
22720     hiddenName: undefined,
22721     /**
22722      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22723      */
22724     listClass: '',
22725     /**
22726      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22727      */
22728     selectedClass: 'x-combo-selected',
22729     /**
22730      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22731      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22732      * which displays a downward arrow icon).
22733      */
22734     triggerClass : 'x-form-arrow-trigger',
22735     /**
22736      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22737      */
22738     shadow:'sides',
22739     /**
22740      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22741      * anchor positions (defaults to 'tl-bl')
22742      */
22743     listAlign: 'tl-bl?',
22744     /**
22745      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22746      */
22747     maxHeight: 300,
22748     /**
22749      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22750      * query specified by the allQuery config option (defaults to 'query')
22751      */
22752     triggerAction: 'query',
22753     /**
22754      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22755      * (defaults to 4, does not apply if editable = false)
22756      */
22757     minChars : 4,
22758     /**
22759      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22760      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22761      */
22762     typeAhead: false,
22763     /**
22764      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22765      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22766      */
22767     queryDelay: 500,
22768     /**
22769      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22770      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22771      */
22772     pageSize: 0,
22773     /**
22774      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22775      * when editable = true (defaults to false)
22776      */
22777     selectOnFocus:false,
22778     /**
22779      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22780      */
22781     queryParam: 'query',
22782     /**
22783      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22784      * when mode = 'remote' (defaults to 'Loading...')
22785      */
22786     loadingText: 'Loading...',
22787     /**
22788      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22789      */
22790     resizable: false,
22791     /**
22792      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22793      */
22794     handleHeight : 8,
22795     /**
22796      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22797      * traditional select (defaults to true)
22798      */
22799     editable: true,
22800     /**
22801      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22802      */
22803     allQuery: '',
22804     /**
22805      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22806      */
22807     mode: 'remote',
22808     /**
22809      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22810      * listWidth has a higher value)
22811      */
22812     minListWidth : 70,
22813     /**
22814      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22815      * allow the user to set arbitrary text into the field (defaults to false)
22816      */
22817     forceSelection:false,
22818     /**
22819      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22820      * if typeAhead = true (defaults to 250)
22821      */
22822     typeAheadDelay : 250,
22823     /**
22824      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22825      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22826      */
22827     valueNotFoundText : undefined,
22828     /**
22829      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22830      */
22831     blockFocus : false,
22832     
22833     /**
22834      * @cfg {Boolean} disableClear Disable showing of clear button.
22835      */
22836     disableClear : false,
22837     /**
22838      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22839      */
22840     alwaysQuery : false,
22841     
22842     //private
22843     addicon : false,
22844     editicon: false,
22845     
22846     
22847     // private
22848     onRender : function(ct, position){
22849         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22850         if(this.hiddenName){
22851             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22852                     'before', true);
22853             this.hiddenField.value =
22854                 this.hiddenValue !== undefined ? this.hiddenValue :
22855                 this.value !== undefined ? this.value : '';
22856
22857             // prevent input submission
22858             this.el.dom.removeAttribute('name');
22859         }
22860         if(Roo.isGecko){
22861             this.el.dom.setAttribute('autocomplete', 'off');
22862         }
22863
22864         var cls = 'x-combo-list';
22865
22866         this.list = new Roo.Layer({
22867             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22868         });
22869
22870         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22871         this.list.setWidth(lw);
22872         this.list.swallowEvent('mousewheel');
22873         this.assetHeight = 0;
22874
22875         if(this.title){
22876             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22877             this.assetHeight += this.header.getHeight();
22878         }
22879
22880         this.innerList = this.list.createChild({cls:cls+'-inner'});
22881         this.innerList.on('mouseover', this.onViewOver, this);
22882         this.innerList.on('mousemove', this.onViewMove, this);
22883         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22884         
22885         if(this.allowBlank && !this.pageSize && !this.disableClear){
22886             this.footer = this.list.createChild({cls:cls+'-ft'});
22887             this.pageTb = new Roo.Toolbar(this.footer);
22888            
22889         }
22890         if(this.pageSize){
22891             this.footer = this.list.createChild({cls:cls+'-ft'});
22892             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22893                     {pageSize: this.pageSize});
22894             
22895         }
22896         
22897         if (this.pageTb && this.allowBlank && !this.disableClear) {
22898             var _this = this;
22899             this.pageTb.add(new Roo.Toolbar.Fill(), {
22900                 cls: 'x-btn-icon x-btn-clear',
22901                 text: '&#160;',
22902                 handler: function()
22903                 {
22904                     _this.collapse();
22905                     _this.clearValue();
22906                     _this.onSelect(false, -1);
22907                 }
22908             });
22909         }
22910         if (this.footer) {
22911             this.assetHeight += this.footer.getHeight();
22912         }
22913         
22914
22915         if(!this.tpl){
22916             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22917         }
22918
22919         this.view = new Roo.View(this.innerList, this.tpl, {
22920             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22921         });
22922
22923         this.view.on('click', this.onViewClick, this);
22924
22925         this.store.on('beforeload', this.onBeforeLoad, this);
22926         this.store.on('load', this.onLoad, this);
22927         this.store.on('loadexception', this.collapse, this);
22928
22929         if(this.resizable){
22930             this.resizer = new Roo.Resizable(this.list,  {
22931                pinned:true, handles:'se'
22932             });
22933             this.resizer.on('resize', function(r, w, h){
22934                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22935                 this.listWidth = w;
22936                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22937                 this.restrictHeight();
22938             }, this);
22939             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22940         }
22941         if(!this.editable){
22942             this.editable = true;
22943             this.setEditable(false);
22944         }  
22945         
22946         
22947         if (typeof(this.events.add.listeners) != 'undefined') {
22948             
22949             this.addicon = this.wrap.createChild(
22950                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22951        
22952             this.addicon.on('click', function(e) {
22953                 this.fireEvent('add', this);
22954             }, this);
22955         }
22956         if (typeof(this.events.edit.listeners) != 'undefined') {
22957             
22958             this.editicon = this.wrap.createChild(
22959                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22960             if (this.addicon) {
22961                 this.editicon.setStyle('margin-left', '40px');
22962             }
22963             this.editicon.on('click', function(e) {
22964                 
22965                 // we fire even  if inothing is selected..
22966                 this.fireEvent('edit', this, this.lastData );
22967                 
22968             }, this);
22969         }
22970         
22971         
22972         
22973     },
22974
22975     // private
22976     initEvents : function(){
22977         Roo.form.ComboBox.superclass.initEvents.call(this);
22978
22979         this.keyNav = new Roo.KeyNav(this.el, {
22980             "up" : function(e){
22981                 this.inKeyMode = true;
22982                 this.selectPrev();
22983             },
22984
22985             "down" : function(e){
22986                 if(!this.isExpanded()){
22987                     this.onTriggerClick();
22988                 }else{
22989                     this.inKeyMode = true;
22990                     this.selectNext();
22991                 }
22992             },
22993
22994             "enter" : function(e){
22995                 this.onViewClick();
22996                 //return true;
22997             },
22998
22999             "esc" : function(e){
23000                 this.collapse();
23001             },
23002
23003             "tab" : function(e){
23004                 this.onViewClick(false);
23005                 return true;
23006             },
23007
23008             scope : this,
23009
23010             doRelay : function(foo, bar, hname){
23011                 if(hname == 'down' || this.scope.isExpanded()){
23012                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23013                 }
23014                 return true;
23015             },
23016
23017             forceKeyDown: true
23018         });
23019         this.queryDelay = Math.max(this.queryDelay || 10,
23020                 this.mode == 'local' ? 10 : 250);
23021         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23022         if(this.typeAhead){
23023             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23024         }
23025         if(this.editable !== false){
23026             this.el.on("keyup", this.onKeyUp, this);
23027         }
23028         if(this.forceSelection){
23029             this.on('blur', this.doForce, this);
23030         }
23031     },
23032
23033     onDestroy : function(){
23034         if(this.view){
23035             this.view.setStore(null);
23036             this.view.el.removeAllListeners();
23037             this.view.el.remove();
23038             this.view.purgeListeners();
23039         }
23040         if(this.list){
23041             this.list.destroy();
23042         }
23043         if(this.store){
23044             this.store.un('beforeload', this.onBeforeLoad, this);
23045             this.store.un('load', this.onLoad, this);
23046             this.store.un('loadexception', this.collapse, this);
23047         }
23048         Roo.form.ComboBox.superclass.onDestroy.call(this);
23049     },
23050
23051     // private
23052     fireKey : function(e){
23053         if(e.isNavKeyPress() && !this.list.isVisible()){
23054             this.fireEvent("specialkey", this, e);
23055         }
23056     },
23057
23058     // private
23059     onResize: function(w, h){
23060         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23061         
23062         if(typeof w != 'number'){
23063             // we do not handle it!?!?
23064             return;
23065         }
23066         var tw = this.trigger.getWidth();
23067         tw += this.addicon ? this.addicon.getWidth() : 0;
23068         tw += this.editicon ? this.editicon.getWidth() : 0;
23069         var x = w - tw;
23070         this.el.setWidth( this.adjustWidth('input', x));
23071             
23072         this.trigger.setStyle('left', x+'px');
23073         
23074         if(this.list && this.listWidth === undefined){
23075             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23076             this.list.setWidth(lw);
23077             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23078         }
23079         
23080     
23081         
23082     },
23083
23084     /**
23085      * Allow or prevent the user from directly editing the field text.  If false is passed,
23086      * the user will only be able to select from the items defined in the dropdown list.  This method
23087      * is the runtime equivalent of setting the 'editable' config option at config time.
23088      * @param {Boolean} value True to allow the user to directly edit the field text
23089      */
23090     setEditable : function(value){
23091         if(value == this.editable){
23092             return;
23093         }
23094         this.editable = value;
23095         if(!value){
23096             this.el.dom.setAttribute('readOnly', true);
23097             this.el.on('mousedown', this.onTriggerClick,  this);
23098             this.el.addClass('x-combo-noedit');
23099         }else{
23100             this.el.dom.setAttribute('readOnly', false);
23101             this.el.un('mousedown', this.onTriggerClick,  this);
23102             this.el.removeClass('x-combo-noedit');
23103         }
23104     },
23105
23106     // private
23107     onBeforeLoad : function(){
23108         if(!this.hasFocus){
23109             return;
23110         }
23111         this.innerList.update(this.loadingText ?
23112                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23113         this.restrictHeight();
23114         this.selectedIndex = -1;
23115     },
23116
23117     // private
23118     onLoad : function(){
23119         if(!this.hasFocus){
23120             return;
23121         }
23122         if(this.store.getCount() > 0){
23123             this.expand();
23124             this.restrictHeight();
23125             if(this.lastQuery == this.allQuery){
23126                 if(this.editable){
23127                     this.el.dom.select();
23128                 }
23129                 if(!this.selectByValue(this.value, true)){
23130                     this.select(0, true);
23131                 }
23132             }else{
23133                 this.selectNext();
23134                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23135                     this.taTask.delay(this.typeAheadDelay);
23136                 }
23137             }
23138         }else{
23139             this.onEmptyResults();
23140         }
23141         //this.el.focus();
23142     },
23143
23144     // private
23145     onTypeAhead : function(){
23146         if(this.store.getCount() > 0){
23147             var r = this.store.getAt(0);
23148             var newValue = r.data[this.displayField];
23149             var len = newValue.length;
23150             var selStart = this.getRawValue().length;
23151             if(selStart != len){
23152                 this.setRawValue(newValue);
23153                 this.selectText(selStart, newValue.length);
23154             }
23155         }
23156     },
23157
23158     // private
23159     onSelect : function(record, index){
23160         if(this.fireEvent('beforeselect', this, record, index) !== false){
23161             this.setFromData(index > -1 ? record.data : false);
23162             this.collapse();
23163             this.fireEvent('select', this, record, index);
23164         }
23165     },
23166
23167     /**
23168      * Returns the currently selected field value or empty string if no value is set.
23169      * @return {String} value The selected value
23170      */
23171     getValue : function(){
23172         if(this.valueField){
23173             return typeof this.value != 'undefined' ? this.value : '';
23174         }else{
23175             return Roo.form.ComboBox.superclass.getValue.call(this);
23176         }
23177     },
23178
23179     /**
23180      * Clears any text/value currently set in the field
23181      */
23182     clearValue : function(){
23183         if(this.hiddenField){
23184             this.hiddenField.value = '';
23185         }
23186         this.value = '';
23187         this.setRawValue('');
23188         this.lastSelectionText = '';
23189         this.applyEmptyText();
23190     },
23191
23192     /**
23193      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23194      * will be displayed in the field.  If the value does not match the data value of an existing item,
23195      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23196      * Otherwise the field will be blank (although the value will still be set).
23197      * @param {String} value The value to match
23198      */
23199     setValue : function(v){
23200         var text = v;
23201         if(this.valueField){
23202             var r = this.findRecord(this.valueField, v);
23203             if(r){
23204                 text = r.data[this.displayField];
23205             }else if(this.valueNotFoundText !== undefined){
23206                 text = this.valueNotFoundText;
23207             }
23208         }
23209         this.lastSelectionText = text;
23210         if(this.hiddenField){
23211             this.hiddenField.value = v;
23212         }
23213         Roo.form.ComboBox.superclass.setValue.call(this, text);
23214         this.value = v;
23215     },
23216     /**
23217      * @property {Object} the last set data for the element
23218      */
23219     
23220     lastData : false,
23221     /**
23222      * Sets the value of the field based on a object which is related to the record format for the store.
23223      * @param {Object} value the value to set as. or false on reset?
23224      */
23225     setFromData : function(o){
23226         var dv = ''; // display value
23227         var vv = ''; // value value..
23228         this.lastData = o;
23229         if (this.displayField) {
23230             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23231         } else {
23232             // this is an error condition!!!
23233             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23234         }
23235         
23236         if(this.valueField){
23237             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23238         }
23239         if(this.hiddenField){
23240             this.hiddenField.value = vv;
23241             
23242             this.lastSelectionText = dv;
23243             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23244             this.value = vv;
23245             return;
23246         }
23247         // no hidden field.. - we store the value in 'value', but still display
23248         // display field!!!!
23249         this.lastSelectionText = dv;
23250         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23251         this.value = vv;
23252         
23253         
23254     },
23255     // private
23256     reset : function(){
23257         // overridden so that last data is reset..
23258         this.setValue(this.originalValue);
23259         this.clearInvalid();
23260         this.lastData = false;
23261     },
23262     // private
23263     findRecord : function(prop, value){
23264         var record;
23265         if(this.store.getCount() > 0){
23266             this.store.each(function(r){
23267                 if(r.data[prop] == value){
23268                     record = r;
23269                     return false;
23270                 }
23271             });
23272         }
23273         return record;
23274     },
23275
23276     // private
23277     onViewMove : function(e, t){
23278         this.inKeyMode = false;
23279     },
23280
23281     // private
23282     onViewOver : function(e, t){
23283         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23284             return;
23285         }
23286         var item = this.view.findItemFromChild(t);
23287         if(item){
23288             var index = this.view.indexOf(item);
23289             this.select(index, false);
23290         }
23291     },
23292
23293     // private
23294     onViewClick : function(doFocus){
23295         var index = this.view.getSelectedIndexes()[0];
23296         var r = this.store.getAt(index);
23297         if(r){
23298             this.onSelect(r, index);
23299         }
23300         if(doFocus !== false && !this.blockFocus){
23301             this.el.focus();
23302         }
23303     },
23304
23305     // private
23306     restrictHeight : function(){
23307         this.innerList.dom.style.height = '';
23308         var inner = this.innerList.dom;
23309         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23310         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23311         this.list.beginUpdate();
23312         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23313         this.list.alignTo(this.el, this.listAlign);
23314         this.list.endUpdate();
23315     },
23316
23317     // private
23318     onEmptyResults : function(){
23319         this.collapse();
23320     },
23321
23322     /**
23323      * Returns true if the dropdown list is expanded, else false.
23324      */
23325     isExpanded : function(){
23326         return this.list.isVisible();
23327     },
23328
23329     /**
23330      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23331      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23332      * @param {String} value The data value of the item to select
23333      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23334      * selected item if it is not currently in view (defaults to true)
23335      * @return {Boolean} True if the value matched an item in the list, else false
23336      */
23337     selectByValue : function(v, scrollIntoView){
23338         if(v !== undefined && v !== null){
23339             var r = this.findRecord(this.valueField || this.displayField, v);
23340             if(r){
23341                 this.select(this.store.indexOf(r), scrollIntoView);
23342                 return true;
23343             }
23344         }
23345         return false;
23346     },
23347
23348     /**
23349      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23350      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23351      * @param {Number} index The zero-based index of the list item to select
23352      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23353      * selected item if it is not currently in view (defaults to true)
23354      */
23355     select : function(index, scrollIntoView){
23356         this.selectedIndex = index;
23357         this.view.select(index);
23358         if(scrollIntoView !== false){
23359             var el = this.view.getNode(index);
23360             if(el){
23361                 this.innerList.scrollChildIntoView(el, false);
23362             }
23363         }
23364     },
23365
23366     // private
23367     selectNext : function(){
23368         var ct = this.store.getCount();
23369         if(ct > 0){
23370             if(this.selectedIndex == -1){
23371                 this.select(0);
23372             }else if(this.selectedIndex < ct-1){
23373                 this.select(this.selectedIndex+1);
23374             }
23375         }
23376     },
23377
23378     // private
23379     selectPrev : function(){
23380         var ct = this.store.getCount();
23381         if(ct > 0){
23382             if(this.selectedIndex == -1){
23383                 this.select(0);
23384             }else if(this.selectedIndex != 0){
23385                 this.select(this.selectedIndex-1);
23386             }
23387         }
23388     },
23389
23390     // private
23391     onKeyUp : function(e){
23392         if(this.editable !== false && !e.isSpecialKey()){
23393             this.lastKey = e.getKey();
23394             this.dqTask.delay(this.queryDelay);
23395         }
23396     },
23397
23398     // private
23399     validateBlur : function(){
23400         return !this.list || !this.list.isVisible();   
23401     },
23402
23403     // private
23404     initQuery : function(){
23405         this.doQuery(this.getRawValue());
23406     },
23407
23408     // private
23409     doForce : function(){
23410         if(this.el.dom.value.length > 0){
23411             this.el.dom.value =
23412                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23413             this.applyEmptyText();
23414         }
23415     },
23416
23417     /**
23418      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23419      * query allowing the query action to be canceled if needed.
23420      * @param {String} query The SQL query to execute
23421      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23422      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23423      * saved in the current store (defaults to false)
23424      */
23425     doQuery : function(q, forceAll){
23426         if(q === undefined || q === null){
23427             q = '';
23428         }
23429         var qe = {
23430             query: q,
23431             forceAll: forceAll,
23432             combo: this,
23433             cancel:false
23434         };
23435         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23436             return false;
23437         }
23438         q = qe.query;
23439         forceAll = qe.forceAll;
23440         if(forceAll === true || (q.length >= this.minChars)){
23441             if(this.lastQuery != q || this.alwaysQuery){
23442                 this.lastQuery = q;
23443                 if(this.mode == 'local'){
23444                     this.selectedIndex = -1;
23445                     if(forceAll){
23446                         this.store.clearFilter();
23447                     }else{
23448                         this.store.filter(this.displayField, q);
23449                     }
23450                     this.onLoad();
23451                 }else{
23452                     this.store.baseParams[this.queryParam] = q;
23453                     this.store.load({
23454                         params: this.getParams(q)
23455                     });
23456                     this.expand();
23457                 }
23458             }else{
23459                 this.selectedIndex = -1;
23460                 this.onLoad();   
23461             }
23462         }
23463     },
23464
23465     // private
23466     getParams : function(q){
23467         var p = {};
23468         //p[this.queryParam] = q;
23469         if(this.pageSize){
23470             p.start = 0;
23471             p.limit = this.pageSize;
23472         }
23473         return p;
23474     },
23475
23476     /**
23477      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23478      */
23479     collapse : function(){
23480         if(!this.isExpanded()){
23481             return;
23482         }
23483         this.list.hide();
23484         Roo.get(document).un('mousedown', this.collapseIf, this);
23485         Roo.get(document).un('mousewheel', this.collapseIf, this);
23486         if (!this.editable) {
23487             Roo.get(document).un('keydown', this.listKeyPress, this);
23488         }
23489         this.fireEvent('collapse', this);
23490     },
23491
23492     // private
23493     collapseIf : function(e){
23494         if(!e.within(this.wrap) && !e.within(this.list)){
23495             this.collapse();
23496         }
23497     },
23498
23499     /**
23500      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23501      */
23502     expand : function(){
23503         if(this.isExpanded() || !this.hasFocus){
23504             return;
23505         }
23506         this.list.alignTo(this.el, this.listAlign);
23507         this.list.show();
23508         Roo.get(document).on('mousedown', this.collapseIf, this);
23509         Roo.get(document).on('mousewheel', this.collapseIf, this);
23510         if (!this.editable) {
23511             Roo.get(document).on('keydown', this.listKeyPress, this);
23512         }
23513         
23514         this.fireEvent('expand', this);
23515     },
23516
23517     // private
23518     // Implements the default empty TriggerField.onTriggerClick function
23519     onTriggerClick : function(){
23520         if(this.disabled){
23521             return;
23522         }
23523         if(this.isExpanded()){
23524             this.collapse();
23525             if (!this.blockFocus) {
23526                 this.el.focus();
23527             }
23528             
23529         }else {
23530             this.hasFocus = true;
23531             if(this.triggerAction == 'all') {
23532                 this.doQuery(this.allQuery, true);
23533             } else {
23534                 this.doQuery(this.getRawValue());
23535             }
23536             if (!this.blockFocus) {
23537                 this.el.focus();
23538             }
23539         }
23540     },
23541     listKeyPress : function(e)
23542     {
23543         //Roo.log('listkeypress');
23544         // scroll to first matching element based on key pres..
23545         if (e.isSpecialKey()) {
23546             return false;
23547         }
23548         var k = String.fromCharCode(e.getKey()).toUpperCase();
23549         //Roo.log(k);
23550         var match  = false;
23551         var csel = this.view.getSelectedNodes();
23552         var cselitem = false;
23553         if (csel.length) {
23554             var ix = this.view.indexOf(csel[0]);
23555             cselitem  = this.store.getAt(ix);
23556             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23557                 cselitem = false;
23558             }
23559             
23560         }
23561         
23562         this.store.each(function(v) { 
23563             if (cselitem) {
23564                 // start at existing selection.
23565                 if (cselitem.id == v.id) {
23566                     cselitem = false;
23567                 }
23568                 return;
23569             }
23570                 
23571             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23572                 match = this.store.indexOf(v);
23573                 return false;
23574             }
23575         }, this);
23576         
23577         if (match === false) {
23578             return true; // no more action?
23579         }
23580         // scroll to?
23581         this.view.select(match);
23582         var sn = Roo.get(this.view.getSelectedNodes()[0])
23583         sn.scrollIntoView(sn.dom.parentNode, false);
23584     }
23585
23586     /** 
23587     * @cfg {Boolean} grow 
23588     * @hide 
23589     */
23590     /** 
23591     * @cfg {Number} growMin 
23592     * @hide 
23593     */
23594     /** 
23595     * @cfg {Number} growMax 
23596     * @hide 
23597     */
23598     /**
23599      * @hide
23600      * @method autoSize
23601      */
23602 });/*
23603  * Based on:
23604  * Ext JS Library 1.1.1
23605  * Copyright(c) 2006-2007, Ext JS, LLC.
23606  *
23607  * Originally Released Under LGPL - original licence link has changed is not relivant.
23608  *
23609  * Fork - LGPL
23610  * <script type="text/javascript">
23611  */
23612 /**
23613  * @class Roo.form.Checkbox
23614  * @extends Roo.form.Field
23615  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23616  * @constructor
23617  * Creates a new Checkbox
23618  * @param {Object} config Configuration options
23619  */
23620 Roo.form.Checkbox = function(config){
23621     Roo.form.Checkbox.superclass.constructor.call(this, config);
23622     this.addEvents({
23623         /**
23624          * @event check
23625          * Fires when the checkbox is checked or unchecked.
23626              * @param {Roo.form.Checkbox} this This checkbox
23627              * @param {Boolean} checked The new checked value
23628              */
23629         check : true
23630     });
23631 };
23632
23633 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23634     /**
23635      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23636      */
23637     focusClass : undefined,
23638     /**
23639      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23640      */
23641     fieldClass: "x-form-field",
23642     /**
23643      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23644      */
23645     checked: false,
23646     /**
23647      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23648      * {tag: "input", type: "checkbox", autocomplete: "off"})
23649      */
23650     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23651     /**
23652      * @cfg {String} boxLabel The text that appears beside the checkbox
23653      */
23654     boxLabel : "",
23655     /**
23656      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23657      */  
23658     inputValue : '1',
23659     /**
23660      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23661      */
23662      valueOff: '0', // value when not checked..
23663
23664     actionMode : 'viewEl', 
23665     //
23666     // private
23667     itemCls : 'x-menu-check-item x-form-item',
23668     groupClass : 'x-menu-group-item',
23669     inputType : 'hidden',
23670     
23671     
23672     inSetChecked: false, // check that we are not calling self...
23673     
23674     inputElement: false, // real input element?
23675     basedOn: false, // ????
23676     
23677     isFormField: true, // not sure where this is needed!!!!
23678
23679     onResize : function(){
23680         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23681         if(!this.boxLabel){
23682             this.el.alignTo(this.wrap, 'c-c');
23683         }
23684     },
23685
23686     initEvents : function(){
23687         Roo.form.Checkbox.superclass.initEvents.call(this);
23688         this.el.on("click", this.onClick,  this);
23689         this.el.on("change", this.onClick,  this);
23690     },
23691
23692
23693     getResizeEl : function(){
23694         return this.wrap;
23695     },
23696
23697     getPositionEl : function(){
23698         return this.wrap;
23699     },
23700
23701     // private
23702     onRender : function(ct, position){
23703         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23704         /*
23705         if(this.inputValue !== undefined){
23706             this.el.dom.value = this.inputValue;
23707         }
23708         */
23709         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23710         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23711         var viewEl = this.wrap.createChild({ 
23712             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23713         this.viewEl = viewEl;   
23714         this.wrap.on('click', this.onClick,  this); 
23715         
23716         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23717         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23718         
23719         
23720         
23721         if(this.boxLabel){
23722             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23723         //    viewEl.on('click', this.onClick,  this); 
23724         }
23725         //if(this.checked){
23726             this.setChecked(this.checked);
23727         //}else{
23728             //this.checked = this.el.dom;
23729         //}
23730
23731     },
23732
23733     // private
23734     initValue : Roo.emptyFn,
23735
23736     /**
23737      * Returns the checked state of the checkbox.
23738      * @return {Boolean} True if checked, else false
23739      */
23740     getValue : function(){
23741         if(this.el){
23742             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23743         }
23744         return this.valueOff;
23745         
23746     },
23747
23748         // private
23749     onClick : function(){ 
23750         this.setChecked(!this.checked);
23751
23752         //if(this.el.dom.checked != this.checked){
23753         //    this.setValue(this.el.dom.checked);
23754        // }
23755     },
23756
23757     /**
23758      * Sets the checked state of the checkbox.
23759      * On is always based on a string comparison between inputValue and the param.
23760      * @param {Boolean/String} value - the value to set 
23761      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23762      */
23763     setValue : function(v,suppressEvent){
23764         
23765         
23766         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23767         //if(this.el && this.el.dom){
23768         //    this.el.dom.checked = this.checked;
23769         //    this.el.dom.defaultChecked = this.checked;
23770         //}
23771         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23772         //this.fireEvent("check", this, this.checked);
23773     },
23774     // private..
23775     setChecked : function(state,suppressEvent)
23776     {
23777         if (this.inSetChecked) {
23778             this.checked = state;
23779             return;
23780         }
23781         
23782     
23783         if(this.wrap){
23784             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23785         }
23786         this.checked = state;
23787         if(suppressEvent !== true){
23788             this.fireEvent('check', this, state);
23789         }
23790         this.inSetChecked = true;
23791         this.el.dom.value = state ? this.inputValue : this.valueOff;
23792         this.inSetChecked = false;
23793         
23794     },
23795     // handle setting of hidden value by some other method!!?!?
23796     setFromHidden: function()
23797     {
23798         if(!this.el){
23799             return;
23800         }
23801         //console.log("SET FROM HIDDEN");
23802         //alert('setFrom hidden');
23803         this.setValue(this.el.dom.value);
23804     },
23805     
23806     onDestroy : function()
23807     {
23808         if(this.viewEl){
23809             Roo.get(this.viewEl).remove();
23810         }
23811          
23812         Roo.form.Checkbox.superclass.onDestroy.call(this);
23813     }
23814
23815 });/*
23816  * Based on:
23817  * Ext JS Library 1.1.1
23818  * Copyright(c) 2006-2007, Ext JS, LLC.
23819  *
23820  * Originally Released Under LGPL - original licence link has changed is not relivant.
23821  *
23822  * Fork - LGPL
23823  * <script type="text/javascript">
23824  */
23825  
23826 /**
23827  * @class Roo.form.Radio
23828  * @extends Roo.form.Checkbox
23829  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23830  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23831  * @constructor
23832  * Creates a new Radio
23833  * @param {Object} config Configuration options
23834  */
23835 Roo.form.Radio = function(){
23836     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23837 };
23838 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23839     inputType: 'radio',
23840
23841     /**
23842      * If this radio is part of a group, it will return the selected value
23843      * @return {String}
23844      */
23845     getGroupValue : function(){
23846         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23847     }
23848 });//<script type="text/javascript">
23849
23850 /*
23851  * Ext JS Library 1.1.1
23852  * Copyright(c) 2006-2007, Ext JS, LLC.
23853  * licensing@extjs.com
23854  * 
23855  * http://www.extjs.com/license
23856  */
23857  
23858  /*
23859   * 
23860   * Known bugs:
23861   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23862   * - IE ? - no idea how much works there.
23863   * 
23864   * 
23865   * 
23866   */
23867  
23868
23869 /**
23870  * @class Ext.form.HtmlEditor
23871  * @extends Ext.form.Field
23872  * Provides a lightweight HTML Editor component.
23873  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23874  * 
23875  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23876  * supported by this editor.</b><br/><br/>
23877  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23878  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23879  */
23880 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23881       /**
23882      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23883      */
23884     toolbars : false,
23885     /**
23886      * @cfg {String} createLinkText The default text for the create link prompt
23887      */
23888     createLinkText : 'Please enter the URL for the link:',
23889     /**
23890      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23891      */
23892     defaultLinkValue : 'http:/'+'/',
23893    
23894     
23895     // id of frame..
23896     frameId: false,
23897     
23898     // private properties
23899     validationEvent : false,
23900     deferHeight: true,
23901     initialized : false,
23902     activated : false,
23903     sourceEditMode : false,
23904     onFocus : Roo.emptyFn,
23905     iframePad:3,
23906     hideMode:'offsets',
23907     defaultAutoCreate : {
23908         tag: "textarea",
23909         style:"width:500px;height:300px;",
23910         autocomplete: "off"
23911     },
23912
23913     // private
23914     initComponent : function(){
23915         this.addEvents({
23916             /**
23917              * @event initialize
23918              * Fires when the editor is fully initialized (including the iframe)
23919              * @param {HtmlEditor} this
23920              */
23921             initialize: true,
23922             /**
23923              * @event activate
23924              * Fires when the editor is first receives the focus. Any insertion must wait
23925              * until after this event.
23926              * @param {HtmlEditor} this
23927              */
23928             activate: true,
23929              /**
23930              * @event beforesync
23931              * Fires before the textarea is updated with content from the editor iframe. Return false
23932              * to cancel the sync.
23933              * @param {HtmlEditor} this
23934              * @param {String} html
23935              */
23936             beforesync: true,
23937              /**
23938              * @event beforepush
23939              * Fires before the iframe editor is updated with content from the textarea. Return false
23940              * to cancel the push.
23941              * @param {HtmlEditor} this
23942              * @param {String} html
23943              */
23944             beforepush: true,
23945              /**
23946              * @event sync
23947              * Fires when the textarea is updated with content from the editor iframe.
23948              * @param {HtmlEditor} this
23949              * @param {String} html
23950              */
23951             sync: true,
23952              /**
23953              * @event push
23954              * Fires when the iframe editor is updated with content from the textarea.
23955              * @param {HtmlEditor} this
23956              * @param {String} html
23957              */
23958             push: true,
23959              /**
23960              * @event editmodechange
23961              * Fires when the editor switches edit modes
23962              * @param {HtmlEditor} this
23963              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23964              */
23965             editmodechange: true,
23966             /**
23967              * @event editorevent
23968              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23969              * @param {HtmlEditor} this
23970              */
23971             editorevent: true
23972         })
23973     },
23974
23975     /**
23976      * Protected method that will not generally be called directly. It
23977      * is called when the editor creates its toolbar. Override this method if you need to
23978      * add custom toolbar buttons.
23979      * @param {HtmlEditor} editor
23980      */
23981     createToolbar : function(editor){
23982         if (!editor.toolbars || !editor.toolbars.length) {
23983             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23984         }
23985         
23986         for (var i =0 ; i < editor.toolbars.length;i++) {
23987             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23988             editor.toolbars[i].init(editor);
23989         }
23990          
23991         
23992     },
23993
23994     /**
23995      * Protected method that will not generally be called directly. It
23996      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23997      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23998      */
23999     getDocMarkup : function(){
24000         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24001     },
24002
24003     // private
24004     onRender : function(ct, position){
24005         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24006         this.el.dom.style.border = '0 none';
24007         this.el.dom.setAttribute('tabIndex', -1);
24008         this.el.addClass('x-hidden');
24009         if(Roo.isIE){ // fix IE 1px bogus margin
24010             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24011         }
24012         this.wrap = this.el.wrap({
24013             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24014         });
24015
24016         this.frameId = Roo.id();
24017         this.createToolbar(this);
24018         
24019         
24020         
24021         
24022       
24023         
24024         var iframe = this.wrap.createChild({
24025             tag: 'iframe',
24026             id: this.frameId,
24027             name: this.frameId,
24028             frameBorder : 'no',
24029             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24030         });
24031         
24032        // console.log(iframe);
24033         //this.wrap.dom.appendChild(iframe);
24034
24035         this.iframe = iframe.dom;
24036
24037          this.assignDocWin();
24038         
24039         this.doc.designMode = 'on';
24040        
24041         this.doc.open();
24042         this.doc.write(this.getDocMarkup());
24043         this.doc.close();
24044
24045         
24046         var task = { // must defer to wait for browser to be ready
24047             run : function(){
24048                 //console.log("run task?" + this.doc.readyState);
24049                 this.assignDocWin();
24050                 if(this.doc.body || this.doc.readyState == 'complete'){
24051                     try {
24052                         this.doc.designMode="on";
24053                     } catch (e) {
24054                         return;
24055                     }
24056                     Roo.TaskMgr.stop(task);
24057                     this.initEditor.defer(10, this);
24058                 }
24059             },
24060             interval : 10,
24061             duration:10000,
24062             scope: this
24063         };
24064         Roo.TaskMgr.start(task);
24065
24066         if(!this.width){
24067             this.setSize(this.el.getSize());
24068         }
24069     },
24070
24071     // private
24072     onResize : function(w, h){
24073         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24074         if(this.el && this.iframe){
24075             if(typeof w == 'number'){
24076                 var aw = w - this.wrap.getFrameWidth('lr');
24077                 this.el.setWidth(this.adjustWidth('textarea', aw));
24078                 this.iframe.style.width = aw + 'px';
24079             }
24080             if(typeof h == 'number'){
24081                 var tbh = 0;
24082                 for (var i =0; i < this.toolbars.length;i++) {
24083                     // fixme - ask toolbars for heights?
24084                     tbh += this.toolbars[i].tb.el.getHeight();
24085                 }
24086                 
24087                 
24088                 
24089                 
24090                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24091                 this.el.setHeight(this.adjustWidth('textarea', ah));
24092                 this.iframe.style.height = ah + 'px';
24093                 if(this.doc){
24094                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24095                 }
24096             }
24097         }
24098     },
24099
24100     /**
24101      * Toggles the editor between standard and source edit mode.
24102      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24103      */
24104     toggleSourceEdit : function(sourceEditMode){
24105         
24106         this.sourceEditMode = sourceEditMode === true;
24107         
24108         if(this.sourceEditMode){
24109           
24110             this.syncValue();
24111             this.iframe.className = 'x-hidden';
24112             this.el.removeClass('x-hidden');
24113             this.el.dom.removeAttribute('tabIndex');
24114             this.el.focus();
24115         }else{
24116              
24117             this.pushValue();
24118             this.iframe.className = '';
24119             this.el.addClass('x-hidden');
24120             this.el.dom.setAttribute('tabIndex', -1);
24121             this.deferFocus();
24122         }
24123         this.setSize(this.wrap.getSize());
24124         this.fireEvent('editmodechange', this, this.sourceEditMode);
24125     },
24126
24127     // private used internally
24128     createLink : function(){
24129         var url = prompt(this.createLinkText, this.defaultLinkValue);
24130         if(url && url != 'http:/'+'/'){
24131             this.relayCmd('createlink', url);
24132         }
24133     },
24134
24135     // private (for BoxComponent)
24136     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24137
24138     // private (for BoxComponent)
24139     getResizeEl : function(){
24140         return this.wrap;
24141     },
24142
24143     // private (for BoxComponent)
24144     getPositionEl : function(){
24145         return this.wrap;
24146     },
24147
24148     // private
24149     initEvents : function(){
24150         this.originalValue = this.getValue();
24151     },
24152
24153     /**
24154      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24155      * @method
24156      */
24157     markInvalid : Roo.emptyFn,
24158     /**
24159      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24160      * @method
24161      */
24162     clearInvalid : Roo.emptyFn,
24163
24164     setValue : function(v){
24165         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24166         this.pushValue();
24167     },
24168
24169     /**
24170      * Protected method that will not generally be called directly. If you need/want
24171      * custom HTML cleanup, this is the method you should override.
24172      * @param {String} html The HTML to be cleaned
24173      * return {String} The cleaned HTML
24174      */
24175     cleanHtml : function(html){
24176         html = String(html);
24177         if(html.length > 5){
24178             if(Roo.isSafari){ // strip safari nonsense
24179                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24180             }
24181         }
24182         if(html == '&nbsp;'){
24183             html = '';
24184         }
24185         return html;
24186     },
24187
24188     /**
24189      * Protected method that will not generally be called directly. Syncs the contents
24190      * of the editor iframe with the textarea.
24191      */
24192     syncValue : function(){
24193         if(this.initialized){
24194             var bd = (this.doc.body || this.doc.documentElement);
24195             var html = bd.innerHTML;
24196             if(Roo.isSafari){
24197                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24198                 var m = bs.match(/text-align:(.*?);/i);
24199                 if(m && m[1]){
24200                     html = '<div style="'+m[0]+'">' + html + '</div>';
24201                 }
24202             }
24203             html = this.cleanHtml(html);
24204             if(this.fireEvent('beforesync', this, html) !== false){
24205                 this.el.dom.value = html;
24206                 this.fireEvent('sync', this, html);
24207             }
24208         }
24209     },
24210
24211     /**
24212      * Protected method that will not generally be called directly. Pushes the value of the textarea
24213      * into the iframe editor.
24214      */
24215     pushValue : function(){
24216         if(this.initialized){
24217             var v = this.el.dom.value;
24218             if(v.length < 1){
24219                 v = '&#160;';
24220             }
24221             if(this.fireEvent('beforepush', this, v) !== false){
24222                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24223                 this.fireEvent('push', this, v);
24224             }
24225         }
24226     },
24227
24228     // private
24229     deferFocus : function(){
24230         this.focus.defer(10, this);
24231     },
24232
24233     // doc'ed in Field
24234     focus : function(){
24235         if(this.win && !this.sourceEditMode){
24236             this.win.focus();
24237         }else{
24238             this.el.focus();
24239         }
24240     },
24241     
24242     assignDocWin: function()
24243     {
24244         var iframe = this.iframe;
24245         
24246          if(Roo.isIE){
24247             this.doc = iframe.contentWindow.document;
24248             this.win = iframe.contentWindow;
24249         } else {
24250             if (!Roo.get(this.frameId)) {
24251                 return;
24252             }
24253             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24254             this.win = Roo.get(this.frameId).dom.contentWindow;
24255         }
24256     },
24257     
24258     // private
24259     initEditor : function(){
24260         //console.log("INIT EDITOR");
24261         this.assignDocWin();
24262         
24263         
24264         
24265         this.doc.designMode="on";
24266         this.doc.open();
24267         this.doc.write(this.getDocMarkup());
24268         this.doc.close();
24269         
24270         var dbody = (this.doc.body || this.doc.documentElement);
24271         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24272         // this copies styles from the containing element into thsi one..
24273         // not sure why we need all of this..
24274         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24275         ss['background-attachment'] = 'fixed'; // w3c
24276         dbody.bgProperties = 'fixed'; // ie
24277         Roo.DomHelper.applyStyles(dbody, ss);
24278         Roo.EventManager.on(this.doc, {
24279             'mousedown': this.onEditorEvent,
24280             'dblclick': this.onEditorEvent,
24281             'click': this.onEditorEvent,
24282             'keyup': this.onEditorEvent,
24283             buffer:100,
24284             scope: this
24285         });
24286         if(Roo.isGecko){
24287             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24288         }
24289         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24290             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24291         }
24292         this.initialized = true;
24293
24294         this.fireEvent('initialize', this);
24295         this.pushValue();
24296     },
24297
24298     // private
24299     onDestroy : function(){
24300         
24301         
24302         
24303         if(this.rendered){
24304             
24305             for (var i =0; i < this.toolbars.length;i++) {
24306                 // fixme - ask toolbars for heights?
24307                 this.toolbars[i].onDestroy();
24308             }
24309             
24310             this.wrap.dom.innerHTML = '';
24311             this.wrap.remove();
24312         }
24313     },
24314
24315     // private
24316     onFirstFocus : function(){
24317         
24318         this.assignDocWin();
24319         
24320         
24321         this.activated = true;
24322         for (var i =0; i < this.toolbars.length;i++) {
24323             this.toolbars[i].onFirstFocus();
24324         }
24325        
24326         if(Roo.isGecko){ // prevent silly gecko errors
24327             this.win.focus();
24328             var s = this.win.getSelection();
24329             if(!s.focusNode || s.focusNode.nodeType != 3){
24330                 var r = s.getRangeAt(0);
24331                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24332                 r.collapse(true);
24333                 this.deferFocus();
24334             }
24335             try{
24336                 this.execCmd('useCSS', true);
24337                 this.execCmd('styleWithCSS', false);
24338             }catch(e){}
24339         }
24340         this.fireEvent('activate', this);
24341     },
24342
24343     // private
24344     adjustFont: function(btn){
24345         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24346         //if(Roo.isSafari){ // safari
24347         //    adjust *= 2;
24348        // }
24349         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24350         if(Roo.isSafari){ // safari
24351             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24352             v =  (v < 10) ? 10 : v;
24353             v =  (v > 48) ? 48 : v;
24354             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24355             
24356         }
24357         
24358         
24359         v = Math.max(1, v+adjust);
24360         
24361         this.execCmd('FontSize', v  );
24362     },
24363
24364     onEditorEvent : function(e){
24365         this.fireEvent('editorevent', this, e);
24366       //  this.updateToolbar();
24367         this.syncValue();
24368     },
24369
24370     insertTag : function(tg)
24371     {
24372         // could be a bit smarter... -> wrap the current selected tRoo..
24373         
24374         this.execCmd("formatblock",   tg);
24375         
24376     },
24377     
24378     insertText : function(txt)
24379     {
24380         
24381         
24382         range = this.createRange();
24383         range.deleteContents();
24384                //alert(Sender.getAttribute('label'));
24385                
24386         range.insertNode(this.doc.createTextNode(txt));
24387     } ,
24388     
24389     // private
24390     relayBtnCmd : function(btn){
24391         this.relayCmd(btn.cmd);
24392     },
24393
24394     /**
24395      * Executes a Midas editor command on the editor document and performs necessary focus and
24396      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24397      * @param {String} cmd The Midas command
24398      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24399      */
24400     relayCmd : function(cmd, value){
24401         this.win.focus();
24402         this.execCmd(cmd, value);
24403         this.fireEvent('editorevent', this);
24404         //this.updateToolbar();
24405         this.deferFocus();
24406     },
24407
24408     /**
24409      * Executes a Midas editor command directly on the editor document.
24410      * For visual commands, you should use {@link #relayCmd} instead.
24411      * <b>This should only be called after the editor is initialized.</b>
24412      * @param {String} cmd The Midas command
24413      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24414      */
24415     execCmd : function(cmd, value){
24416         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24417         this.syncValue();
24418     },
24419
24420    
24421     /**
24422      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24423      * to insert tRoo.
24424      * @param {String} text
24425      */
24426     insertAtCursor : function(text){
24427         if(!this.activated){
24428             return;
24429         }
24430         if(Roo.isIE){
24431             this.win.focus();
24432             var r = this.doc.selection.createRange();
24433             if(r){
24434                 r.collapse(true);
24435                 r.pasteHTML(text);
24436                 this.syncValue();
24437                 this.deferFocus();
24438             }
24439         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24440             this.win.focus();
24441             this.execCmd('InsertHTML', text);
24442             this.deferFocus();
24443         }
24444     },
24445  // private
24446     mozKeyPress : function(e){
24447         if(e.ctrlKey){
24448             var c = e.getCharCode(), cmd;
24449           
24450             if(c > 0){
24451                 c = String.fromCharCode(c).toLowerCase();
24452                 switch(c){
24453                     case 'b':
24454                         cmd = 'bold';
24455                     break;
24456                     case 'i':
24457                         cmd = 'italic';
24458                     break;
24459                     case 'u':
24460                         cmd = 'underline';
24461                     case 'v':
24462                         this.cleanUpPaste.defer(100, this);
24463                         return;
24464                     break;
24465                 }
24466                 if(cmd){
24467                     this.win.focus();
24468                     this.execCmd(cmd);
24469                     this.deferFocus();
24470                     e.preventDefault();
24471                 }
24472                 
24473             }
24474         }
24475     },
24476
24477     // private
24478     fixKeys : function(){ // load time branching for fastest keydown performance
24479         if(Roo.isIE){
24480             return function(e){
24481                 var k = e.getKey(), r;
24482                 if(k == e.TAB){
24483                     e.stopEvent();
24484                     r = this.doc.selection.createRange();
24485                     if(r){
24486                         r.collapse(true);
24487                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24488                         this.deferFocus();
24489                     }
24490                     return;
24491                 }
24492                 
24493                 if(k == e.ENTER){
24494                     r = this.doc.selection.createRange();
24495                     if(r){
24496                         var target = r.parentElement();
24497                         if(!target || target.tagName.toLowerCase() != 'li'){
24498                             e.stopEvent();
24499                             r.pasteHTML('<br />');
24500                             r.collapse(false);
24501                             r.select();
24502                         }
24503                     }
24504                 }
24505                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24506                     this.cleanUpPaste.defer(100, this);
24507                     return;
24508                 }
24509                 
24510                 
24511             };
24512         }else if(Roo.isOpera){
24513             return function(e){
24514                 var k = e.getKey();
24515                 if(k == e.TAB){
24516                     e.stopEvent();
24517                     this.win.focus();
24518                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24519                     this.deferFocus();
24520                 }
24521                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24522                     this.cleanUpPaste.defer(100, this);
24523                     return;
24524                 }
24525                 
24526             };
24527         }else if(Roo.isSafari){
24528             return function(e){
24529                 var k = e.getKey();
24530                 
24531                 if(k == e.TAB){
24532                     e.stopEvent();
24533                     this.execCmd('InsertText','\t');
24534                     this.deferFocus();
24535                     return;
24536                 }
24537                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24538                     this.cleanUpPaste.defer(100, this);
24539                     return;
24540                 }
24541                 
24542              };
24543         }
24544     }(),
24545     
24546     getAllAncestors: function()
24547     {
24548         var p = this.getSelectedNode();
24549         var a = [];
24550         if (!p) {
24551             a.push(p); // push blank onto stack..
24552             p = this.getParentElement();
24553         }
24554         
24555         
24556         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24557             a.push(p);
24558             p = p.parentNode;
24559         }
24560         a.push(this.doc.body);
24561         return a;
24562     },
24563     lastSel : false,
24564     lastSelNode : false,
24565     
24566     
24567     getSelection : function() 
24568     {
24569         this.assignDocWin();
24570         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24571     },
24572     
24573     getSelectedNode: function() 
24574     {
24575         // this may only work on Gecko!!!
24576         
24577         // should we cache this!!!!
24578         
24579         
24580         
24581          
24582         var range = this.createRange(this.getSelection());
24583         
24584         if (Roo.isIE) {
24585             var parent = range.parentElement();
24586             while (true) {
24587                 var testRange = range.duplicate();
24588                 testRange.moveToElementText(parent);
24589                 if (testRange.inRange(range)) {
24590                     break;
24591                 }
24592                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24593                     break;
24594                 }
24595                 parent = parent.parentElement;
24596             }
24597             return parent;
24598         }
24599         
24600         
24601         var ar = range.endContainer.childNodes;
24602         if (!ar.length) {
24603             ar = range.commonAncestorContainer.childNodes;
24604             //alert(ar.length);
24605         }
24606         var nodes = [];
24607         var other_nodes = [];
24608         var has_other_nodes = false;
24609         for (var i=0;i<ar.length;i++) {
24610             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24611                 continue;
24612             }
24613             // fullly contained node.
24614             
24615             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24616                 nodes.push(ar[i]);
24617                 continue;
24618             }
24619             
24620             // probably selected..
24621             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24622                 other_nodes.push(ar[i]);
24623                 continue;
24624             }
24625             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24626                 continue;
24627             }
24628             
24629             
24630             has_other_nodes = true;
24631         }
24632         if (!nodes.length && other_nodes.length) {
24633             nodes= other_nodes;
24634         }
24635         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24636             return false;
24637         }
24638         
24639         return nodes[0];
24640     },
24641     createRange: function(sel)
24642     {
24643         // this has strange effects when using with 
24644         // top toolbar - not sure if it's a great idea.
24645         //this.editor.contentWindow.focus();
24646         if (typeof sel != "undefined") {
24647             try {
24648                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24649             } catch(e) {
24650                 return this.doc.createRange();
24651             }
24652         } else {
24653             return this.doc.createRange();
24654         }
24655     },
24656     getParentElement: function()
24657     {
24658         
24659         this.assignDocWin();
24660         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24661         
24662         var range = this.createRange(sel);
24663          
24664         try {
24665             var p = range.commonAncestorContainer;
24666             while (p.nodeType == 3) { // text node
24667                 p = p.parentNode;
24668             }
24669             return p;
24670         } catch (e) {
24671             return null;
24672         }
24673     
24674     },
24675     
24676     
24677     
24678     // BC Hacks - cause I cant work out what i was trying to do..
24679     rangeIntersectsNode : function(range, node)
24680     {
24681         var nodeRange = node.ownerDocument.createRange();
24682         try {
24683             nodeRange.selectNode(node);
24684         }
24685         catch (e) {
24686             nodeRange.selectNodeContents(node);
24687         }
24688
24689         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24690                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24691     },
24692     rangeCompareNode : function(range, node) {
24693         var nodeRange = node.ownerDocument.createRange();
24694         try {
24695             nodeRange.selectNode(node);
24696         } catch (e) {
24697             nodeRange.selectNodeContents(node);
24698         }
24699         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24700         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24701
24702         if (nodeIsBefore && !nodeIsAfter)
24703             return 0;
24704         if (!nodeIsBefore && nodeIsAfter)
24705             return 1;
24706         if (nodeIsBefore && nodeIsAfter)
24707             return 2;
24708
24709         return 3;
24710     },
24711
24712     // private? - in a new class?
24713     cleanUpPaste :  function()
24714     {
24715         // cleans up the whole document..
24716       //  console.log('cleanuppaste');
24717         this.cleanUpChildren(this.doc.body)
24718         
24719         
24720     },
24721     cleanUpChildren : function (n)
24722     {
24723         if (!n.childNodes.length) {
24724             return;
24725         }
24726         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24727            this.cleanUpChild(n.childNodes[i]);
24728         }
24729     },
24730     
24731     
24732         
24733     
24734     cleanUpChild : function (node)
24735     {
24736         //console.log(node);
24737         if (node.nodeName == "#text") {
24738             // clean up silly Windows -- stuff?
24739             return; 
24740         }
24741         if (node.nodeName == "#comment") {
24742             node.parentNode.removeChild(node);
24743             // clean up silly Windows -- stuff?
24744             return; 
24745         }
24746         
24747         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24748             // remove node.
24749             node.parentNode.removeChild(node);
24750             return;
24751             
24752         }
24753         if (!node.attributes || !node.attributes.length) {
24754             this.cleanUpChildren(node);
24755             return;
24756         }
24757         
24758         function cleanAttr(n,v)
24759         {
24760             
24761             if (v.match(/^\./) || v.match(/^\//)) {
24762                 return;
24763             }
24764             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24765                 return;
24766             }
24767             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24768             node.removeAttribute(n);
24769             
24770         }
24771         
24772         function cleanStyle(n,v)
24773         {
24774             if (v.match(/expression/)) { //XSS?? should we even bother..
24775                 node.removeAttribute(n);
24776                 return;
24777             }
24778             
24779             
24780             var parts = v.split(/;/);
24781             Roo.each(parts, function(p) {
24782                 p = p.replace(/\s+/g,'');
24783                 if (!p.length) {
24784                     return;
24785                 }
24786                 var l = p.split(':').shift().replace(/\s+/g,'');
24787                 
24788                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24789                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24790                     node.removeAttribute(n);
24791                     return false;
24792                 }
24793             });
24794             
24795             
24796         }
24797         
24798         
24799         for (var i = node.attributes.length-1; i > -1 ; i--) {
24800             var a = node.attributes[i];
24801             //console.log(a);
24802             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24803                 node.removeAttribute(a.name);
24804                 return;
24805             }
24806             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24807                 cleanAttr(a.name,a.value); // fixme..
24808                 return;
24809             }
24810             if (a.name == 'style') {
24811                 cleanStyle(a.name,a.value);
24812             }
24813             /// clean up MS crap..
24814             if (a.name == 'class') {
24815                 if (a.value.match(/^Mso/)) {
24816                     node.className = '';
24817                 }
24818             }
24819             
24820             // style cleanup!?
24821             // class cleanup?
24822             
24823         }
24824         
24825         
24826         this.cleanUpChildren(node);
24827         
24828         
24829     }
24830     
24831     
24832     // hide stuff that is not compatible
24833     /**
24834      * @event blur
24835      * @hide
24836      */
24837     /**
24838      * @event change
24839      * @hide
24840      */
24841     /**
24842      * @event focus
24843      * @hide
24844      */
24845     /**
24846      * @event specialkey
24847      * @hide
24848      */
24849     /**
24850      * @cfg {String} fieldClass @hide
24851      */
24852     /**
24853      * @cfg {String} focusClass @hide
24854      */
24855     /**
24856      * @cfg {String} autoCreate @hide
24857      */
24858     /**
24859      * @cfg {String} inputType @hide
24860      */
24861     /**
24862      * @cfg {String} invalidClass @hide
24863      */
24864     /**
24865      * @cfg {String} invalidText @hide
24866      */
24867     /**
24868      * @cfg {String} msgFx @hide
24869      */
24870     /**
24871      * @cfg {String} validateOnBlur @hide
24872      */
24873 });
24874
24875 Roo.form.HtmlEditor.white = [
24876         'area', 'br', 'img', 'input', 'hr', 'wbr',
24877         
24878        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24879        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24880        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24881        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24882        'table',   'ul',         'xmp', 
24883        
24884        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24885       'thead',   'tr', 
24886      
24887       'dir', 'menu', 'ol', 'ul', 'dl',
24888        
24889       'embed',  'object'
24890 ];
24891
24892
24893 Roo.form.HtmlEditor.black = [
24894     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24895         'applet', // 
24896         'base',   'basefont', 'bgsound', 'blink',  'body', 
24897         'frame',  'frameset', 'head',    'html',   'ilayer', 
24898         'iframe', 'layer',  'link',     'meta',    'object',   
24899         'script', 'style' ,'title',  'xml' // clean later..
24900 ];
24901 Roo.form.HtmlEditor.clean = [
24902     'script', 'style', 'title', 'xml'
24903 ];
24904
24905 // attributes..
24906
24907 Roo.form.HtmlEditor.ablack = [
24908     'on'
24909 ];
24910     
24911 Roo.form.HtmlEditor.aclean = [ 
24912     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24913 ];
24914
24915 // protocols..
24916 Roo.form.HtmlEditor.pwhite= [
24917         'http',  'https',  'mailto'
24918 ];
24919
24920 Roo.form.HtmlEditor.cwhite= [
24921         'text-align',
24922         'font-size'
24923 ];
24924
24925 // <script type="text/javascript">
24926 /*
24927  * Based on
24928  * Ext JS Library 1.1.1
24929  * Copyright(c) 2006-2007, Ext JS, LLC.
24930  *  
24931  
24932  */
24933
24934 /**
24935  * @class Roo.form.HtmlEditorToolbar1
24936  * Basic Toolbar
24937  * 
24938  * Usage:
24939  *
24940  new Roo.form.HtmlEditor({
24941     ....
24942     toolbars : [
24943         new Roo.form.HtmlEditorToolbar1({
24944             disable : { fonts: 1 , format: 1, ..., ... , ...],
24945             btns : [ .... ]
24946         })
24947     }
24948      
24949  * 
24950  * @cfg {Object} disable List of elements to disable..
24951  * @cfg {Array} btns List of additional buttons.
24952  * 
24953  * 
24954  * NEEDS Extra CSS? 
24955  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24956  */
24957  
24958 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24959 {
24960     
24961     Roo.apply(this, config);
24962     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24963     // dont call parent... till later.
24964 }
24965
24966 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24967     
24968     tb: false,
24969     
24970     rendered: false,
24971     
24972     editor : false,
24973     /**
24974      * @cfg {Object} disable  List of toolbar elements to disable
24975          
24976      */
24977     disable : false,
24978       /**
24979      * @cfg {Array} fontFamilies An array of available font families
24980      */
24981     fontFamilies : [
24982         'Arial',
24983         'Courier New',
24984         'Tahoma',
24985         'Times New Roman',
24986         'Verdana'
24987     ],
24988     
24989     specialChars : [
24990            "&#169;",
24991           "&#174;",     
24992           "&#8482;",    
24993           "&#163;" ,    
24994          // "&#8212;",    
24995           "&#8230;",    
24996           "&#247;" ,    
24997         //  "&#225;" ,     ?? a acute?
24998            "&#8364;"    , //Euro
24999        //   "&#8220;"    ,
25000         //  "&#8221;"    ,
25001         //  "&#8226;"    ,
25002           "&#176;"  //   , // degrees
25003
25004          // "&#233;"     , // e ecute
25005          // "&#250;"     , // u ecute?
25006     ],
25007     inputElements : [ 
25008             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25009             "input:submit", "input:button", "select", "textarea", "label" ],
25010     formats : [
25011         ["p"] ,  
25012         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25013         ["pre"],[ "code"], 
25014         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25015     ],
25016      /**
25017      * @cfg {String} defaultFont default font to use.
25018      */
25019     defaultFont: 'tahoma',
25020    
25021     fontSelect : false,
25022     
25023     
25024     formatCombo : false,
25025     
25026     init : function(editor)
25027     {
25028         this.editor = editor;
25029         
25030         
25031         var fid = editor.frameId;
25032         var etb = this;
25033         function btn(id, toggle, handler){
25034             var xid = fid + '-'+ id ;
25035             return {
25036                 id : xid,
25037                 cmd : id,
25038                 cls : 'x-btn-icon x-edit-'+id,
25039                 enableToggle:toggle !== false,
25040                 scope: editor, // was editor...
25041                 handler:handler||editor.relayBtnCmd,
25042                 clickEvent:'mousedown',
25043                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25044                 tabIndex:-1
25045             };
25046         }
25047         
25048         
25049         
25050         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25051         this.tb = tb;
25052          // stop form submits
25053         tb.el.on('click', function(e){
25054             e.preventDefault(); // what does this do?
25055         });
25056
25057         if(!this.disable.font && !Roo.isSafari){
25058             /* why no safari for fonts
25059             editor.fontSelect = tb.el.createChild({
25060                 tag:'select',
25061                 tabIndex: -1,
25062                 cls:'x-font-select',
25063                 html: editor.createFontOptions()
25064             });
25065             editor.fontSelect.on('change', function(){
25066                 var font = editor.fontSelect.dom.value;
25067                 editor.relayCmd('fontname', font);
25068                 editor.deferFocus();
25069             }, editor);
25070             tb.add(
25071                 editor.fontSelect.dom,
25072                 '-'
25073             );
25074             */
25075         };
25076         if(!this.disable.formats){
25077             this.formatCombo = new Roo.form.ComboBox({
25078                 store: new Roo.data.SimpleStore({
25079                     id : 'tag',
25080                     fields: ['tag'],
25081                     data : this.formats // from states.js
25082                 }),
25083                 blockFocus : true,
25084                 //autoCreate : {tag: "div",  size: "20"},
25085                 displayField:'tag',
25086                 typeAhead: false,
25087                 mode: 'local',
25088                 editable : false,
25089                 triggerAction: 'all',
25090                 emptyText:'Add tag',
25091                 selectOnFocus:true,
25092                 width:135,
25093                 listeners : {
25094                     'select': function(c, r, i) {
25095                         editor.insertTag(r.get('tag'));
25096                         editor.focus();
25097                     }
25098                 }
25099
25100             });
25101             tb.addField(this.formatCombo);
25102             
25103         }
25104         
25105         if(!this.disable.format){
25106             tb.add(
25107                 btn('bold'),
25108                 btn('italic'),
25109                 btn('underline')
25110             );
25111         };
25112         if(!this.disable.fontSize){
25113             tb.add(
25114                 '-',
25115                 
25116                 
25117                 btn('increasefontsize', false, editor.adjustFont),
25118                 btn('decreasefontsize', false, editor.adjustFont)
25119             );
25120         };
25121         
25122         
25123         if(this.disable.colors){
25124             tb.add(
25125                 '-', {
25126                     id:editor.frameId +'-forecolor',
25127                     cls:'x-btn-icon x-edit-forecolor',
25128                     clickEvent:'mousedown',
25129                     tooltip: this.buttonTips['forecolor'] || undefined,
25130                     tabIndex:-1,
25131                     menu : new Roo.menu.ColorMenu({
25132                         allowReselect: true,
25133                         focus: Roo.emptyFn,
25134                         value:'000000',
25135                         plain:true,
25136                         selectHandler: function(cp, color){
25137                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25138                             editor.deferFocus();
25139                         },
25140                         scope: editor,
25141                         clickEvent:'mousedown'
25142                     })
25143                 }, {
25144                     id:editor.frameId +'backcolor',
25145                     cls:'x-btn-icon x-edit-backcolor',
25146                     clickEvent:'mousedown',
25147                     tooltip: this.buttonTips['backcolor'] || undefined,
25148                     tabIndex:-1,
25149                     menu : new Roo.menu.ColorMenu({
25150                         focus: Roo.emptyFn,
25151                         value:'FFFFFF',
25152                         plain:true,
25153                         allowReselect: true,
25154                         selectHandler: function(cp, color){
25155                             if(Roo.isGecko){
25156                                 editor.execCmd('useCSS', false);
25157                                 editor.execCmd('hilitecolor', color);
25158                                 editor.execCmd('useCSS', true);
25159                                 editor.deferFocus();
25160                             }else{
25161                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25162                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25163                                 editor.deferFocus();
25164                             }
25165                         },
25166                         scope:editor,
25167                         clickEvent:'mousedown'
25168                     })
25169                 }
25170             );
25171         };
25172         // now add all the items...
25173         
25174
25175         if(!this.disable.alignments){
25176             tb.add(
25177                 '-',
25178                 btn('justifyleft'),
25179                 btn('justifycenter'),
25180                 btn('justifyright')
25181             );
25182         };
25183
25184         //if(!Roo.isSafari){
25185             if(!this.disable.links){
25186                 tb.add(
25187                     '-',
25188                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25189                 );
25190             };
25191
25192             if(!this.disable.lists){
25193                 tb.add(
25194                     '-',
25195                     btn('insertorderedlist'),
25196                     btn('insertunorderedlist')
25197                 );
25198             }
25199             if(!this.disable.sourceEdit){
25200                 tb.add(
25201                     '-',
25202                     btn('sourceedit', true, function(btn){
25203                         this.toggleSourceEdit(btn.pressed);
25204                     })
25205                 );
25206             }
25207         //}
25208         
25209         var smenu = { };
25210         // special menu.. - needs to be tidied up..
25211         if (!this.disable.special) {
25212             smenu = {
25213                 text: "&#169;",
25214                 cls: 'x-edit-none',
25215                 menu : {
25216                     items : []
25217                    }
25218             };
25219             for (var i =0; i < this.specialChars.length; i++) {
25220                 smenu.menu.items.push({
25221                     
25222                     html: this.specialChars[i],
25223                     handler: function(a,b) {
25224                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25225                         
25226                     },
25227                     tabIndex:-1
25228                 });
25229             }
25230             
25231             
25232             tb.add(smenu);
25233             
25234             
25235         }
25236         if (this.btns) {
25237             for(var i =0; i< this.btns.length;i++) {
25238                 var b = this.btns[i];
25239                 b.cls =  'x-edit-none';
25240                 b.scope = editor;
25241                 tb.add(b);
25242             }
25243         
25244         }
25245         
25246         
25247         
25248         // disable everything...
25249         
25250         this.tb.items.each(function(item){
25251            if(item.id != editor.frameId+ '-sourceedit'){
25252                 item.disable();
25253             }
25254         });
25255         this.rendered = true;
25256         
25257         // the all the btns;
25258         editor.on('editorevent', this.updateToolbar, this);
25259         // other toolbars need to implement this..
25260         //editor.on('editmodechange', this.updateToolbar, this);
25261     },
25262     
25263     
25264     
25265     /**
25266      * Protected method that will not generally be called directly. It triggers
25267      * a toolbar update by reading the markup state of the current selection in the editor.
25268      */
25269     updateToolbar: function(){
25270
25271         if(!this.editor.activated){
25272             this.editor.onFirstFocus();
25273             return;
25274         }
25275
25276         var btns = this.tb.items.map, 
25277             doc = this.editor.doc,
25278             frameId = this.editor.frameId;
25279
25280         if(!this.disable.font && !Roo.isSafari){
25281             /*
25282             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25283             if(name != this.fontSelect.dom.value){
25284                 this.fontSelect.dom.value = name;
25285             }
25286             */
25287         }
25288         if(!this.disable.format){
25289             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25290             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25291             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25292         }
25293         if(!this.disable.alignments){
25294             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25295             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25296             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25297         }
25298         if(!Roo.isSafari && !this.disable.lists){
25299             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25300             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25301         }
25302         
25303         var ans = this.editor.getAllAncestors();
25304         if (this.formatCombo) {
25305             
25306             
25307             var store = this.formatCombo.store;
25308             this.formatCombo.setValue("");
25309             for (var i =0; i < ans.length;i++) {
25310                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25311                     // select it..
25312                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25313                     break;
25314                 }
25315             }
25316         }
25317         
25318         
25319         
25320         // hides menus... - so this cant be on a menu...
25321         Roo.menu.MenuMgr.hideAll();
25322
25323         //this.editorsyncValue();
25324     },
25325    
25326     
25327     createFontOptions : function(){
25328         var buf = [], fs = this.fontFamilies, ff, lc;
25329         for(var i = 0, len = fs.length; i< len; i++){
25330             ff = fs[i];
25331             lc = ff.toLowerCase();
25332             buf.push(
25333                 '<option value="',lc,'" style="font-family:',ff,';"',
25334                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25335                     ff,
25336                 '</option>'
25337             );
25338         }
25339         return buf.join('');
25340     },
25341     
25342     toggleSourceEdit : function(sourceEditMode){
25343         if(sourceEditMode === undefined){
25344             sourceEditMode = !this.sourceEditMode;
25345         }
25346         this.sourceEditMode = sourceEditMode === true;
25347         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25348         // just toggle the button?
25349         if(btn.pressed !== this.editor.sourceEditMode){
25350             btn.toggle(this.editor.sourceEditMode);
25351             return;
25352         }
25353         
25354         if(this.sourceEditMode){
25355             this.tb.items.each(function(item){
25356                 if(item.cmd != 'sourceedit'){
25357                     item.disable();
25358                 }
25359             });
25360           
25361         }else{
25362             if(this.initialized){
25363                 this.tb.items.each(function(item){
25364                     item.enable();
25365                 });
25366             }
25367             
25368         }
25369         // tell the editor that it's been pressed..
25370         this.editor.toggleSourceEdit(sourceEditMode);
25371        
25372     },
25373      /**
25374      * Object collection of toolbar tooltips for the buttons in the editor. The key
25375      * is the command id associated with that button and the value is a valid QuickTips object.
25376      * For example:
25377 <pre><code>
25378 {
25379     bold : {
25380         title: 'Bold (Ctrl+B)',
25381         text: 'Make the selected text bold.',
25382         cls: 'x-html-editor-tip'
25383     },
25384     italic : {
25385         title: 'Italic (Ctrl+I)',
25386         text: 'Make the selected text italic.',
25387         cls: 'x-html-editor-tip'
25388     },
25389     ...
25390 </code></pre>
25391     * @type Object
25392      */
25393     buttonTips : {
25394         bold : {
25395             title: 'Bold (Ctrl+B)',
25396             text: 'Make the selected text bold.',
25397             cls: 'x-html-editor-tip'
25398         },
25399         italic : {
25400             title: 'Italic (Ctrl+I)',
25401             text: 'Make the selected text italic.',
25402             cls: 'x-html-editor-tip'
25403         },
25404         underline : {
25405             title: 'Underline (Ctrl+U)',
25406             text: 'Underline the selected text.',
25407             cls: 'x-html-editor-tip'
25408         },
25409         increasefontsize : {
25410             title: 'Grow Text',
25411             text: 'Increase the font size.',
25412             cls: 'x-html-editor-tip'
25413         },
25414         decreasefontsize : {
25415             title: 'Shrink Text',
25416             text: 'Decrease the font size.',
25417             cls: 'x-html-editor-tip'
25418         },
25419         backcolor : {
25420             title: 'Text Highlight Color',
25421             text: 'Change the background color of the selected text.',
25422             cls: 'x-html-editor-tip'
25423         },
25424         forecolor : {
25425             title: 'Font Color',
25426             text: 'Change the color of the selected text.',
25427             cls: 'x-html-editor-tip'
25428         },
25429         justifyleft : {
25430             title: 'Align Text Left',
25431             text: 'Align text to the left.',
25432             cls: 'x-html-editor-tip'
25433         },
25434         justifycenter : {
25435             title: 'Center Text',
25436             text: 'Center text in the editor.',
25437             cls: 'x-html-editor-tip'
25438         },
25439         justifyright : {
25440             title: 'Align Text Right',
25441             text: 'Align text to the right.',
25442             cls: 'x-html-editor-tip'
25443         },
25444         insertunorderedlist : {
25445             title: 'Bullet List',
25446             text: 'Start a bulleted list.',
25447             cls: 'x-html-editor-tip'
25448         },
25449         insertorderedlist : {
25450             title: 'Numbered List',
25451             text: 'Start a numbered list.',
25452             cls: 'x-html-editor-tip'
25453         },
25454         createlink : {
25455             title: 'Hyperlink',
25456             text: 'Make the selected text a hyperlink.',
25457             cls: 'x-html-editor-tip'
25458         },
25459         sourceedit : {
25460             title: 'Source Edit',
25461             text: 'Switch to source editing mode.',
25462             cls: 'x-html-editor-tip'
25463         }
25464     },
25465     // private
25466     onDestroy : function(){
25467         if(this.rendered){
25468             
25469             this.tb.items.each(function(item){
25470                 if(item.menu){
25471                     item.menu.removeAll();
25472                     if(item.menu.el){
25473                         item.menu.el.destroy();
25474                     }
25475                 }
25476                 item.destroy();
25477             });
25478              
25479         }
25480     },
25481     onFirstFocus: function() {
25482         this.tb.items.each(function(item){
25483            item.enable();
25484         });
25485     }
25486 });
25487
25488
25489
25490
25491 // <script type="text/javascript">
25492 /*
25493  * Based on
25494  * Ext JS Library 1.1.1
25495  * Copyright(c) 2006-2007, Ext JS, LLC.
25496  *  
25497  
25498  */
25499
25500  
25501 /**
25502  * @class Roo.form.HtmlEditor.ToolbarContext
25503  * Context Toolbar
25504  * 
25505  * Usage:
25506  *
25507  new Roo.form.HtmlEditor({
25508     ....
25509     toolbars : [
25510         new Roo.form.HtmlEditor.ToolbarStandard(),
25511         new Roo.form.HtmlEditor.ToolbarContext()
25512         })
25513     }
25514      
25515  * 
25516  * @config : {Object} disable List of elements to disable.. (not done yet.)
25517  * 
25518  * 
25519  */
25520
25521 Roo.form.HtmlEditor.ToolbarContext = function(config)
25522 {
25523     
25524     Roo.apply(this, config);
25525     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25526     // dont call parent... till later.
25527 }
25528 Roo.form.HtmlEditor.ToolbarContext.types = {
25529     'IMG' : {
25530         width : {
25531             title: "Width",
25532             width: 40
25533         },
25534         height:  {
25535             title: "Height",
25536             width: 40
25537         },
25538         align: {
25539             title: "Align",
25540             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25541             width : 80
25542             
25543         },
25544         border: {
25545             title: "Border",
25546             width: 40
25547         },
25548         alt: {
25549             title: "Alt",
25550             width: 120
25551         },
25552         src : {
25553             title: "Src",
25554             width: 220
25555         }
25556         
25557     },
25558     'A' : {
25559         name : {
25560             title: "Name",
25561             width: 50
25562         },
25563         href:  {
25564             title: "Href",
25565             width: 220
25566         } // border?
25567         
25568     },
25569     'TABLE' : {
25570         rows : {
25571             title: "Rows",
25572             width: 20
25573         },
25574         cols : {
25575             title: "Cols",
25576             width: 20
25577         },
25578         width : {
25579             title: "Width",
25580             width: 40
25581         },
25582         height : {
25583             title: "Height",
25584             width: 40
25585         },
25586         border : {
25587             title: "Border",
25588             width: 20
25589         }
25590     },
25591     'TD' : {
25592         width : {
25593             title: "Width",
25594             width: 40
25595         },
25596         height : {
25597             title: "Height",
25598             width: 40
25599         },   
25600         align: {
25601             title: "Align",
25602             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25603             width: 40
25604         },
25605         valign: {
25606             title: "Valign",
25607             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25608             width: 40
25609         },
25610         colspan: {
25611             title: "Colspan",
25612             width: 20
25613             
25614         }
25615     },
25616     'INPUT' : {
25617         name : {
25618             title: "name",
25619             width: 120
25620         },
25621         value : {
25622             title: "Value",
25623             width: 120
25624         },
25625         width : {
25626             title: "Width",
25627             width: 40
25628         }
25629     },
25630     'LABEL' : {
25631         'for' : {
25632             title: "For",
25633             width: 120
25634         }
25635     },
25636     'TEXTAREA' : {
25637           name : {
25638             title: "name",
25639             width: 120
25640         },
25641         rows : {
25642             title: "Rows",
25643             width: 20
25644         },
25645         cols : {
25646             title: "Cols",
25647             width: 20
25648         }
25649     },
25650     'SELECT' : {
25651         name : {
25652             title: "name",
25653             width: 120
25654         },
25655         selectoptions : {
25656             title: "Options",
25657             width: 200
25658         }
25659     },
25660     'BODY' : {
25661         title : {
25662             title: "title",
25663             width: 120,
25664             disabled : true
25665         }
25666     }
25667 };
25668
25669
25670
25671 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25672     
25673     tb: false,
25674     
25675     rendered: false,
25676     
25677     editor : false,
25678     /**
25679      * @cfg {Object} disable  List of toolbar elements to disable
25680          
25681      */
25682     disable : false,
25683     
25684     
25685     
25686     toolbars : false,
25687     
25688     init : function(editor)
25689     {
25690         this.editor = editor;
25691         
25692         
25693         var fid = editor.frameId;
25694         var etb = this;
25695         function btn(id, toggle, handler){
25696             var xid = fid + '-'+ id ;
25697             return {
25698                 id : xid,
25699                 cmd : id,
25700                 cls : 'x-btn-icon x-edit-'+id,
25701                 enableToggle:toggle !== false,
25702                 scope: editor, // was editor...
25703                 handler:handler||editor.relayBtnCmd,
25704                 clickEvent:'mousedown',
25705                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25706                 tabIndex:-1
25707             };
25708         }
25709         // create a new element.
25710         var wdiv = editor.wrap.createChild({
25711                 tag: 'div'
25712             }, editor.wrap.dom.firstChild.nextSibling, true);
25713         
25714         // can we do this more than once??
25715         
25716          // stop form submits
25717       
25718  
25719         // disable everything...
25720         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25721         this.toolbars = {};
25722            
25723         for (var i in  ty) {
25724           
25725             this.toolbars[i] = this.buildToolbar(ty[i],i);
25726         }
25727         this.tb = this.toolbars.BODY;
25728         this.tb.el.show();
25729         
25730          
25731         this.rendered = true;
25732         
25733         // the all the btns;
25734         editor.on('editorevent', this.updateToolbar, this);
25735         // other toolbars need to implement this..
25736         //editor.on('editmodechange', this.updateToolbar, this);
25737     },
25738     
25739     
25740     
25741     /**
25742      * Protected method that will not generally be called directly. It triggers
25743      * a toolbar update by reading the markup state of the current selection in the editor.
25744      */
25745     updateToolbar: function(){
25746
25747         if(!this.editor.activated){
25748             this.editor.onFirstFocus();
25749             return;
25750         }
25751
25752         
25753         var ans = this.editor.getAllAncestors();
25754         
25755         // pick
25756         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25757         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25758         sel = sel ? sel : this.editor.doc.body;
25759         sel = sel.tagName.length ? sel : this.editor.doc.body;
25760         var tn = sel.tagName.toUpperCase();
25761         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25762         tn = sel.tagName.toUpperCase();
25763         if (this.tb.name  == tn) {
25764             return; // no change
25765         }
25766         this.tb.el.hide();
25767         ///console.log("show: " + tn);
25768         this.tb =  this.toolbars[tn];
25769         this.tb.el.show();
25770         this.tb.fields.each(function(e) {
25771             e.setValue(sel.getAttribute(e.name));
25772         });
25773         this.tb.selectedNode = sel;
25774         
25775         
25776         Roo.menu.MenuMgr.hideAll();
25777
25778         //this.editorsyncValue();
25779     },
25780    
25781        
25782     // private
25783     onDestroy : function(){
25784         if(this.rendered){
25785             
25786             this.tb.items.each(function(item){
25787                 if(item.menu){
25788                     item.menu.removeAll();
25789                     if(item.menu.el){
25790                         item.menu.el.destroy();
25791                     }
25792                 }
25793                 item.destroy();
25794             });
25795              
25796         }
25797     },
25798     onFirstFocus: function() {
25799         // need to do this for all the toolbars..
25800         this.tb.items.each(function(item){
25801            item.enable();
25802         });
25803     },
25804     buildToolbar: function(tlist, nm)
25805     {
25806         var editor = this.editor;
25807          // create a new element.
25808         var wdiv = editor.wrap.createChild({
25809                 tag: 'div'
25810             }, editor.wrap.dom.firstChild.nextSibling, true);
25811         
25812        
25813         var tb = new Roo.Toolbar(wdiv);
25814         tb.add(nm+ ":&nbsp;");
25815         for (var i in tlist) {
25816             var item = tlist[i];
25817             tb.add(item.title + ":&nbsp;");
25818             if (item.opts) {
25819                 // fixme
25820                 
25821               
25822                 tb.addField( new Roo.form.ComboBox({
25823                     store: new Roo.data.SimpleStore({
25824                         id : 'val',
25825                         fields: ['val'],
25826                         data : item.opts // from states.js
25827                     }),
25828                     name : i,
25829                     displayField:'val',
25830                     typeAhead: false,
25831                     mode: 'local',
25832                     editable : false,
25833                     triggerAction: 'all',
25834                     emptyText:'Select',
25835                     selectOnFocus:true,
25836                     width: item.width ? item.width  : 130,
25837                     listeners : {
25838                         'select': function(c, r, i) {
25839                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25840                         }
25841                     }
25842
25843                 }));
25844                 continue;
25845                     
25846                 
25847                 
25848                 
25849                 
25850                 tb.addField( new Roo.form.TextField({
25851                     name: i,
25852                     width: 100,
25853                     //allowBlank:false,
25854                     value: ''
25855                 }));
25856                 continue;
25857             }
25858             tb.addField( new Roo.form.TextField({
25859                 name: i,
25860                 width: item.width,
25861                 //allowBlank:true,
25862                 value: '',
25863                 listeners: {
25864                     'change' : function(f, nv, ov) {
25865                         tb.selectedNode.setAttribute(f.name, nv);
25866                     }
25867                 }
25868             }));
25869              
25870         }
25871         tb.el.on('click', function(e){
25872             e.preventDefault(); // what does this do?
25873         });
25874         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25875         tb.el.hide();
25876         tb.name = nm;
25877         // dont need to disable them... as they will get hidden
25878         return tb;
25879          
25880         
25881     }
25882     
25883     
25884     
25885     
25886 });
25887
25888
25889
25890
25891
25892 /*
25893  * Based on:
25894  * Ext JS Library 1.1.1
25895  * Copyright(c) 2006-2007, Ext JS, LLC.
25896  *
25897  * Originally Released Under LGPL - original licence link has changed is not relivant.
25898  *
25899  * Fork - LGPL
25900  * <script type="text/javascript">
25901  */
25902  
25903 /**
25904  * @class Roo.form.BasicForm
25905  * @extends Roo.util.Observable
25906  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25907  * @constructor
25908  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25909  * @param {Object} config Configuration options
25910  */
25911 Roo.form.BasicForm = function(el, config){
25912     this.allItems = [];
25913     this.childForms = [];
25914     Roo.apply(this, config);
25915     /*
25916      * The Roo.form.Field items in this form.
25917      * @type MixedCollection
25918      */
25919      
25920      
25921     this.items = new Roo.util.MixedCollection(false, function(o){
25922         return o.id || (o.id = Roo.id());
25923     });
25924     this.addEvents({
25925         /**
25926          * @event beforeaction
25927          * Fires before any action is performed. Return false to cancel the action.
25928          * @param {Form} this
25929          * @param {Action} action The action to be performed
25930          */
25931         beforeaction: true,
25932         /**
25933          * @event actionfailed
25934          * Fires when an action fails.
25935          * @param {Form} this
25936          * @param {Action} action The action that failed
25937          */
25938         actionfailed : true,
25939         /**
25940          * @event actioncomplete
25941          * Fires when an action is completed.
25942          * @param {Form} this
25943          * @param {Action} action The action that completed
25944          */
25945         actioncomplete : true
25946     });
25947     if(el){
25948         this.initEl(el);
25949     }
25950     Roo.form.BasicForm.superclass.constructor.call(this);
25951 };
25952
25953 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25954     /**
25955      * @cfg {String} method
25956      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25957      */
25958     /**
25959      * @cfg {DataReader} reader
25960      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25961      * This is optional as there is built-in support for processing JSON.
25962      */
25963     /**
25964      * @cfg {DataReader} errorReader
25965      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25966      * This is completely optional as there is built-in support for processing JSON.
25967      */
25968     /**
25969      * @cfg {String} url
25970      * The URL to use for form actions if one isn't supplied in the action options.
25971      */
25972     /**
25973      * @cfg {Boolean} fileUpload
25974      * Set to true if this form is a file upload.
25975      */
25976     /**
25977      * @cfg {Object} baseParams
25978      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25979      */
25980     /**
25981      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25982      */
25983     timeout: 30,
25984
25985     // private
25986     activeAction : null,
25987
25988     /**
25989      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25990      * or setValues() data instead of when the form was first created.
25991      */
25992     trackResetOnLoad : false,
25993     
25994     
25995     /**
25996      * childForms - used for multi-tab forms
25997      * @type {Array}
25998      */
25999     childForms : false,
26000     
26001     /**
26002      * allItems - full list of fields.
26003      * @type {Array}
26004      */
26005     allItems : false,
26006     
26007     /**
26008      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26009      * element by passing it or its id or mask the form itself by passing in true.
26010      * @type Mixed
26011      */
26012     waitMsgTarget : undefined,
26013
26014     // private
26015     initEl : function(el){
26016         this.el = Roo.get(el);
26017         this.id = this.el.id || Roo.id();
26018         this.el.on('submit', this.onSubmit, this);
26019         this.el.addClass('x-form');
26020     },
26021
26022     // private
26023     onSubmit : function(e){
26024         e.stopEvent();
26025     },
26026
26027     /**
26028      * Returns true if client-side validation on the form is successful.
26029      * @return Boolean
26030      */
26031     isValid : function(){
26032         var valid = true;
26033         this.items.each(function(f){
26034            if(!f.validate()){
26035                valid = false;
26036            }
26037         });
26038         return valid;
26039     },
26040
26041     /**
26042      * Returns true if any fields in this form have changed since their original load.
26043      * @return Boolean
26044      */
26045     isDirty : function(){
26046         var dirty = false;
26047         this.items.each(function(f){
26048            if(f.isDirty()){
26049                dirty = true;
26050                return false;
26051            }
26052         });
26053         return dirty;
26054     },
26055
26056     /**
26057      * Performs a predefined action (submit or load) or custom actions you define on this form.
26058      * @param {String} actionName The name of the action type
26059      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26060      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26061      * accept other config options):
26062      * <pre>
26063 Property          Type             Description
26064 ----------------  ---------------  ----------------------------------------------------------------------------------
26065 url               String           The url for the action (defaults to the form's url)
26066 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26067 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26068 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26069                                    validate the form on the client (defaults to false)
26070      * </pre>
26071      * @return {BasicForm} this
26072      */
26073     doAction : function(action, options){
26074         if(typeof action == 'string'){
26075             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26076         }
26077         if(this.fireEvent('beforeaction', this, action) !== false){
26078             this.beforeAction(action);
26079             action.run.defer(100, action);
26080         }
26081         return this;
26082     },
26083
26084     /**
26085      * Shortcut to do a submit action.
26086      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26087      * @return {BasicForm} this
26088      */
26089     submit : function(options){
26090         this.doAction('submit', options);
26091         return this;
26092     },
26093
26094     /**
26095      * Shortcut to do a load action.
26096      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26097      * @return {BasicForm} this
26098      */
26099     load : function(options){
26100         this.doAction('load', options);
26101         return this;
26102     },
26103
26104     /**
26105      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26106      * @param {Record} record The record to edit
26107      * @return {BasicForm} this
26108      */
26109     updateRecord : function(record){
26110         record.beginEdit();
26111         var fs = record.fields;
26112         fs.each(function(f){
26113             var field = this.findField(f.name);
26114             if(field){
26115                 record.set(f.name, field.getValue());
26116             }
26117         }, this);
26118         record.endEdit();
26119         return this;
26120     },
26121
26122     /**
26123      * Loads an Roo.data.Record into this form.
26124      * @param {Record} record The record to load
26125      * @return {BasicForm} this
26126      */
26127     loadRecord : function(record){
26128         this.setValues(record.data);
26129         return this;
26130     },
26131
26132     // private
26133     beforeAction : function(action){
26134         var o = action.options;
26135         if(o.waitMsg){
26136             if(this.waitMsgTarget === true){
26137                 this.el.mask(o.waitMsg, 'x-mask-loading');
26138             }else if(this.waitMsgTarget){
26139                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26140                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26141             }else{
26142                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26143             }
26144         }
26145     },
26146
26147     // private
26148     afterAction : function(action, success){
26149         this.activeAction = null;
26150         var o = action.options;
26151         if(o.waitMsg){
26152             if(this.waitMsgTarget === true){
26153                 this.el.unmask();
26154             }else if(this.waitMsgTarget){
26155                 this.waitMsgTarget.unmask();
26156             }else{
26157                 Roo.MessageBox.updateProgress(1);
26158                 Roo.MessageBox.hide();
26159             }
26160         }
26161         if(success){
26162             if(o.reset){
26163                 this.reset();
26164             }
26165             Roo.callback(o.success, o.scope, [this, action]);
26166             this.fireEvent('actioncomplete', this, action);
26167         }else{
26168             Roo.callback(o.failure, o.scope, [this, action]);
26169             this.fireEvent('actionfailed', this, action);
26170         }
26171     },
26172
26173     /**
26174      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26175      * @param {String} id The value to search for
26176      * @return Field
26177      */
26178     findField : function(id){
26179         var field = this.items.get(id);
26180         if(!field){
26181             this.items.each(function(f){
26182                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26183                     field = f;
26184                     return false;
26185                 }
26186             });
26187         }
26188         return field || null;
26189     },
26190
26191     /**
26192      * Add a secondary form to this one, 
26193      * Used to provide tabbed forms. One form is primary, with hidden values 
26194      * which mirror the elements from the other forms.
26195      * 
26196      * @param {Roo.form.Form} form to add.
26197      * 
26198      */
26199     addForm : function(form)
26200     {
26201        
26202         if (this.childForms.indexOf(form) > -1) {
26203             // already added..
26204             return;
26205         }
26206         this.childForms.push(form);
26207         var n = '';
26208         Roo.each(form.allItems, function (fe) {
26209             
26210             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26211             if (this.findField(n)) { // already added..
26212                 return;
26213             }
26214             var add = new Roo.form.Hidden({
26215                 name : n
26216             });
26217             add.render(this.el);
26218             
26219             this.add( add );
26220         }, this);
26221         
26222     },
26223     /**
26224      * Mark fields in this form invalid in bulk.
26225      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26226      * @return {BasicForm} this
26227      */
26228     markInvalid : function(errors){
26229         if(errors instanceof Array){
26230             for(var i = 0, len = errors.length; i < len; i++){
26231                 var fieldError = errors[i];
26232                 var f = this.findField(fieldError.id);
26233                 if(f){
26234                     f.markInvalid(fieldError.msg);
26235                 }
26236             }
26237         }else{
26238             var field, id;
26239             for(id in errors){
26240                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26241                     field.markInvalid(errors[id]);
26242                 }
26243             }
26244         }
26245         Roo.each(this.childForms || [], function (f) {
26246             f.markInvalid(errors);
26247         });
26248         
26249         return this;
26250     },
26251
26252     /**
26253      * Set values for fields in this form in bulk.
26254      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26255      * @return {BasicForm} this
26256      */
26257     setValues : function(values){
26258         if(values instanceof Array){ // array of objects
26259             for(var i = 0, len = values.length; i < len; i++){
26260                 var v = values[i];
26261                 var f = this.findField(v.id);
26262                 if(f){
26263                     f.setValue(v.value);
26264                     if(this.trackResetOnLoad){
26265                         f.originalValue = f.getValue();
26266                     }
26267                 }
26268             }
26269         }else{ // object hash
26270             var field, id;
26271             for(id in values){
26272                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26273                     
26274                     if (field.setFromData && 
26275                         field.valueField && 
26276                         field.displayField &&
26277                         // combos' with local stores can 
26278                         // be queried via setValue()
26279                         // to set their value..
26280                         (field.store && !field.store.isLocal)
26281                         ) {
26282                         // it's a combo
26283                         var sd = { };
26284                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26285                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26286                         field.setFromData(sd);
26287                         
26288                     } else {
26289                         field.setValue(values[id]);
26290                     }
26291                     
26292                     
26293                     if(this.trackResetOnLoad){
26294                         field.originalValue = field.getValue();
26295                     }
26296                 }
26297             }
26298         }
26299          
26300         Roo.each(this.childForms || [], function (f) {
26301             f.setValues(values);
26302         });
26303                 
26304         return this;
26305     },
26306
26307     /**
26308      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26309      * they are returned as an array.
26310      * @param {Boolean} asString
26311      * @return {Object}
26312      */
26313     getValues : function(asString){
26314         if (this.childForms) {
26315             // copy values from the child forms
26316             Roo.each(this.childForms, function (f) {
26317                 this.setValues(f.getValues());
26318             }, this);
26319         }
26320         
26321         
26322         
26323         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26324         if(asString === true){
26325             return fs;
26326         }
26327         return Roo.urlDecode(fs);
26328     },
26329     
26330     /**
26331      * Returns the fields in this form as an object with key/value pairs. 
26332      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26333      * @return {Object}
26334      */
26335     getFieldValues : function()
26336     {
26337         if (this.childForms) {
26338             // copy values from the child forms
26339             Roo.each(this.childForms, function (f) {
26340                 this.setValues(f.getValues());
26341             }, this);
26342         }
26343         
26344         var ret = {};
26345         this.items.each(function(f){
26346             if (!f.getName()) {
26347                 return;
26348             }
26349             var v = f.getValue();
26350             if ((typeof(v) == 'object') && f.getRawValue) {
26351                 v = f.getRawValue() ; // dates..
26352             }
26353             ret[f.getName()] = v;
26354         });
26355         
26356         return ret;
26357     },
26358
26359     /**
26360      * Clears all invalid messages in this form.
26361      * @return {BasicForm} this
26362      */
26363     clearInvalid : function(){
26364         this.items.each(function(f){
26365            f.clearInvalid();
26366         });
26367         
26368         Roo.each(this.childForms || [], function (f) {
26369             f.clearInvalid();
26370         });
26371         
26372         
26373         return this;
26374     },
26375
26376     /**
26377      * Resets this form.
26378      * @return {BasicForm} this
26379      */
26380     reset : function(){
26381         this.items.each(function(f){
26382             f.reset();
26383         });
26384         
26385         Roo.each(this.childForms || [], function (f) {
26386             f.reset();
26387         });
26388        
26389         
26390         return this;
26391     },
26392
26393     /**
26394      * Add Roo.form components to this form.
26395      * @param {Field} field1
26396      * @param {Field} field2 (optional)
26397      * @param {Field} etc (optional)
26398      * @return {BasicForm} this
26399      */
26400     add : function(){
26401         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26402         return this;
26403     },
26404
26405
26406     /**
26407      * Removes a field from the items collection (does NOT remove its markup).
26408      * @param {Field} field
26409      * @return {BasicForm} this
26410      */
26411     remove : function(field){
26412         this.items.remove(field);
26413         return this;
26414     },
26415
26416     /**
26417      * Looks at the fields in this form, checks them for an id attribute,
26418      * and calls applyTo on the existing dom element with that id.
26419      * @return {BasicForm} this
26420      */
26421     render : function(){
26422         this.items.each(function(f){
26423             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26424                 f.applyTo(f.id);
26425             }
26426         });
26427         return this;
26428     },
26429
26430     /**
26431      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26432      * @param {Object} values
26433      * @return {BasicForm} this
26434      */
26435     applyToFields : function(o){
26436         this.items.each(function(f){
26437            Roo.apply(f, o);
26438         });
26439         return this;
26440     },
26441
26442     /**
26443      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26444      * @param {Object} values
26445      * @return {BasicForm} this
26446      */
26447     applyIfToFields : function(o){
26448         this.items.each(function(f){
26449            Roo.applyIf(f, o);
26450         });
26451         return this;
26452     }
26453 });
26454
26455 // back compat
26456 Roo.BasicForm = Roo.form.BasicForm;/*
26457  * Based on:
26458  * Ext JS Library 1.1.1
26459  * Copyright(c) 2006-2007, Ext JS, LLC.
26460  *
26461  * Originally Released Under LGPL - original licence link has changed is not relivant.
26462  *
26463  * Fork - LGPL
26464  * <script type="text/javascript">
26465  */
26466
26467 /**
26468  * @class Roo.form.Form
26469  * @extends Roo.form.BasicForm
26470  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26471  * @constructor
26472  * @param {Object} config Configuration options
26473  */
26474 Roo.form.Form = function(config){
26475     var xitems =  [];
26476     if (config.items) {
26477         xitems = config.items;
26478         delete config.items;
26479     }
26480    
26481     
26482     Roo.form.Form.superclass.constructor.call(this, null, config);
26483     this.url = this.url || this.action;
26484     if(!this.root){
26485         this.root = new Roo.form.Layout(Roo.applyIf({
26486             id: Roo.id()
26487         }, config));
26488     }
26489     this.active = this.root;
26490     /**
26491      * Array of all the buttons that have been added to this form via {@link addButton}
26492      * @type Array
26493      */
26494     this.buttons = [];
26495     this.allItems = [];
26496     this.addEvents({
26497         /**
26498          * @event clientvalidation
26499          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26500          * @param {Form} this
26501          * @param {Boolean} valid true if the form has passed client-side validation
26502          */
26503         clientvalidation: true,
26504         /**
26505          * @event rendered
26506          * Fires when the form is rendered
26507          * @param {Roo.form.Form} form
26508          */
26509         rendered : true
26510     });
26511     
26512     if (this.progressUrl) {
26513             // push a hidden field onto the list of fields..
26514             this.addxtype( {
26515                     xns: Roo.form, 
26516                     xtype : 'Hidden', 
26517                     name : 'UPLOAD_IDENTIFIER' 
26518             });
26519         }
26520         
26521     
26522     Roo.each(xitems, this.addxtype, this);
26523     
26524     
26525     
26526 };
26527
26528 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26529     /**
26530      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26531      */
26532     /**
26533      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26534      */
26535     /**
26536      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26537      */
26538     buttonAlign:'center',
26539
26540     /**
26541      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26542      */
26543     minButtonWidth:75,
26544
26545     /**
26546      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26547      * This property cascades to child containers if not set.
26548      */
26549     labelAlign:'left',
26550
26551     /**
26552      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26553      * fires a looping event with that state. This is required to bind buttons to the valid
26554      * state using the config value formBind:true on the button.
26555      */
26556     monitorValid : false,
26557
26558     /**
26559      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26560      */
26561     monitorPoll : 200,
26562     
26563     /**
26564      * @cfg {String} progressUrl - Url to return progress data 
26565      */
26566     
26567     progressUrl : false,
26568   
26569     /**
26570      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26571      * fields are added and the column is closed. If no fields are passed the column remains open
26572      * until end() is called.
26573      * @param {Object} config The config to pass to the column
26574      * @param {Field} field1 (optional)
26575      * @param {Field} field2 (optional)
26576      * @param {Field} etc (optional)
26577      * @return Column The column container object
26578      */
26579     column : function(c){
26580         var col = new Roo.form.Column(c);
26581         this.start(col);
26582         if(arguments.length > 1){ // duplicate code required because of Opera
26583             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26584             this.end();
26585         }
26586         return col;
26587     },
26588
26589     /**
26590      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26591      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26592      * until end() is called.
26593      * @param {Object} config The config to pass to the fieldset
26594      * @param {Field} field1 (optional)
26595      * @param {Field} field2 (optional)
26596      * @param {Field} etc (optional)
26597      * @return FieldSet The fieldset container object
26598      */
26599     fieldset : function(c){
26600         var fs = new Roo.form.FieldSet(c);
26601         this.start(fs);
26602         if(arguments.length > 1){ // duplicate code required because of Opera
26603             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26604             this.end();
26605         }
26606         return fs;
26607     },
26608
26609     /**
26610      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26611      * fields are added and the container is closed. If no fields are passed the container remains open
26612      * until end() is called.
26613      * @param {Object} config The config to pass to the Layout
26614      * @param {Field} field1 (optional)
26615      * @param {Field} field2 (optional)
26616      * @param {Field} etc (optional)
26617      * @return Layout The container object
26618      */
26619     container : function(c){
26620         var l = new Roo.form.Layout(c);
26621         this.start(l);
26622         if(arguments.length > 1){ // duplicate code required because of Opera
26623             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26624             this.end();
26625         }
26626         return l;
26627     },
26628
26629     /**
26630      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26631      * @param {Object} container A Roo.form.Layout or subclass of Layout
26632      * @return {Form} this
26633      */
26634     start : function(c){
26635         // cascade label info
26636         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26637         this.active.stack.push(c);
26638         c.ownerCt = this.active;
26639         this.active = c;
26640         return this;
26641     },
26642
26643     /**
26644      * Closes the current open container
26645      * @return {Form} this
26646      */
26647     end : function(){
26648         if(this.active == this.root){
26649             return this;
26650         }
26651         this.active = this.active.ownerCt;
26652         return this;
26653     },
26654
26655     /**
26656      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26657      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26658      * as the label of the field.
26659      * @param {Field} field1
26660      * @param {Field} field2 (optional)
26661      * @param {Field} etc. (optional)
26662      * @return {Form} this
26663      */
26664     add : function(){
26665         this.active.stack.push.apply(this.active.stack, arguments);
26666         this.allItems.push.apply(this.allItems,arguments);
26667         var r = [];
26668         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26669             if(a[i].isFormField){
26670                 r.push(a[i]);
26671             }
26672         }
26673         if(r.length > 0){
26674             Roo.form.Form.superclass.add.apply(this, r);
26675         }
26676         return this;
26677     },
26678     
26679
26680     
26681     
26682     
26683      /**
26684      * Find any element that has been added to a form, using it's ID or name
26685      * This can include framesets, columns etc. along with regular fields..
26686      * @param {String} id - id or name to find.
26687      
26688      * @return {Element} e - or false if nothing found.
26689      */
26690     findbyId : function(id)
26691     {
26692         var ret = false;
26693         if (!id) {
26694             return ret;
26695         }
26696         Ext.each(this.allItems, function(f){
26697             if (f.id == id || f.name == id ){
26698                 ret = f;
26699                 return false;
26700             }
26701         });
26702         return ret;
26703     },
26704
26705     
26706     
26707     /**
26708      * Render this form into the passed container. This should only be called once!
26709      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26710      * @return {Form} this
26711      */
26712     render : function(ct)
26713     {
26714         
26715         
26716         
26717         ct = Roo.get(ct);
26718         var o = this.autoCreate || {
26719             tag: 'form',
26720             method : this.method || 'POST',
26721             id : this.id || Roo.id()
26722         };
26723         this.initEl(ct.createChild(o));
26724
26725         this.root.render(this.el);
26726         
26727        
26728              
26729         this.items.each(function(f){
26730             f.render('x-form-el-'+f.id);
26731         });
26732
26733         if(this.buttons.length > 0){
26734             // tables are required to maintain order and for correct IE layout
26735             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26736                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26737                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26738             }}, null, true);
26739             var tr = tb.getElementsByTagName('tr')[0];
26740             for(var i = 0, len = this.buttons.length; i < len; i++) {
26741                 var b = this.buttons[i];
26742                 var td = document.createElement('td');
26743                 td.className = 'x-form-btn-td';
26744                 b.render(tr.appendChild(td));
26745             }
26746         }
26747         if(this.monitorValid){ // initialize after render
26748             this.startMonitoring();
26749         }
26750         this.fireEvent('rendered', this);
26751         return this;
26752     },
26753
26754     /**
26755      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26756      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26757      * object or a valid Roo.DomHelper element config
26758      * @param {Function} handler The function called when the button is clicked
26759      * @param {Object} scope (optional) The scope of the handler function
26760      * @return {Roo.Button}
26761      */
26762     addButton : function(config, handler, scope){
26763         var bc = {
26764             handler: handler,
26765             scope: scope,
26766             minWidth: this.minButtonWidth,
26767             hideParent:true
26768         };
26769         if(typeof config == "string"){
26770             bc.text = config;
26771         }else{
26772             Roo.apply(bc, config);
26773         }
26774         var btn = new Roo.Button(null, bc);
26775         this.buttons.push(btn);
26776         return btn;
26777     },
26778
26779      /**
26780      * Adds a series of form elements (using the xtype property as the factory method.
26781      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26782      * @param {Object} config 
26783      */
26784     
26785     addxtype : function()
26786     {
26787         var ar = Array.prototype.slice.call(arguments, 0);
26788         var ret = false;
26789         for(var i = 0; i < ar.length; i++) {
26790             if (!ar[i]) {
26791                 continue; // skip -- if this happends something invalid got sent, we 
26792                 // should ignore it, as basically that interface element will not show up
26793                 // and that should be pretty obvious!!
26794             }
26795             
26796             if (Roo.form[ar[i].xtype]) {
26797                 ar[i].form = this;
26798                 var fe = Roo.factory(ar[i], Roo.form);
26799                 if (!ret) {
26800                     ret = fe;
26801                 }
26802                 fe.form = this;
26803                 if (fe.store) {
26804                     fe.store.form = this;
26805                 }
26806                 if (fe.isLayout) {  
26807                          
26808                     this.start(fe);
26809                     this.allItems.push(fe);
26810                     if (fe.items && fe.addxtype) {
26811                         fe.addxtype.apply(fe, fe.items);
26812                         delete fe.items;
26813                     }
26814                      this.end();
26815                     continue;
26816                 }
26817                 
26818                 
26819                  
26820                 this.add(fe);
26821               //  console.log('adding ' + ar[i].xtype);
26822             }
26823             if (ar[i].xtype == 'Button') {  
26824                 //console.log('adding button');
26825                 //console.log(ar[i]);
26826                 this.addButton(ar[i]);
26827                 this.allItems.push(fe);
26828                 continue;
26829             }
26830             
26831             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26832                 alert('end is not supported on xtype any more, use items');
26833             //    this.end();
26834             //    //console.log('adding end');
26835             }
26836             
26837         }
26838         return ret;
26839     },
26840     
26841     /**
26842      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26843      * option "monitorValid"
26844      */
26845     startMonitoring : function(){
26846         if(!this.bound){
26847             this.bound = true;
26848             Roo.TaskMgr.start({
26849                 run : this.bindHandler,
26850                 interval : this.monitorPoll || 200,
26851                 scope: this
26852             });
26853         }
26854     },
26855
26856     /**
26857      * Stops monitoring of the valid state of this form
26858      */
26859     stopMonitoring : function(){
26860         this.bound = false;
26861     },
26862
26863     // private
26864     bindHandler : function(){
26865         if(!this.bound){
26866             return false; // stops binding
26867         }
26868         var valid = true;
26869         this.items.each(function(f){
26870             if(!f.isValid(true)){
26871                 valid = false;
26872                 return false;
26873             }
26874         });
26875         for(var i = 0, len = this.buttons.length; i < len; i++){
26876             var btn = this.buttons[i];
26877             if(btn.formBind === true && btn.disabled === valid){
26878                 btn.setDisabled(!valid);
26879             }
26880         }
26881         this.fireEvent('clientvalidation', this, valid);
26882     }
26883     
26884     
26885     
26886     
26887     
26888     
26889     
26890     
26891 });
26892
26893
26894 // back compat
26895 Roo.Form = Roo.form.Form;
26896 /*
26897  * Based on:
26898  * Ext JS Library 1.1.1
26899  * Copyright(c) 2006-2007, Ext JS, LLC.
26900  *
26901  * Originally Released Under LGPL - original licence link has changed is not relivant.
26902  *
26903  * Fork - LGPL
26904  * <script type="text/javascript">
26905  */
26906  
26907  /**
26908  * @class Roo.form.Action
26909  * Internal Class used to handle form actions
26910  * @constructor
26911  * @param {Roo.form.BasicForm} el The form element or its id
26912  * @param {Object} config Configuration options
26913  */
26914  
26915  
26916 // define the action interface
26917 Roo.form.Action = function(form, options){
26918     this.form = form;
26919     this.options = options || {};
26920 };
26921 /**
26922  * Client Validation Failed
26923  * @const 
26924  */
26925 Roo.form.Action.CLIENT_INVALID = 'client';
26926 /**
26927  * Server Validation Failed
26928  * @const 
26929  */
26930  Roo.form.Action.SERVER_INVALID = 'server';
26931  /**
26932  * Connect to Server Failed
26933  * @const 
26934  */
26935 Roo.form.Action.CONNECT_FAILURE = 'connect';
26936 /**
26937  * Reading Data from Server Failed
26938  * @const 
26939  */
26940 Roo.form.Action.LOAD_FAILURE = 'load';
26941
26942 Roo.form.Action.prototype = {
26943     type : 'default',
26944     failureType : undefined,
26945     response : undefined,
26946     result : undefined,
26947
26948     // interface method
26949     run : function(options){
26950
26951     },
26952
26953     // interface method
26954     success : function(response){
26955
26956     },
26957
26958     // interface method
26959     handleResponse : function(response){
26960
26961     },
26962
26963     // default connection failure
26964     failure : function(response){
26965         this.response = response;
26966         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26967         this.form.afterAction(this, false);
26968     },
26969
26970     processResponse : function(response){
26971         this.response = response;
26972         if(!response.responseText){
26973             return true;
26974         }
26975         this.result = this.handleResponse(response);
26976         return this.result;
26977     },
26978
26979     // utility functions used internally
26980     getUrl : function(appendParams){
26981         var url = this.options.url || this.form.url || this.form.el.dom.action;
26982         if(appendParams){
26983             var p = this.getParams();
26984             if(p){
26985                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26986             }
26987         }
26988         return url;
26989     },
26990
26991     getMethod : function(){
26992         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26993     },
26994
26995     getParams : function(){
26996         var bp = this.form.baseParams;
26997         var p = this.options.params;
26998         if(p){
26999             if(typeof p == "object"){
27000                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27001             }else if(typeof p == 'string' && bp){
27002                 p += '&' + Roo.urlEncode(bp);
27003             }
27004         }else if(bp){
27005             p = Roo.urlEncode(bp);
27006         }
27007         return p;
27008     },
27009
27010     createCallback : function(){
27011         return {
27012             success: this.success,
27013             failure: this.failure,
27014             scope: this,
27015             timeout: (this.form.timeout*1000),
27016             upload: this.form.fileUpload ? this.success : undefined
27017         };
27018     }
27019 };
27020
27021 Roo.form.Action.Submit = function(form, options){
27022     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27023 };
27024
27025 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27026     type : 'submit',
27027
27028     haveProgress : false,
27029     uploadComplete : false,
27030     
27031     // uploadProgress indicator.
27032     uploadProgress : function()
27033     {
27034         if (!this.form.progressUrl) {
27035             return;
27036         }
27037         
27038         if (!this.haveProgress) {
27039             Roo.MessageBox.progress("Uploading", "Uploading");
27040         }
27041         if (this.uploadComplete) {
27042            Roo.MessageBox.hide();
27043            return;
27044         }
27045         
27046         this.haveProgress = true;
27047    
27048         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27049         
27050         var c = new Roo.data.Connection();
27051         c.request({
27052             url : this.form.progressUrl,
27053             params: {
27054                 id : uid
27055             },
27056             method: 'GET',
27057             success : function(req){
27058                //console.log(data);
27059                 var rdata = false;
27060                 var edata;
27061                 try  {
27062                    rdata = Roo.decode(req.responseText)
27063                 } catch (e) {
27064                     Roo.log("Invalid data from server..");
27065                     Roo.log(edata);
27066                     return;
27067                 }
27068                 if (!rdata || !rdata.success) {
27069                     Roo.log(rdata);
27070                     return;
27071                 }
27072                 var data = rdata.data;
27073                 
27074                 if (this.uploadComplete) {
27075                    Roo.MessageBox.hide();
27076                    return;
27077                 }
27078                    
27079                 if (data){
27080                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27081                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27082                     );
27083                 }
27084                 this.uploadProgress.defer(2000,this);
27085             },
27086        
27087             failure: function(data) {
27088                 Roo.log('progress url failed ');
27089                 Roo.log(data);
27090             },
27091             scope : this
27092         });
27093            
27094     },
27095     
27096     
27097     run : function()
27098     {
27099         // run get Values on the form, so it syncs any secondary forms.
27100         this.form.getValues();
27101         
27102         var o = this.options;
27103         var method = this.getMethod();
27104         var isPost = method == 'POST';
27105         if(o.clientValidation === false || this.form.isValid()){
27106             
27107             if (this.form.progressUrl) {
27108                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27109                     (new Date() * 1) + '' + Math.random());
27110                     
27111             } 
27112             
27113             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27114                 form:this.form.el.dom,
27115                 url:this.getUrl(!isPost),
27116                 method: method,
27117                 params:isPost ? this.getParams() : null,
27118                 isUpload: this.form.fileUpload
27119             }));
27120             
27121             this.uploadProgress();
27122
27123         }else if (o.clientValidation !== false){ // client validation failed
27124             this.failureType = Roo.form.Action.CLIENT_INVALID;
27125             this.form.afterAction(this, false);
27126         }
27127     },
27128
27129     success : function(response)
27130     {
27131         this.uploadComplete= true;
27132         if (this.haveProgress) {
27133             Roo.MessageBox.hide();
27134         }
27135         
27136         var result = this.processResponse(response);
27137         if(result === true || result.success){
27138             this.form.afterAction(this, true);
27139             return;
27140         }
27141         if(result.errors){
27142             this.form.markInvalid(result.errors);
27143             this.failureType = Roo.form.Action.SERVER_INVALID;
27144         }
27145         this.form.afterAction(this, false);
27146     },
27147     failure : function(response)
27148     {
27149         this.uploadComplete= true;
27150         if (this.haveProgress) {
27151             Roo.MessageBox.hide();
27152         }
27153         
27154         this.response = response;
27155         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27156         this.form.afterAction(this, false);
27157     },
27158     
27159     handleResponse : function(response){
27160         if(this.form.errorReader){
27161             var rs = this.form.errorReader.read(response);
27162             var errors = [];
27163             if(rs.records){
27164                 for(var i = 0, len = rs.records.length; i < len; i++) {
27165                     var r = rs.records[i];
27166                     errors[i] = r.data;
27167                 }
27168             }
27169             if(errors.length < 1){
27170                 errors = null;
27171             }
27172             return {
27173                 success : rs.success,
27174                 errors : errors
27175             };
27176         }
27177         var ret = false;
27178         try {
27179             ret = Roo.decode(response.responseText);
27180         } catch (e) {
27181             ret = {
27182                 success: false,
27183                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27184                 errors : []
27185             };
27186         }
27187         return ret;
27188         
27189     }
27190 });
27191
27192
27193 Roo.form.Action.Load = function(form, options){
27194     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27195     this.reader = this.form.reader;
27196 };
27197
27198 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27199     type : 'load',
27200
27201     run : function(){
27202         Roo.Ajax.request(Roo.apply(
27203                 this.createCallback(), {
27204                     method:this.getMethod(),
27205                     url:this.getUrl(false),
27206                     params:this.getParams()
27207         }));
27208     },
27209
27210     success : function(response){
27211         var result = this.processResponse(response);
27212         if(result === true || !result.success || !result.data){
27213             this.failureType = Roo.form.Action.LOAD_FAILURE;
27214             this.form.afterAction(this, false);
27215             return;
27216         }
27217         this.form.clearInvalid();
27218         this.form.setValues(result.data);
27219         this.form.afterAction(this, true);
27220     },
27221
27222     handleResponse : function(response){
27223         if(this.form.reader){
27224             var rs = this.form.reader.read(response);
27225             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27226             return {
27227                 success : rs.success,
27228                 data : data
27229             };
27230         }
27231         return Roo.decode(response.responseText);
27232     }
27233 });
27234
27235 Roo.form.Action.ACTION_TYPES = {
27236     'load' : Roo.form.Action.Load,
27237     'submit' : Roo.form.Action.Submit
27238 };/*
27239  * Based on:
27240  * Ext JS Library 1.1.1
27241  * Copyright(c) 2006-2007, Ext JS, LLC.
27242  *
27243  * Originally Released Under LGPL - original licence link has changed is not relivant.
27244  *
27245  * Fork - LGPL
27246  * <script type="text/javascript">
27247  */
27248  
27249 /**
27250  * @class Roo.form.Layout
27251  * @extends Roo.Component
27252  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27253  * @constructor
27254  * @param {Object} config Configuration options
27255  */
27256 Roo.form.Layout = function(config){
27257     var xitems = [];
27258     if (config.items) {
27259         xitems = config.items;
27260         delete config.items;
27261     }
27262     Roo.form.Layout.superclass.constructor.call(this, config);
27263     this.stack = [];
27264     Roo.each(xitems, this.addxtype, this);
27265      
27266 };
27267
27268 Roo.extend(Roo.form.Layout, Roo.Component, {
27269     /**
27270      * @cfg {String/Object} autoCreate
27271      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27272      */
27273     /**
27274      * @cfg {String/Object/Function} style
27275      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27276      * a function which returns such a specification.
27277      */
27278     /**
27279      * @cfg {String} labelAlign
27280      * Valid values are "left," "top" and "right" (defaults to "left")
27281      */
27282     /**
27283      * @cfg {Number} labelWidth
27284      * Fixed width in pixels of all field labels (defaults to undefined)
27285      */
27286     /**
27287      * @cfg {Boolean} clear
27288      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27289      */
27290     clear : true,
27291     /**
27292      * @cfg {String} labelSeparator
27293      * The separator to use after field labels (defaults to ':')
27294      */
27295     labelSeparator : ':',
27296     /**
27297      * @cfg {Boolean} hideLabels
27298      * True to suppress the display of field labels in this layout (defaults to false)
27299      */
27300     hideLabels : false,
27301
27302     // private
27303     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27304     
27305     isLayout : true,
27306     
27307     // private
27308     onRender : function(ct, position){
27309         if(this.el){ // from markup
27310             this.el = Roo.get(this.el);
27311         }else {  // generate
27312             var cfg = this.getAutoCreate();
27313             this.el = ct.createChild(cfg, position);
27314         }
27315         if(this.style){
27316             this.el.applyStyles(this.style);
27317         }
27318         if(this.labelAlign){
27319             this.el.addClass('x-form-label-'+this.labelAlign);
27320         }
27321         if(this.hideLabels){
27322             this.labelStyle = "display:none";
27323             this.elementStyle = "padding-left:0;";
27324         }else{
27325             if(typeof this.labelWidth == 'number'){
27326                 this.labelStyle = "width:"+this.labelWidth+"px;";
27327                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27328             }
27329             if(this.labelAlign == 'top'){
27330                 this.labelStyle = "width:auto;";
27331                 this.elementStyle = "padding-left:0;";
27332             }
27333         }
27334         var stack = this.stack;
27335         var slen = stack.length;
27336         if(slen > 0){
27337             if(!this.fieldTpl){
27338                 var t = new Roo.Template(
27339                     '<div class="x-form-item {5}">',
27340                         '<label for="{0}" style="{2}">{1}{4}</label>',
27341                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27342                         '</div>',
27343                     '</div><div class="x-form-clear-left"></div>'
27344                 );
27345                 t.disableFormats = true;
27346                 t.compile();
27347                 Roo.form.Layout.prototype.fieldTpl = t;
27348             }
27349             for(var i = 0; i < slen; i++) {
27350                 if(stack[i].isFormField){
27351                     this.renderField(stack[i]);
27352                 }else{
27353                     this.renderComponent(stack[i]);
27354                 }
27355             }
27356         }
27357         if(this.clear){
27358             this.el.createChild({cls:'x-form-clear'});
27359         }
27360     },
27361
27362     // private
27363     renderField : function(f){
27364         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27365                f.id, //0
27366                f.fieldLabel, //1
27367                f.labelStyle||this.labelStyle||'', //2
27368                this.elementStyle||'', //3
27369                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27370                f.itemCls||this.itemCls||''  //5
27371        ], true).getPrevSibling());
27372     },
27373
27374     // private
27375     renderComponent : function(c){
27376         c.render(c.isLayout ? this.el : this.el.createChild());    
27377     },
27378     /**
27379      * Adds a object form elements (using the xtype property as the factory method.)
27380      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27381      * @param {Object} config 
27382      */
27383     addxtype : function(o)
27384     {
27385         // create the lement.
27386         o.form = this.form;
27387         var fe = Roo.factory(o, Roo.form);
27388         this.form.allItems.push(fe);
27389         this.stack.push(fe);
27390         
27391         if (fe.isFormField) {
27392             this.form.items.add(fe);
27393         }
27394          
27395         return fe;
27396     }
27397 });
27398
27399 /**
27400  * @class Roo.form.Column
27401  * @extends Roo.form.Layout
27402  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27403  * @constructor
27404  * @param {Object} config Configuration options
27405  */
27406 Roo.form.Column = function(config){
27407     Roo.form.Column.superclass.constructor.call(this, config);
27408 };
27409
27410 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27411     /**
27412      * @cfg {Number/String} width
27413      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27414      */
27415     /**
27416      * @cfg {String/Object} autoCreate
27417      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27418      */
27419
27420     // private
27421     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27422
27423     // private
27424     onRender : function(ct, position){
27425         Roo.form.Column.superclass.onRender.call(this, ct, position);
27426         if(this.width){
27427             this.el.setWidth(this.width);
27428         }
27429     }
27430 });
27431
27432
27433 /**
27434  * @class Roo.form.Row
27435  * @extends Roo.form.Layout
27436  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27437  * @constructor
27438  * @param {Object} config Configuration options
27439  */
27440
27441  
27442 Roo.form.Row = function(config){
27443     Roo.form.Row.superclass.constructor.call(this, config);
27444 };
27445  
27446 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27447       /**
27448      * @cfg {Number/String} width
27449      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27450      */
27451     /**
27452      * @cfg {Number/String} height
27453      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27454      */
27455     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27456     
27457     padWidth : 20,
27458     // private
27459     onRender : function(ct, position){
27460         //console.log('row render');
27461         if(!this.rowTpl){
27462             var t = new Roo.Template(
27463                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27464                     '<label for="{0}" style="{2}">{1}{4}</label>',
27465                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27466                     '</div>',
27467                 '</div>'
27468             );
27469             t.disableFormats = true;
27470             t.compile();
27471             Roo.form.Layout.prototype.rowTpl = t;
27472         }
27473         this.fieldTpl = this.rowTpl;
27474         
27475         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27476         var labelWidth = 100;
27477         
27478         if ((this.labelAlign != 'top')) {
27479             if (typeof this.labelWidth == 'number') {
27480                 labelWidth = this.labelWidth
27481             }
27482             this.padWidth =  20 + labelWidth;
27483             
27484         }
27485         
27486         Roo.form.Column.superclass.onRender.call(this, ct, position);
27487         if(this.width){
27488             this.el.setWidth(this.width);
27489         }
27490         if(this.height){
27491             this.el.setHeight(this.height);
27492         }
27493     },
27494     
27495     // private
27496     renderField : function(f){
27497         f.fieldEl = this.fieldTpl.append(this.el, [
27498                f.id, f.fieldLabel,
27499                f.labelStyle||this.labelStyle||'',
27500                this.elementStyle||'',
27501                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27502                f.itemCls||this.itemCls||'',
27503                f.width ? f.width + this.padWidth : 160 + this.padWidth
27504        ],true);
27505     }
27506 });
27507  
27508
27509 /**
27510  * @class Roo.form.FieldSet
27511  * @extends Roo.form.Layout
27512  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27513  * @constructor
27514  * @param {Object} config Configuration options
27515  */
27516 Roo.form.FieldSet = function(config){
27517     Roo.form.FieldSet.superclass.constructor.call(this, config);
27518 };
27519
27520 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27521     /**
27522      * @cfg {String} legend
27523      * The text to display as the legend for the FieldSet (defaults to '')
27524      */
27525     /**
27526      * @cfg {String/Object} autoCreate
27527      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27528      */
27529
27530     // private
27531     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27532
27533     // private
27534     onRender : function(ct, position){
27535         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27536         if(this.legend){
27537             this.setLegend(this.legend);
27538         }
27539     },
27540
27541     // private
27542     setLegend : function(text){
27543         if(this.rendered){
27544             this.el.child('legend').update(text);
27545         }
27546     }
27547 });/*
27548  * Based on:
27549  * Ext JS Library 1.1.1
27550  * Copyright(c) 2006-2007, Ext JS, LLC.
27551  *
27552  * Originally Released Under LGPL - original licence link has changed is not relivant.
27553  *
27554  * Fork - LGPL
27555  * <script type="text/javascript">
27556  */
27557 /**
27558  * @class Roo.form.VTypes
27559  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27560  * @singleton
27561  */
27562 Roo.form.VTypes = function(){
27563     // closure these in so they are only created once.
27564     var alpha = /^[a-zA-Z_]+$/;
27565     var alphanum = /^[a-zA-Z0-9_]+$/;
27566     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27567     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27568
27569     // All these messages and functions are configurable
27570     return {
27571         /**
27572          * The function used to validate email addresses
27573          * @param {String} value The email address
27574          */
27575         'email' : function(v){
27576             return email.test(v);
27577         },
27578         /**
27579          * The error text to display when the email validation function returns false
27580          * @type String
27581          */
27582         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27583         /**
27584          * The keystroke filter mask to be applied on email input
27585          * @type RegExp
27586          */
27587         'emailMask' : /[a-z0-9_\.\-@]/i,
27588
27589         /**
27590          * The function used to validate URLs
27591          * @param {String} value The URL
27592          */
27593         'url' : function(v){
27594             return url.test(v);
27595         },
27596         /**
27597          * The error text to display when the url validation function returns false
27598          * @type String
27599          */
27600         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27601         
27602         /**
27603          * The function used to validate alpha values
27604          * @param {String} value The value
27605          */
27606         'alpha' : function(v){
27607             return alpha.test(v);
27608         },
27609         /**
27610          * The error text to display when the alpha validation function returns false
27611          * @type String
27612          */
27613         'alphaText' : 'This field should only contain letters and _',
27614         /**
27615          * The keystroke filter mask to be applied on alpha input
27616          * @type RegExp
27617          */
27618         'alphaMask' : /[a-z_]/i,
27619
27620         /**
27621          * The function used to validate alphanumeric values
27622          * @param {String} value The value
27623          */
27624         'alphanum' : function(v){
27625             return alphanum.test(v);
27626         },
27627         /**
27628          * The error text to display when the alphanumeric validation function returns false
27629          * @type String
27630          */
27631         'alphanumText' : 'This field should only contain letters, numbers and _',
27632         /**
27633          * The keystroke filter mask to be applied on alphanumeric input
27634          * @type RegExp
27635          */
27636         'alphanumMask' : /[a-z0-9_]/i
27637     };
27638 }();//<script type="text/javascript">
27639
27640 /**
27641  * @class Roo.form.FCKeditor
27642  * @extends Roo.form.TextArea
27643  * Wrapper around the FCKEditor http://www.fckeditor.net
27644  * @constructor
27645  * Creates a new FCKeditor
27646  * @param {Object} config Configuration options
27647  */
27648 Roo.form.FCKeditor = function(config){
27649     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27650     this.addEvents({
27651          /**
27652          * @event editorinit
27653          * Fired when the editor is initialized - you can add extra handlers here..
27654          * @param {FCKeditor} this
27655          * @param {Object} the FCK object.
27656          */
27657         editorinit : true
27658     });
27659     
27660     
27661 };
27662 Roo.form.FCKeditor.editors = { };
27663 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27664 {
27665     //defaultAutoCreate : {
27666     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27667     //},
27668     // private
27669     /**
27670      * @cfg {Object} fck options - see fck manual for details.
27671      */
27672     fckconfig : false,
27673     
27674     /**
27675      * @cfg {Object} fck toolbar set (Basic or Default)
27676      */
27677     toolbarSet : 'Basic',
27678     /**
27679      * @cfg {Object} fck BasePath
27680      */ 
27681     basePath : '/fckeditor/',
27682     
27683     
27684     frame : false,
27685     
27686     value : '',
27687     
27688    
27689     onRender : function(ct, position)
27690     {
27691         if(!this.el){
27692             this.defaultAutoCreate = {
27693                 tag: "textarea",
27694                 style:"width:300px;height:60px;",
27695                 autocomplete: "off"
27696             };
27697         }
27698         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27699         /*
27700         if(this.grow){
27701             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27702             if(this.preventScrollbars){
27703                 this.el.setStyle("overflow", "hidden");
27704             }
27705             this.el.setHeight(this.growMin);
27706         }
27707         */
27708         //console.log('onrender' + this.getId() );
27709         Roo.form.FCKeditor.editors[this.getId()] = this;
27710          
27711
27712         this.replaceTextarea() ;
27713         
27714     },
27715     
27716     getEditor : function() {
27717         return this.fckEditor;
27718     },
27719     /**
27720      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27721      * @param {Mixed} value The value to set
27722      */
27723     
27724     
27725     setValue : function(value)
27726     {
27727         //console.log('setValue: ' + value);
27728         
27729         if(typeof(value) == 'undefined') { // not sure why this is happending...
27730             return;
27731         }
27732         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27733         
27734         //if(!this.el || !this.getEditor()) {
27735         //    this.value = value;
27736             //this.setValue.defer(100,this,[value]);    
27737         //    return;
27738         //} 
27739         
27740         if(!this.getEditor()) {
27741             return;
27742         }
27743         
27744         this.getEditor().SetData(value);
27745         
27746         //
27747
27748     },
27749
27750     /**
27751      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27752      * @return {Mixed} value The field value
27753      */
27754     getValue : function()
27755     {
27756         
27757         if (this.frame && this.frame.dom.style.display == 'none') {
27758             return Roo.form.FCKeditor.superclass.getValue.call(this);
27759         }
27760         
27761         if(!this.el || !this.getEditor()) {
27762            
27763            // this.getValue.defer(100,this); 
27764             return this.value;
27765         }
27766        
27767         
27768         var value=this.getEditor().GetData();
27769         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27770         return Roo.form.FCKeditor.superclass.getValue.call(this);
27771         
27772
27773     },
27774
27775     /**
27776      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27777      * @return {Mixed} value The field value
27778      */
27779     getRawValue : function()
27780     {
27781         if (this.frame && this.frame.dom.style.display == 'none') {
27782             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27783         }
27784         
27785         if(!this.el || !this.getEditor()) {
27786             //this.getRawValue.defer(100,this); 
27787             return this.value;
27788             return;
27789         }
27790         
27791         
27792         
27793         var value=this.getEditor().GetData();
27794         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27795         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27796          
27797     },
27798     
27799     setSize : function(w,h) {
27800         
27801         
27802         
27803         //if (this.frame && this.frame.dom.style.display == 'none') {
27804         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27805         //    return;
27806         //}
27807         //if(!this.el || !this.getEditor()) {
27808         //    this.setSize.defer(100,this, [w,h]); 
27809         //    return;
27810         //}
27811         
27812         
27813         
27814         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27815         
27816         this.frame.dom.setAttribute('width', w);
27817         this.frame.dom.setAttribute('height', h);
27818         this.frame.setSize(w,h);
27819         
27820     },
27821     
27822     toggleSourceEdit : function(value) {
27823         
27824       
27825          
27826         this.el.dom.style.display = value ? '' : 'none';
27827         this.frame.dom.style.display = value ?  'none' : '';
27828         
27829     },
27830     
27831     
27832     focus: function(tag)
27833     {
27834         if (this.frame.dom.style.display == 'none') {
27835             return Roo.form.FCKeditor.superclass.focus.call(this);
27836         }
27837         if(!this.el || !this.getEditor()) {
27838             this.focus.defer(100,this, [tag]); 
27839             return;
27840         }
27841         
27842         
27843         
27844         
27845         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27846         this.getEditor().Focus();
27847         if (tgs.length) {
27848             if (!this.getEditor().Selection.GetSelection()) {
27849                 this.focus.defer(100,this, [tag]); 
27850                 return;
27851             }
27852             
27853             
27854             var r = this.getEditor().EditorDocument.createRange();
27855             r.setStart(tgs[0],0);
27856             r.setEnd(tgs[0],0);
27857             this.getEditor().Selection.GetSelection().removeAllRanges();
27858             this.getEditor().Selection.GetSelection().addRange(r);
27859             this.getEditor().Focus();
27860         }
27861         
27862     },
27863     
27864     
27865     
27866     replaceTextarea : function()
27867     {
27868         if ( document.getElementById( this.getId() + '___Frame' ) )
27869             return ;
27870         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27871         //{
27872             // We must check the elements firstly using the Id and then the name.
27873         var oTextarea = document.getElementById( this.getId() );
27874         
27875         var colElementsByName = document.getElementsByName( this.getId() ) ;
27876          
27877         oTextarea.style.display = 'none' ;
27878
27879         if ( oTextarea.tabIndex ) {            
27880             this.TabIndex = oTextarea.tabIndex ;
27881         }
27882         
27883         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27884         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27885         this.frame = Roo.get(this.getId() + '___Frame')
27886     },
27887     
27888     _getConfigHtml : function()
27889     {
27890         var sConfig = '' ;
27891
27892         for ( var o in this.fckconfig ) {
27893             sConfig += sConfig.length > 0  ? '&amp;' : '';
27894             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27895         }
27896
27897         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27898     },
27899     
27900     
27901     _getIFrameHtml : function()
27902     {
27903         var sFile = 'fckeditor.html' ;
27904         /* no idea what this is about..
27905         try
27906         {
27907             if ( (/fcksource=true/i).test( window.top.location.search ) )
27908                 sFile = 'fckeditor.original.html' ;
27909         }
27910         catch (e) { 
27911         */
27912
27913         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27914         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27915         
27916         
27917         var html = '<iframe id="' + this.getId() +
27918             '___Frame" src="' + sLink +
27919             '" width="' + this.width +
27920             '" height="' + this.height + '"' +
27921             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27922             ' frameborder="0" scrolling="no"></iframe>' ;
27923
27924         return html ;
27925     },
27926     
27927     _insertHtmlBefore : function( html, element )
27928     {
27929         if ( element.insertAdjacentHTML )       {
27930             // IE
27931             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27932         } else { // Gecko
27933             var oRange = document.createRange() ;
27934             oRange.setStartBefore( element ) ;
27935             var oFragment = oRange.createContextualFragment( html );
27936             element.parentNode.insertBefore( oFragment, element ) ;
27937         }
27938     }
27939     
27940     
27941   
27942     
27943     
27944     
27945     
27946
27947 });
27948
27949 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27950
27951 function FCKeditor_OnComplete(editorInstance){
27952     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27953     f.fckEditor = editorInstance;
27954     //console.log("loaded");
27955     f.fireEvent('editorinit', f, editorInstance);
27956
27957   
27958
27959  
27960
27961
27962
27963
27964
27965
27966
27967
27968
27969
27970
27971
27972
27973
27974
27975 //<script type="text/javascript">
27976 /**
27977  * @class Roo.form.GridField
27978  * @extends Roo.form.Field
27979  * Embed a grid (or editable grid into a form)
27980  * STATUS ALPHA
27981  * 
27982  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27983  * it needs 
27984  * xgrid.store = Roo.data.Store
27985  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27986  * xgrid.store.reader = Roo.data.JsonReader 
27987  * 
27988  * 
27989  * @constructor
27990  * Creates a new GridField
27991  * @param {Object} config Configuration options
27992  */
27993 Roo.form.GridField = function(config){
27994     Roo.form.GridField.superclass.constructor.call(this, config);
27995      
27996 };
27997
27998 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27999     /**
28000      * @cfg {Number} width  - used to restrict width of grid..
28001      */
28002     width : 100,
28003     /**
28004      * @cfg {Number} height - used to restrict height of grid..
28005      */
28006     height : 50,
28007      /**
28008      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28009          * 
28010          *}
28011      */
28012     xgrid : false, 
28013     /**
28014      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28015      * {tag: "input", type: "checkbox", autocomplete: "off"})
28016      */
28017    // defaultAutoCreate : { tag: 'div' },
28018     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28019     /**
28020      * @cfg {String} addTitle Text to include for adding a title.
28021      */
28022     addTitle : false,
28023     //
28024     onResize : function(){
28025         Roo.form.Field.superclass.onResize.apply(this, arguments);
28026     },
28027
28028     initEvents : function(){
28029         // Roo.form.Checkbox.superclass.initEvents.call(this);
28030         // has no events...
28031        
28032     },
28033
28034
28035     getResizeEl : function(){
28036         return this.wrap;
28037     },
28038
28039     getPositionEl : function(){
28040         return this.wrap;
28041     },
28042
28043     // private
28044     onRender : function(ct, position){
28045         
28046         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28047         var style = this.style;
28048         delete this.style;
28049         
28050         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28051         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28052         this.viewEl = this.wrap.createChild({ tag: 'div' });
28053         if (style) {
28054             this.viewEl.applyStyles(style);
28055         }
28056         if (this.width) {
28057             this.viewEl.setWidth(this.width);
28058         }
28059         if (this.height) {
28060             this.viewEl.setHeight(this.height);
28061         }
28062         //if(this.inputValue !== undefined){
28063         //this.setValue(this.value);
28064         
28065         
28066         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28067         
28068         
28069         this.grid.render();
28070         this.grid.getDataSource().on('remove', this.refreshValue, this);
28071         this.grid.getDataSource().on('update', this.refreshValue, this);
28072         this.grid.on('afteredit', this.refreshValue, this);
28073  
28074     },
28075      
28076     
28077     /**
28078      * Sets the value of the item. 
28079      * @param {String} either an object  or a string..
28080      */
28081     setValue : function(v){
28082         //this.value = v;
28083         v = v || []; // empty set..
28084         // this does not seem smart - it really only affects memoryproxy grids..
28085         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28086             var ds = this.grid.getDataSource();
28087             // assumes a json reader..
28088             var data = {}
28089             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28090             ds.loadData( data);
28091         }
28092         Roo.form.GridField.superclass.setValue.call(this, v);
28093         this.refreshValue();
28094         // should load data in the grid really....
28095     },
28096     
28097     // private
28098     refreshValue: function() {
28099          var val = [];
28100         this.grid.getDataSource().each(function(r) {
28101             val.push(r.data);
28102         });
28103         this.el.dom.value = Roo.encode(val);
28104     }
28105     
28106      
28107     
28108     
28109 });/*
28110  * Based on:
28111  * Ext JS Library 1.1.1
28112  * Copyright(c) 2006-2007, Ext JS, LLC.
28113  *
28114  * Originally Released Under LGPL - original licence link has changed is not relivant.
28115  *
28116  * Fork - LGPL
28117  * <script type="text/javascript">
28118  */
28119 /**
28120  * @class Roo.form.DisplayField
28121  * @extends Roo.form.Field
28122  * A generic Field to display non-editable data.
28123  * @constructor
28124  * Creates a new Display Field item.
28125  * @param {Object} config Configuration options
28126  */
28127 Roo.form.DisplayField = function(config){
28128     Roo.form.DisplayField.superclass.constructor.call(this, config);
28129     
28130 };
28131
28132 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28133     inputType:      'hidden',
28134     allowBlank:     true,
28135     readOnly:         true,
28136     
28137  
28138     /**
28139      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28140      */
28141     focusClass : undefined,
28142     /**
28143      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28144      */
28145     fieldClass: 'x-form-field',
28146     
28147      /**
28148      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28149      */
28150     valueRenderer: undefined,
28151     
28152     width: 100,
28153     /**
28154      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28155      * {tag: "input", type: "checkbox", autocomplete: "off"})
28156      */
28157      
28158  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28159
28160     onResize : function(){
28161         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28162         
28163     },
28164
28165     initEvents : function(){
28166         // Roo.form.Checkbox.superclass.initEvents.call(this);
28167         // has no events...
28168        
28169     },
28170
28171
28172     getResizeEl : function(){
28173         return this.wrap;
28174     },
28175
28176     getPositionEl : function(){
28177         return this.wrap;
28178     },
28179
28180     // private
28181     onRender : function(ct, position){
28182         
28183         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28184         //if(this.inputValue !== undefined){
28185         this.wrap = this.el.wrap();
28186         
28187         this.viewEl = this.wrap.createChild({ tag: 'div'});
28188         
28189         if (this.bodyStyle) {
28190             this.viewEl.applyStyles(this.bodyStyle);
28191         }
28192         //this.viewEl.setStyle('padding', '2px');
28193         
28194         this.setValue(this.value);
28195         
28196     },
28197 /*
28198     // private
28199     initValue : Roo.emptyFn,
28200
28201   */
28202
28203         // private
28204     onClick : function(){
28205         
28206     },
28207
28208     /**
28209      * Sets the checked state of the checkbox.
28210      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28211      */
28212     setValue : function(v){
28213         this.value = v;
28214         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28215         // this might be called before we have a dom element..
28216         if (!this.viewEl) {
28217             return;
28218         }
28219         this.viewEl.dom.innerHTML = html;
28220         Roo.form.DisplayField.superclass.setValue.call(this, v);
28221
28222     }
28223 });//<script type="text/javasscript">
28224  
28225
28226 /**
28227  * @class Roo.DDView
28228  * A DnD enabled version of Roo.View.
28229  * @param {Element/String} container The Element in which to create the View.
28230  * @param {String} tpl The template string used to create the markup for each element of the View
28231  * @param {Object} config The configuration properties. These include all the config options of
28232  * {@link Roo.View} plus some specific to this class.<br>
28233  * <p>
28234  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28235  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28236  * <p>
28237  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28238 .x-view-drag-insert-above {
28239         border-top:1px dotted #3366cc;
28240 }
28241 .x-view-drag-insert-below {
28242         border-bottom:1px dotted #3366cc;
28243 }
28244 </code></pre>
28245  * 
28246  */
28247  
28248 Roo.DDView = function(container, tpl, config) {
28249     Roo.DDView.superclass.constructor.apply(this, arguments);
28250     this.getEl().setStyle("outline", "0px none");
28251     this.getEl().unselectable();
28252     if (this.dragGroup) {
28253                 this.setDraggable(this.dragGroup.split(","));
28254     }
28255     if (this.dropGroup) {
28256                 this.setDroppable(this.dropGroup.split(","));
28257     }
28258     if (this.deletable) {
28259         this.setDeletable();
28260     }
28261     this.isDirtyFlag = false;
28262         this.addEvents({
28263                 "drop" : true
28264         });
28265 };
28266
28267 Roo.extend(Roo.DDView, Roo.View, {
28268 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28269 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28270 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28271 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28272
28273         isFormField: true,
28274
28275         reset: Roo.emptyFn,
28276         
28277         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28278
28279         validate: function() {
28280                 return true;
28281         },
28282         
28283         destroy: function() {
28284                 this.purgeListeners();
28285                 this.getEl.removeAllListeners();
28286                 this.getEl().remove();
28287                 if (this.dragZone) {
28288                         if (this.dragZone.destroy) {
28289                                 this.dragZone.destroy();
28290                         }
28291                 }
28292                 if (this.dropZone) {
28293                         if (this.dropZone.destroy) {
28294                                 this.dropZone.destroy();
28295                         }
28296                 }
28297         },
28298
28299 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28300         getName: function() {
28301                 return this.name;
28302         },
28303
28304 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28305         setValue: function(v) {
28306                 if (!this.store) {
28307                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28308                 }
28309                 var data = {};
28310                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28311                 this.store.proxy = new Roo.data.MemoryProxy(data);
28312                 this.store.load();
28313         },
28314
28315 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28316         getValue: function() {
28317                 var result = '(';
28318                 this.store.each(function(rec) {
28319                         result += rec.id + ',';
28320                 });
28321                 return result.substr(0, result.length - 1) + ')';
28322         },
28323         
28324         getIds: function() {
28325                 var i = 0, result = new Array(this.store.getCount());
28326                 this.store.each(function(rec) {
28327                         result[i++] = rec.id;
28328                 });
28329                 return result;
28330         },
28331         
28332         isDirty: function() {
28333                 return this.isDirtyFlag;
28334         },
28335
28336 /**
28337  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28338  *      whole Element becomes the target, and this causes the drop gesture to append.
28339  */
28340     getTargetFromEvent : function(e) {
28341                 var target = e.getTarget();
28342                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28343                 target = target.parentNode;
28344                 }
28345                 if (!target) {
28346                         target = this.el.dom.lastChild || this.el.dom;
28347                 }
28348                 return target;
28349     },
28350
28351 /**
28352  *      Create the drag data which consists of an object which has the property "ddel" as
28353  *      the drag proxy element. 
28354  */
28355     getDragData : function(e) {
28356         var target = this.findItemFromChild(e.getTarget());
28357                 if(target) {
28358                         this.handleSelection(e);
28359                         var selNodes = this.getSelectedNodes();
28360             var dragData = {
28361                 source: this,
28362                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28363                 nodes: selNodes,
28364                 records: []
28365                         };
28366                         var selectedIndices = this.getSelectedIndexes();
28367                         for (var i = 0; i < selectedIndices.length; i++) {
28368                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28369                         }
28370                         if (selNodes.length == 1) {
28371                                 dragData.ddel = target.cloneNode(true); // the div element
28372                         } else {
28373                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28374                                 div.className = 'multi-proxy';
28375                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28376                                         div.appendChild(selNodes[i].cloneNode(true));
28377                                 }
28378                                 dragData.ddel = div;
28379                         }
28380             //console.log(dragData)
28381             //console.log(dragData.ddel.innerHTML)
28382                         return dragData;
28383                 }
28384         //console.log('nodragData')
28385                 return false;
28386     },
28387     
28388 /**     Specify to which ddGroup items in this DDView may be dragged. */
28389     setDraggable: function(ddGroup) {
28390         if (ddGroup instanceof Array) {
28391                 Roo.each(ddGroup, this.setDraggable, this);
28392                 return;
28393         }
28394         if (this.dragZone) {
28395                 this.dragZone.addToGroup(ddGroup);
28396         } else {
28397                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28398                                 containerScroll: true,
28399                                 ddGroup: ddGroup 
28400
28401                         });
28402 //                      Draggability implies selection. DragZone's mousedown selects the element.
28403                         if (!this.multiSelect) { this.singleSelect = true; }
28404
28405 //                      Wire the DragZone's handlers up to methods in *this*
28406                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28407                 }
28408     },
28409
28410 /**     Specify from which ddGroup this DDView accepts drops. */
28411     setDroppable: function(ddGroup) {
28412         if (ddGroup instanceof Array) {
28413                 Roo.each(ddGroup, this.setDroppable, this);
28414                 return;
28415         }
28416         if (this.dropZone) {
28417                 this.dropZone.addToGroup(ddGroup);
28418         } else {
28419                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28420                                 containerScroll: true,
28421                                 ddGroup: ddGroup
28422                         });
28423
28424 //                      Wire the DropZone's handlers up to methods in *this*
28425                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28426                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28427                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28428                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28429                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28430                 }
28431     },
28432
28433 /**     Decide whether to drop above or below a View node. */
28434     getDropPoint : function(e, n, dd){
28435         if (n == this.el.dom) { return "above"; }
28436                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28437                 var c = t + (b - t) / 2;
28438                 var y = Roo.lib.Event.getPageY(e);
28439                 if(y <= c) {
28440                         return "above";
28441                 }else{
28442                         return "below";
28443                 }
28444     },
28445
28446     onNodeEnter : function(n, dd, e, data){
28447                 return false;
28448     },
28449     
28450     onNodeOver : function(n, dd, e, data){
28451                 var pt = this.getDropPoint(e, n, dd);
28452                 // set the insert point style on the target node
28453                 var dragElClass = this.dropNotAllowed;
28454                 if (pt) {
28455                         var targetElClass;
28456                         if (pt == "above"){
28457                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28458                                 targetElClass = "x-view-drag-insert-above";
28459                         } else {
28460                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28461                                 targetElClass = "x-view-drag-insert-below";
28462                         }
28463                         if (this.lastInsertClass != targetElClass){
28464                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28465                                 this.lastInsertClass = targetElClass;
28466                         }
28467                 }
28468                 return dragElClass;
28469         },
28470
28471     onNodeOut : function(n, dd, e, data){
28472                 this.removeDropIndicators(n);
28473     },
28474
28475     onNodeDrop : function(n, dd, e, data){
28476         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28477                 return false;
28478         }
28479         var pt = this.getDropPoint(e, n, dd);
28480                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28481                 if (pt == "below") { insertAt++; }
28482                 for (var i = 0; i < data.records.length; i++) {
28483                         var r = data.records[i];
28484                         var dup = this.store.getById(r.id);
28485                         if (dup && (dd != this.dragZone)) {
28486                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28487                         } else {
28488                                 if (data.copy) {
28489                                         this.store.insert(insertAt++, r.copy());
28490                                 } else {
28491                                         data.source.isDirtyFlag = true;
28492                                         r.store.remove(r);
28493                                         this.store.insert(insertAt++, r);
28494                                 }
28495                                 this.isDirtyFlag = true;
28496                         }
28497                 }
28498                 this.dragZone.cachedTarget = null;
28499                 return true;
28500     },
28501
28502     removeDropIndicators : function(n){
28503                 if(n){
28504                         Roo.fly(n).removeClass([
28505                                 "x-view-drag-insert-above",
28506                                 "x-view-drag-insert-below"]);
28507                         this.lastInsertClass = "_noclass";
28508                 }
28509     },
28510
28511 /**
28512  *      Utility method. Add a delete option to the DDView's context menu.
28513  *      @param {String} imageUrl The URL of the "delete" icon image.
28514  */
28515         setDeletable: function(imageUrl) {
28516                 if (!this.singleSelect && !this.multiSelect) {
28517                         this.singleSelect = true;
28518                 }
28519                 var c = this.getContextMenu();
28520                 this.contextMenu.on("itemclick", function(item) {
28521                         switch (item.id) {
28522                                 case "delete":
28523                                         this.remove(this.getSelectedIndexes());
28524                                         break;
28525                         }
28526                 }, this);
28527                 this.contextMenu.add({
28528                         icon: imageUrl,
28529                         id: "delete",
28530                         text: 'Delete'
28531                 });
28532         },
28533         
28534 /**     Return the context menu for this DDView. */
28535         getContextMenu: function() {
28536                 if (!this.contextMenu) {
28537 //                      Create the View's context menu
28538                         this.contextMenu = new Roo.menu.Menu({
28539                                 id: this.id + "-contextmenu"
28540                         });
28541                         this.el.on("contextmenu", this.showContextMenu, this);
28542                 }
28543                 return this.contextMenu;
28544         },
28545         
28546         disableContextMenu: function() {
28547                 if (this.contextMenu) {
28548                         this.el.un("contextmenu", this.showContextMenu, this);
28549                 }
28550         },
28551
28552         showContextMenu: function(e, item) {
28553         item = this.findItemFromChild(e.getTarget());
28554                 if (item) {
28555                         e.stopEvent();
28556                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28557                         this.contextMenu.showAt(e.getXY());
28558             }
28559     },
28560
28561 /**
28562  *      Remove {@link Roo.data.Record}s at the specified indices.
28563  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28564  */
28565     remove: function(selectedIndices) {
28566                 selectedIndices = [].concat(selectedIndices);
28567                 for (var i = 0; i < selectedIndices.length; i++) {
28568                         var rec = this.store.getAt(selectedIndices[i]);
28569                         this.store.remove(rec);
28570                 }
28571     },
28572
28573 /**
28574  *      Double click fires the event, but also, if this is draggable, and there is only one other
28575  *      related DropZone, it transfers the selected node.
28576  */
28577     onDblClick : function(e){
28578         var item = this.findItemFromChild(e.getTarget());
28579         if(item){
28580             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28581                 return false;
28582             }
28583             if (this.dragGroup) {
28584                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28585                     while (targets.indexOf(this.dropZone) > -1) {
28586                             targets.remove(this.dropZone);
28587                                 }
28588                     if (targets.length == 1) {
28589                                         this.dragZone.cachedTarget = null;
28590                         var el = Roo.get(targets[0].getEl());
28591                         var box = el.getBox(true);
28592                         targets[0].onNodeDrop(el.dom, {
28593                                 target: el.dom,
28594                                 xy: [box.x, box.y + box.height - 1]
28595                         }, null, this.getDragData(e));
28596                     }
28597                 }
28598         }
28599     },
28600     
28601     handleSelection: function(e) {
28602                 this.dragZone.cachedTarget = null;
28603         var item = this.findItemFromChild(e.getTarget());
28604         if (!item) {
28605                 this.clearSelections(true);
28606                 return;
28607         }
28608                 if (item && (this.multiSelect || this.singleSelect)){
28609                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28610                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28611                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28612                                 this.unselect(item);
28613                         } else {
28614                                 this.select(item, this.multiSelect && e.ctrlKey);
28615                                 this.lastSelection = item;
28616                         }
28617                 }
28618     },
28619
28620     onItemClick : function(item, index, e){
28621                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28622                         return false;
28623                 }
28624                 return true;
28625     },
28626
28627     unselect : function(nodeInfo, suppressEvent){
28628                 var node = this.getNode(nodeInfo);
28629                 if(node && this.isSelected(node)){
28630                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28631                                 Roo.fly(node).removeClass(this.selectedClass);
28632                                 this.selections.remove(node);
28633                                 if(!suppressEvent){
28634                                         this.fireEvent("selectionchange", this, this.selections);
28635                                 }
28636                         }
28637                 }
28638     }
28639 });
28640 /*
28641  * Based on:
28642  * Ext JS Library 1.1.1
28643  * Copyright(c) 2006-2007, Ext JS, LLC.
28644  *
28645  * Originally Released Under LGPL - original licence link has changed is not relivant.
28646  *
28647  * Fork - LGPL
28648  * <script type="text/javascript">
28649  */
28650  
28651 /**
28652  * @class Roo.LayoutManager
28653  * @extends Roo.util.Observable
28654  * Base class for layout managers.
28655  */
28656 Roo.LayoutManager = function(container, config){
28657     Roo.LayoutManager.superclass.constructor.call(this);
28658     this.el = Roo.get(container);
28659     // ie scrollbar fix
28660     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28661         document.body.scroll = "no";
28662     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28663         this.el.position('relative');
28664     }
28665     this.id = this.el.id;
28666     this.el.addClass("x-layout-container");
28667     /** false to disable window resize monitoring @type Boolean */
28668     this.monitorWindowResize = true;
28669     this.regions = {};
28670     this.addEvents({
28671         /**
28672          * @event layout
28673          * Fires when a layout is performed. 
28674          * @param {Roo.LayoutManager} this
28675          */
28676         "layout" : true,
28677         /**
28678          * @event regionresized
28679          * Fires when the user resizes a region. 
28680          * @param {Roo.LayoutRegion} region The resized region
28681          * @param {Number} newSize The new size (width for east/west, height for north/south)
28682          */
28683         "regionresized" : true,
28684         /**
28685          * @event regioncollapsed
28686          * Fires when a region is collapsed. 
28687          * @param {Roo.LayoutRegion} region The collapsed region
28688          */
28689         "regioncollapsed" : true,
28690         /**
28691          * @event regionexpanded
28692          * Fires when a region is expanded.  
28693          * @param {Roo.LayoutRegion} region The expanded region
28694          */
28695         "regionexpanded" : true
28696     });
28697     this.updating = false;
28698     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28699 };
28700
28701 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28702     /**
28703      * Returns true if this layout is currently being updated
28704      * @return {Boolean}
28705      */
28706     isUpdating : function(){
28707         return this.updating; 
28708     },
28709     
28710     /**
28711      * Suspend the LayoutManager from doing auto-layouts while
28712      * making multiple add or remove calls
28713      */
28714     beginUpdate : function(){
28715         this.updating = true;    
28716     },
28717     
28718     /**
28719      * Restore auto-layouts and optionally disable the manager from performing a layout
28720      * @param {Boolean} noLayout true to disable a layout update 
28721      */
28722     endUpdate : function(noLayout){
28723         this.updating = false;
28724         if(!noLayout){
28725             this.layout();
28726         }    
28727     },
28728     
28729     layout: function(){
28730         
28731     },
28732     
28733     onRegionResized : function(region, newSize){
28734         this.fireEvent("regionresized", region, newSize);
28735         this.layout();
28736     },
28737     
28738     onRegionCollapsed : function(region){
28739         this.fireEvent("regioncollapsed", region);
28740     },
28741     
28742     onRegionExpanded : function(region){
28743         this.fireEvent("regionexpanded", region);
28744     },
28745         
28746     /**
28747      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28748      * performs box-model adjustments.
28749      * @return {Object} The size as an object {width: (the width), height: (the height)}
28750      */
28751     getViewSize : function(){
28752         var size;
28753         if(this.el.dom != document.body){
28754             size = this.el.getSize();
28755         }else{
28756             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28757         }
28758         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28759         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28760         return size;
28761     },
28762     
28763     /**
28764      * Returns the Element this layout is bound to.
28765      * @return {Roo.Element}
28766      */
28767     getEl : function(){
28768         return this.el;
28769     },
28770     
28771     /**
28772      * Returns the specified region.
28773      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28774      * @return {Roo.LayoutRegion}
28775      */
28776     getRegion : function(target){
28777         return this.regions[target.toLowerCase()];
28778     },
28779     
28780     onWindowResize : function(){
28781         if(this.monitorWindowResize){
28782             this.layout();
28783         }
28784     }
28785 });/*
28786  * Based on:
28787  * Ext JS Library 1.1.1
28788  * Copyright(c) 2006-2007, Ext JS, LLC.
28789  *
28790  * Originally Released Under LGPL - original licence link has changed is not relivant.
28791  *
28792  * Fork - LGPL
28793  * <script type="text/javascript">
28794  */
28795 /**
28796  * @class Roo.BorderLayout
28797  * @extends Roo.LayoutManager
28798  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28799  * please see: <br><br>
28800  * <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>
28801  * <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>
28802  * Example:
28803  <pre><code>
28804  var layout = new Roo.BorderLayout(document.body, {
28805     north: {
28806         initialSize: 25,
28807         titlebar: false
28808     },
28809     west: {
28810         split:true,
28811         initialSize: 200,
28812         minSize: 175,
28813         maxSize: 400,
28814         titlebar: true,
28815         collapsible: true
28816     },
28817     east: {
28818         split:true,
28819         initialSize: 202,
28820         minSize: 175,
28821         maxSize: 400,
28822         titlebar: true,
28823         collapsible: true
28824     },
28825     south: {
28826         split:true,
28827         initialSize: 100,
28828         minSize: 100,
28829         maxSize: 200,
28830         titlebar: true,
28831         collapsible: true
28832     },
28833     center: {
28834         titlebar: true,
28835         autoScroll:true,
28836         resizeTabs: true,
28837         minTabWidth: 50,
28838         preferredTabWidth: 150
28839     }
28840 });
28841
28842 // shorthand
28843 var CP = Roo.ContentPanel;
28844
28845 layout.beginUpdate();
28846 layout.add("north", new CP("north", "North"));
28847 layout.add("south", new CP("south", {title: "South", closable: true}));
28848 layout.add("west", new CP("west", {title: "West"}));
28849 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28850 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28851 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28852 layout.getRegion("center").showPanel("center1");
28853 layout.endUpdate();
28854 </code></pre>
28855
28856 <b>The container the layout is rendered into can be either the body element or any other element.
28857 If it is not the body element, the container needs to either be an absolute positioned element,
28858 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28859 the container size if it is not the body element.</b>
28860
28861 * @constructor
28862 * Create a new BorderLayout
28863 * @param {String/HTMLElement/Element} container The container this layout is bound to
28864 * @param {Object} config Configuration options
28865  */
28866 Roo.BorderLayout = function(container, config){
28867     config = config || {};
28868     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28869     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28870     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28871         var target = this.factory.validRegions[i];
28872         if(config[target]){
28873             this.addRegion(target, config[target]);
28874         }
28875     }
28876 };
28877
28878 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28879     /**
28880      * Creates and adds a new region if it doesn't already exist.
28881      * @param {String} target The target region key (north, south, east, west or center).
28882      * @param {Object} config The regions config object
28883      * @return {BorderLayoutRegion} The new region
28884      */
28885     addRegion : function(target, config){
28886         if(!this.regions[target]){
28887             var r = this.factory.create(target, this, config);
28888             this.bindRegion(target, r);
28889         }
28890         return this.regions[target];
28891     },
28892
28893     // private (kinda)
28894     bindRegion : function(name, r){
28895         this.regions[name] = r;
28896         r.on("visibilitychange", this.layout, this);
28897         r.on("paneladded", this.layout, this);
28898         r.on("panelremoved", this.layout, this);
28899         r.on("invalidated", this.layout, this);
28900         r.on("resized", this.onRegionResized, this);
28901         r.on("collapsed", this.onRegionCollapsed, this);
28902         r.on("expanded", this.onRegionExpanded, this);
28903     },
28904
28905     /**
28906      * Performs a layout update.
28907      */
28908     layout : function(){
28909         if(this.updating) return;
28910         var size = this.getViewSize();
28911         var w = size.width;
28912         var h = size.height;
28913         var centerW = w;
28914         var centerH = h;
28915         var centerY = 0;
28916         var centerX = 0;
28917         //var x = 0, y = 0;
28918
28919         var rs = this.regions;
28920         var north = rs["north"];
28921         var south = rs["south"]; 
28922         var west = rs["west"];
28923         var east = rs["east"];
28924         var center = rs["center"];
28925         //if(this.hideOnLayout){ // not supported anymore
28926             //c.el.setStyle("display", "none");
28927         //}
28928         if(north && north.isVisible()){
28929             var b = north.getBox();
28930             var m = north.getMargins();
28931             b.width = w - (m.left+m.right);
28932             b.x = m.left;
28933             b.y = m.top;
28934             centerY = b.height + b.y + m.bottom;
28935             centerH -= centerY;
28936             north.updateBox(this.safeBox(b));
28937         }
28938         if(south && south.isVisible()){
28939             var b = south.getBox();
28940             var m = south.getMargins();
28941             b.width = w - (m.left+m.right);
28942             b.x = m.left;
28943             var totalHeight = (b.height + m.top + m.bottom);
28944             b.y = h - totalHeight + m.top;
28945             centerH -= totalHeight;
28946             south.updateBox(this.safeBox(b));
28947         }
28948         if(west && west.isVisible()){
28949             var b = west.getBox();
28950             var m = west.getMargins();
28951             b.height = centerH - (m.top+m.bottom);
28952             b.x = m.left;
28953             b.y = centerY + m.top;
28954             var totalWidth = (b.width + m.left + m.right);
28955             centerX += totalWidth;
28956             centerW -= totalWidth;
28957             west.updateBox(this.safeBox(b));
28958         }
28959         if(east && east.isVisible()){
28960             var b = east.getBox();
28961             var m = east.getMargins();
28962             b.height = centerH - (m.top+m.bottom);
28963             var totalWidth = (b.width + m.left + m.right);
28964             b.x = w - totalWidth + m.left;
28965             b.y = centerY + m.top;
28966             centerW -= totalWidth;
28967             east.updateBox(this.safeBox(b));
28968         }
28969         if(center){
28970             var m = center.getMargins();
28971             var centerBox = {
28972                 x: centerX + m.left,
28973                 y: centerY + m.top,
28974                 width: centerW - (m.left+m.right),
28975                 height: centerH - (m.top+m.bottom)
28976             };
28977             //if(this.hideOnLayout){
28978                 //center.el.setStyle("display", "block");
28979             //}
28980             center.updateBox(this.safeBox(centerBox));
28981         }
28982         this.el.repaint();
28983         this.fireEvent("layout", this);
28984     },
28985
28986     // private
28987     safeBox : function(box){
28988         box.width = Math.max(0, box.width);
28989         box.height = Math.max(0, box.height);
28990         return box;
28991     },
28992
28993     /**
28994      * Adds a ContentPanel (or subclass) to this layout.
28995      * @param {String} target The target region key (north, south, east, west or center).
28996      * @param {Roo.ContentPanel} panel The panel to add
28997      * @return {Roo.ContentPanel} The added panel
28998      */
28999     add : function(target, panel){
29000          
29001         target = target.toLowerCase();
29002         return this.regions[target].add(panel);
29003     },
29004
29005     /**
29006      * Remove a ContentPanel (or subclass) to this layout.
29007      * @param {String} target The target region key (north, south, east, west or center).
29008      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29009      * @return {Roo.ContentPanel} The removed panel
29010      */
29011     remove : function(target, panel){
29012         target = target.toLowerCase();
29013         return this.regions[target].remove(panel);
29014     },
29015
29016     /**
29017      * Searches all regions for a panel with the specified id
29018      * @param {String} panelId
29019      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29020      */
29021     findPanel : function(panelId){
29022         var rs = this.regions;
29023         for(var target in rs){
29024             if(typeof rs[target] != "function"){
29025                 var p = rs[target].getPanel(panelId);
29026                 if(p){
29027                     return p;
29028                 }
29029             }
29030         }
29031         return null;
29032     },
29033
29034     /**
29035      * Searches all regions for a panel with the specified id and activates (shows) it.
29036      * @param {String/ContentPanel} panelId The panels id or the panel itself
29037      * @return {Roo.ContentPanel} The shown panel or null
29038      */
29039     showPanel : function(panelId) {
29040       var rs = this.regions;
29041       for(var target in rs){
29042          var r = rs[target];
29043          if(typeof r != "function"){
29044             if(r.hasPanel(panelId)){
29045                return r.showPanel(panelId);
29046             }
29047          }
29048       }
29049       return null;
29050    },
29051
29052    /**
29053      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29054      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29055      */
29056     restoreState : function(provider){
29057         if(!provider){
29058             provider = Roo.state.Manager;
29059         }
29060         var sm = new Roo.LayoutStateManager();
29061         sm.init(this, provider);
29062     },
29063
29064     /**
29065      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29066      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29067      * a valid ContentPanel config object.  Example:
29068      * <pre><code>
29069 // Create the main layout
29070 var layout = new Roo.BorderLayout('main-ct', {
29071     west: {
29072         split:true,
29073         minSize: 175,
29074         titlebar: true
29075     },
29076     center: {
29077         title:'Components'
29078     }
29079 }, 'main-ct');
29080
29081 // Create and add multiple ContentPanels at once via configs
29082 layout.batchAdd({
29083    west: {
29084        id: 'source-files',
29085        autoCreate:true,
29086        title:'Ext Source Files',
29087        autoScroll:true,
29088        fitToFrame:true
29089    },
29090    center : {
29091        el: cview,
29092        autoScroll:true,
29093        fitToFrame:true,
29094        toolbar: tb,
29095        resizeEl:'cbody'
29096    }
29097 });
29098 </code></pre>
29099      * @param {Object} regions An object containing ContentPanel configs by region name
29100      */
29101     batchAdd : function(regions){
29102         this.beginUpdate();
29103         for(var rname in regions){
29104             var lr = this.regions[rname];
29105             if(lr){
29106                 this.addTypedPanels(lr, regions[rname]);
29107             }
29108         }
29109         this.endUpdate();
29110     },
29111
29112     // private
29113     addTypedPanels : function(lr, ps){
29114         if(typeof ps == 'string'){
29115             lr.add(new Roo.ContentPanel(ps));
29116         }
29117         else if(ps instanceof Array){
29118             for(var i =0, len = ps.length; i < len; i++){
29119                 this.addTypedPanels(lr, ps[i]);
29120             }
29121         }
29122         else if(!ps.events){ // raw config?
29123             var el = ps.el;
29124             delete ps.el; // prevent conflict
29125             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29126         }
29127         else {  // panel object assumed!
29128             lr.add(ps);
29129         }
29130     },
29131     /**
29132      * Adds a xtype elements to the layout.
29133      * <pre><code>
29134
29135 layout.addxtype({
29136        xtype : 'ContentPanel',
29137        region: 'west',
29138        items: [ .... ]
29139    }
29140 );
29141
29142 layout.addxtype({
29143         xtype : 'NestedLayoutPanel',
29144         region: 'west',
29145         layout: {
29146            center: { },
29147            west: { }   
29148         },
29149         items : [ ... list of content panels or nested layout panels.. ]
29150    }
29151 );
29152 </code></pre>
29153      * @param {Object} cfg Xtype definition of item to add.
29154      */
29155     addxtype : function(cfg)
29156     {
29157         // basically accepts a pannel...
29158         // can accept a layout region..!?!?
29159        // console.log('BorderLayout add ' + cfg.xtype)
29160         
29161         if (!cfg.xtype.match(/Panel$/)) {
29162             return false;
29163         }
29164         var ret = false;
29165         var region = cfg.region;
29166         delete cfg.region;
29167         
29168           
29169         var xitems = [];
29170         if (cfg.items) {
29171             xitems = cfg.items;
29172             delete cfg.items;
29173         }
29174         
29175         
29176         switch(cfg.xtype) 
29177         {
29178             case 'ContentPanel':  // ContentPanel (el, cfg)
29179             case 'ScrollPanel':  // ContentPanel (el, cfg)
29180                 if(cfg.autoCreate) {
29181                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29182                 } else {
29183                     var el = this.el.createChild();
29184                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29185                 }
29186                 
29187                 this.add(region, ret);
29188                 break;
29189             
29190             
29191             case 'TreePanel': // our new panel!
29192                 cfg.el = this.el.createChild();
29193                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29194                 this.add(region, ret);
29195                 break;
29196             
29197             case 'NestedLayoutPanel': 
29198                 // create a new Layout (which is  a Border Layout...
29199                 var el = this.el.createChild();
29200                 var clayout = cfg.layout;
29201                 delete cfg.layout;
29202                 clayout.items   = clayout.items  || [];
29203                 // replace this exitems with the clayout ones..
29204                 xitems = clayout.items;
29205                  
29206                 
29207                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29208                     cfg.background = false;
29209                 }
29210                 var layout = new Roo.BorderLayout(el, clayout);
29211                 
29212                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29213                 //console.log('adding nested layout panel '  + cfg.toSource());
29214                 this.add(region, ret);
29215                 
29216                 break;
29217                 
29218             case 'GridPanel': 
29219             
29220                 // needs grid and region
29221                 
29222                 //var el = this.getRegion(region).el.createChild();
29223                 var el = this.el.createChild();
29224                 // create the grid first...
29225                 
29226                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29227                 delete cfg.grid;
29228                 if (region == 'center' && this.active ) {
29229                     cfg.background = false;
29230                 }
29231                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29232                 
29233                 this.add(region, ret);
29234                 if (cfg.background) {
29235                     ret.on('activate', function(gp) {
29236                         if (!gp.grid.rendered) {
29237                             gp.grid.render();
29238                         }
29239                     });
29240                 } else {
29241                     grid.render();
29242                 }
29243                 break;
29244            
29245                
29246                 
29247                 
29248             default: 
29249                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29250                 return;
29251              // GridPanel (grid, cfg)
29252             
29253         }
29254         this.beginUpdate();
29255         // add children..
29256         Roo.each(xitems, function(i)  {
29257             ret.addxtype(i);
29258         });
29259         this.endUpdate();
29260         return ret;
29261         
29262     }
29263 });
29264
29265 /**
29266  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29267  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29268  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29269  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29270  * <pre><code>
29271 // shorthand
29272 var CP = Roo.ContentPanel;
29273
29274 var layout = Roo.BorderLayout.create({
29275     north: {
29276         initialSize: 25,
29277         titlebar: false,
29278         panels: [new CP("north", "North")]
29279     },
29280     west: {
29281         split:true,
29282         initialSize: 200,
29283         minSize: 175,
29284         maxSize: 400,
29285         titlebar: true,
29286         collapsible: true,
29287         panels: [new CP("west", {title: "West"})]
29288     },
29289     east: {
29290         split:true,
29291         initialSize: 202,
29292         minSize: 175,
29293         maxSize: 400,
29294         titlebar: true,
29295         collapsible: true,
29296         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29297     },
29298     south: {
29299         split:true,
29300         initialSize: 100,
29301         minSize: 100,
29302         maxSize: 200,
29303         titlebar: true,
29304         collapsible: true,
29305         panels: [new CP("south", {title: "South", closable: true})]
29306     },
29307     center: {
29308         titlebar: true,
29309         autoScroll:true,
29310         resizeTabs: true,
29311         minTabWidth: 50,
29312         preferredTabWidth: 150,
29313         panels: [
29314             new CP("center1", {title: "Close Me", closable: true}),
29315             new CP("center2", {title: "Center Panel", closable: false})
29316         ]
29317     }
29318 }, document.body);
29319
29320 layout.getRegion("center").showPanel("center1");
29321 </code></pre>
29322  * @param config
29323  * @param targetEl
29324  */
29325 Roo.BorderLayout.create = function(config, targetEl){
29326     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29327     layout.beginUpdate();
29328     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29329     for(var j = 0, jlen = regions.length; j < jlen; j++){
29330         var lr = regions[j];
29331         if(layout.regions[lr] && config[lr].panels){
29332             var r = layout.regions[lr];
29333             var ps = config[lr].panels;
29334             layout.addTypedPanels(r, ps);
29335         }
29336     }
29337     layout.endUpdate();
29338     return layout;
29339 };
29340
29341 // private
29342 Roo.BorderLayout.RegionFactory = {
29343     // private
29344     validRegions : ["north","south","east","west","center"],
29345
29346     // private
29347     create : function(target, mgr, config){
29348         target = target.toLowerCase();
29349         if(config.lightweight || config.basic){
29350             return new Roo.BasicLayoutRegion(mgr, config, target);
29351         }
29352         switch(target){
29353             case "north":
29354                 return new Roo.NorthLayoutRegion(mgr, config);
29355             case "south":
29356                 return new Roo.SouthLayoutRegion(mgr, config);
29357             case "east":
29358                 return new Roo.EastLayoutRegion(mgr, config);
29359             case "west":
29360                 return new Roo.WestLayoutRegion(mgr, config);
29361             case "center":
29362                 return new Roo.CenterLayoutRegion(mgr, config);
29363         }
29364         throw 'Layout region "'+target+'" not supported.';
29365     }
29366 };/*
29367  * Based on:
29368  * Ext JS Library 1.1.1
29369  * Copyright(c) 2006-2007, Ext JS, LLC.
29370  *
29371  * Originally Released Under LGPL - original licence link has changed is not relivant.
29372  *
29373  * Fork - LGPL
29374  * <script type="text/javascript">
29375  */
29376  
29377 /**
29378  * @class Roo.BasicLayoutRegion
29379  * @extends Roo.util.Observable
29380  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29381  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29382  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29383  */
29384 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29385     this.mgr = mgr;
29386     this.position  = pos;
29387     this.events = {
29388         /**
29389          * @scope Roo.BasicLayoutRegion
29390          */
29391         
29392         /**
29393          * @event beforeremove
29394          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29395          * @param {Roo.LayoutRegion} this
29396          * @param {Roo.ContentPanel} panel The panel
29397          * @param {Object} e The cancel event object
29398          */
29399         "beforeremove" : true,
29400         /**
29401          * @event invalidated
29402          * Fires when the layout for this region is changed.
29403          * @param {Roo.LayoutRegion} this
29404          */
29405         "invalidated" : true,
29406         /**
29407          * @event visibilitychange
29408          * Fires when this region is shown or hidden 
29409          * @param {Roo.LayoutRegion} this
29410          * @param {Boolean} visibility true or false
29411          */
29412         "visibilitychange" : true,
29413         /**
29414          * @event paneladded
29415          * Fires when a panel is added. 
29416          * @param {Roo.LayoutRegion} this
29417          * @param {Roo.ContentPanel} panel The panel
29418          */
29419         "paneladded" : true,
29420         /**
29421          * @event panelremoved
29422          * Fires when a panel is removed. 
29423          * @param {Roo.LayoutRegion} this
29424          * @param {Roo.ContentPanel} panel The panel
29425          */
29426         "panelremoved" : true,
29427         /**
29428          * @event collapsed
29429          * Fires when this region is collapsed.
29430          * @param {Roo.LayoutRegion} this
29431          */
29432         "collapsed" : true,
29433         /**
29434          * @event expanded
29435          * Fires when this region is expanded.
29436          * @param {Roo.LayoutRegion} this
29437          */
29438         "expanded" : true,
29439         /**
29440          * @event slideshow
29441          * Fires when this region is slid into view.
29442          * @param {Roo.LayoutRegion} this
29443          */
29444         "slideshow" : true,
29445         /**
29446          * @event slidehide
29447          * Fires when this region slides out of view. 
29448          * @param {Roo.LayoutRegion} this
29449          */
29450         "slidehide" : true,
29451         /**
29452          * @event panelactivated
29453          * Fires when a panel is activated. 
29454          * @param {Roo.LayoutRegion} this
29455          * @param {Roo.ContentPanel} panel The activated panel
29456          */
29457         "panelactivated" : true,
29458         /**
29459          * @event resized
29460          * Fires when the user resizes this region. 
29461          * @param {Roo.LayoutRegion} this
29462          * @param {Number} newSize The new size (width for east/west, height for north/south)
29463          */
29464         "resized" : true
29465     };
29466     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29467     this.panels = new Roo.util.MixedCollection();
29468     this.panels.getKey = this.getPanelId.createDelegate(this);
29469     this.box = null;
29470     this.activePanel = null;
29471     // ensure listeners are added...
29472     
29473     if (config.listeners || config.events) {
29474         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29475             listeners : config.listeners || {},
29476             events : config.events || {}
29477         });
29478     }
29479     
29480     if(skipConfig !== true){
29481         this.applyConfig(config);
29482     }
29483 };
29484
29485 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29486     getPanelId : function(p){
29487         return p.getId();
29488     },
29489     
29490     applyConfig : function(config){
29491         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29492         this.config = config;
29493         
29494     },
29495     
29496     /**
29497      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29498      * the width, for horizontal (north, south) the height.
29499      * @param {Number} newSize The new width or height
29500      */
29501     resizeTo : function(newSize){
29502         var el = this.el ? this.el :
29503                  (this.activePanel ? this.activePanel.getEl() : null);
29504         if(el){
29505             switch(this.position){
29506                 case "east":
29507                 case "west":
29508                     el.setWidth(newSize);
29509                     this.fireEvent("resized", this, newSize);
29510                 break;
29511                 case "north":
29512                 case "south":
29513                     el.setHeight(newSize);
29514                     this.fireEvent("resized", this, newSize);
29515                 break;                
29516             }
29517         }
29518     },
29519     
29520     getBox : function(){
29521         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29522     },
29523     
29524     getMargins : function(){
29525         return this.margins;
29526     },
29527     
29528     updateBox : function(box){
29529         this.box = box;
29530         var el = this.activePanel.getEl();
29531         el.dom.style.left = box.x + "px";
29532         el.dom.style.top = box.y + "px";
29533         this.activePanel.setSize(box.width, box.height);
29534     },
29535     
29536     /**
29537      * Returns the container element for this region.
29538      * @return {Roo.Element}
29539      */
29540     getEl : function(){
29541         return this.activePanel;
29542     },
29543     
29544     /**
29545      * Returns true if this region is currently visible.
29546      * @return {Boolean}
29547      */
29548     isVisible : function(){
29549         return this.activePanel ? true : false;
29550     },
29551     
29552     setActivePanel : function(panel){
29553         panel = this.getPanel(panel);
29554         if(this.activePanel && this.activePanel != panel){
29555             this.activePanel.setActiveState(false);
29556             this.activePanel.getEl().setLeftTop(-10000,-10000);
29557         }
29558         this.activePanel = panel;
29559         panel.setActiveState(true);
29560         if(this.box){
29561             panel.setSize(this.box.width, this.box.height);
29562         }
29563         this.fireEvent("panelactivated", this, panel);
29564         this.fireEvent("invalidated");
29565     },
29566     
29567     /**
29568      * Show the specified panel.
29569      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29570      * @return {Roo.ContentPanel} The shown panel or null
29571      */
29572     showPanel : function(panel){
29573         if(panel = this.getPanel(panel)){
29574             this.setActivePanel(panel);
29575         }
29576         return panel;
29577     },
29578     
29579     /**
29580      * Get the active panel for this region.
29581      * @return {Roo.ContentPanel} The active panel or null
29582      */
29583     getActivePanel : function(){
29584         return this.activePanel;
29585     },
29586     
29587     /**
29588      * Add the passed ContentPanel(s)
29589      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29590      * @return {Roo.ContentPanel} The panel added (if only one was added)
29591      */
29592     add : function(panel){
29593         if(arguments.length > 1){
29594             for(var i = 0, len = arguments.length; i < len; i++) {
29595                 this.add(arguments[i]);
29596             }
29597             return null;
29598         }
29599         if(this.hasPanel(panel)){
29600             this.showPanel(panel);
29601             return panel;
29602         }
29603         var el = panel.getEl();
29604         if(el.dom.parentNode != this.mgr.el.dom){
29605             this.mgr.el.dom.appendChild(el.dom);
29606         }
29607         if(panel.setRegion){
29608             panel.setRegion(this);
29609         }
29610         this.panels.add(panel);
29611         el.setStyle("position", "absolute");
29612         if(!panel.background){
29613             this.setActivePanel(panel);
29614             if(this.config.initialSize && this.panels.getCount()==1){
29615                 this.resizeTo(this.config.initialSize);
29616             }
29617         }
29618         this.fireEvent("paneladded", this, panel);
29619         return panel;
29620     },
29621     
29622     /**
29623      * Returns true if the panel is in this region.
29624      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29625      * @return {Boolean}
29626      */
29627     hasPanel : function(panel){
29628         if(typeof panel == "object"){ // must be panel obj
29629             panel = panel.getId();
29630         }
29631         return this.getPanel(panel) ? true : false;
29632     },
29633     
29634     /**
29635      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29636      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29637      * @param {Boolean} preservePanel Overrides the config preservePanel option
29638      * @return {Roo.ContentPanel} The panel that was removed
29639      */
29640     remove : function(panel, preservePanel){
29641         panel = this.getPanel(panel);
29642         if(!panel){
29643             return null;
29644         }
29645         var e = {};
29646         this.fireEvent("beforeremove", this, panel, e);
29647         if(e.cancel === true){
29648             return null;
29649         }
29650         var panelId = panel.getId();
29651         this.panels.removeKey(panelId);
29652         return panel;
29653     },
29654     
29655     /**
29656      * Returns the panel specified or null if it's not in this region.
29657      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29658      * @return {Roo.ContentPanel}
29659      */
29660     getPanel : function(id){
29661         if(typeof id == "object"){ // must be panel obj
29662             return id;
29663         }
29664         return this.panels.get(id);
29665     },
29666     
29667     /**
29668      * Returns this regions position (north/south/east/west/center).
29669      * @return {String} 
29670      */
29671     getPosition: function(){
29672         return this.position;    
29673     }
29674 });/*
29675  * Based on:
29676  * Ext JS Library 1.1.1
29677  * Copyright(c) 2006-2007, Ext JS, LLC.
29678  *
29679  * Originally Released Under LGPL - original licence link has changed is not relivant.
29680  *
29681  * Fork - LGPL
29682  * <script type="text/javascript">
29683  */
29684  
29685 /**
29686  * @class Roo.LayoutRegion
29687  * @extends Roo.BasicLayoutRegion
29688  * This class represents a region in a layout manager.
29689  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29690  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29691  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29692  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29693  * @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})
29694  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29695  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29696  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29697  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29698  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29699  * @cfg {String} title The title for the region (overrides panel titles)
29700  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29701  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29702  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29703  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29704  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29705  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29706  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29707  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29708  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29709  * @cfg {Boolean} showPin True to show a pin button
29710 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29711 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29712 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29713 * @cfg {Number} width  For East/West panels
29714 * @cfg {Number} height For North/South panels
29715 * @cfg {Boolean} split To show the splitter
29716  */
29717 Roo.LayoutRegion = function(mgr, config, pos){
29718     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29719     var dh = Roo.DomHelper;
29720     /** This region's container element 
29721     * @type Roo.Element */
29722     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29723     /** This region's title element 
29724     * @type Roo.Element */
29725
29726     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29727         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29728         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29729     ]}, true);
29730     this.titleEl.enableDisplayMode();
29731     /** This region's title text element 
29732     * @type HTMLElement */
29733     this.titleTextEl = this.titleEl.dom.firstChild;
29734     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29735     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29736     this.closeBtn.enableDisplayMode();
29737     this.closeBtn.on("click", this.closeClicked, this);
29738     this.closeBtn.hide();
29739
29740     this.createBody(config);
29741     this.visible = true;
29742     this.collapsed = false;
29743
29744     if(config.hideWhenEmpty){
29745         this.hide();
29746         this.on("paneladded", this.validateVisibility, this);
29747         this.on("panelremoved", this.validateVisibility, this);
29748     }
29749     this.applyConfig(config);
29750 };
29751
29752 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29753
29754     createBody : function(){
29755         /** This region's body element 
29756         * @type Roo.Element */
29757         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29758     },
29759
29760     applyConfig : function(c){
29761         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29762             var dh = Roo.DomHelper;
29763             if(c.titlebar !== false){
29764                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29765                 this.collapseBtn.on("click", this.collapse, this);
29766                 this.collapseBtn.enableDisplayMode();
29767
29768                 if(c.showPin === true || this.showPin){
29769                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29770                     this.stickBtn.enableDisplayMode();
29771                     this.stickBtn.on("click", this.expand, this);
29772                     this.stickBtn.hide();
29773                 }
29774             }
29775             /** This region's collapsed element
29776             * @type Roo.Element */
29777             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29778                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29779             ]}, true);
29780             if(c.floatable !== false){
29781                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29782                this.collapsedEl.on("click", this.collapseClick, this);
29783             }
29784
29785             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29786                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29787                    id: "message", unselectable: "on", style:{"float":"left"}});
29788                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29789              }
29790             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29791             this.expandBtn.on("click", this.expand, this);
29792         }
29793         if(this.collapseBtn){
29794             this.collapseBtn.setVisible(c.collapsible == true);
29795         }
29796         this.cmargins = c.cmargins || this.cmargins ||
29797                          (this.position == "west" || this.position == "east" ?
29798                              {top: 0, left: 2, right:2, bottom: 0} :
29799                              {top: 2, left: 0, right:0, bottom: 2});
29800         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29801         this.bottomTabs = c.tabPosition != "top";
29802         this.autoScroll = c.autoScroll || false;
29803         if(this.autoScroll){
29804             this.bodyEl.setStyle("overflow", "auto");
29805         }else{
29806             this.bodyEl.setStyle("overflow", "hidden");
29807         }
29808         //if(c.titlebar !== false){
29809             if((!c.titlebar && !c.title) || c.titlebar === false){
29810                 this.titleEl.hide();
29811             }else{
29812                 this.titleEl.show();
29813                 if(c.title){
29814                     this.titleTextEl.innerHTML = c.title;
29815                 }
29816             }
29817         //}
29818         this.duration = c.duration || .30;
29819         this.slideDuration = c.slideDuration || .45;
29820         this.config = c;
29821         if(c.collapsed){
29822             this.collapse(true);
29823         }
29824         if(c.hidden){
29825             this.hide();
29826         }
29827     },
29828     /**
29829      * Returns true if this region is currently visible.
29830      * @return {Boolean}
29831      */
29832     isVisible : function(){
29833         return this.visible;
29834     },
29835
29836     /**
29837      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29838      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29839      */
29840     setCollapsedTitle : function(title){
29841         title = title || "&#160;";
29842         if(this.collapsedTitleTextEl){
29843             this.collapsedTitleTextEl.innerHTML = title;
29844         }
29845     },
29846
29847     getBox : function(){
29848         var b;
29849         if(!this.collapsed){
29850             b = this.el.getBox(false, true);
29851         }else{
29852             b = this.collapsedEl.getBox(false, true);
29853         }
29854         return b;
29855     },
29856
29857     getMargins : function(){
29858         return this.collapsed ? this.cmargins : this.margins;
29859     },
29860
29861     highlight : function(){
29862         this.el.addClass("x-layout-panel-dragover");
29863     },
29864
29865     unhighlight : function(){
29866         this.el.removeClass("x-layout-panel-dragover");
29867     },
29868
29869     updateBox : function(box){
29870         this.box = box;
29871         if(!this.collapsed){
29872             this.el.dom.style.left = box.x + "px";
29873             this.el.dom.style.top = box.y + "px";
29874             this.updateBody(box.width, box.height);
29875         }else{
29876             this.collapsedEl.dom.style.left = box.x + "px";
29877             this.collapsedEl.dom.style.top = box.y + "px";
29878             this.collapsedEl.setSize(box.width, box.height);
29879         }
29880         if(this.tabs){
29881             this.tabs.autoSizeTabs();
29882         }
29883     },
29884
29885     updateBody : function(w, h){
29886         if(w !== null){
29887             this.el.setWidth(w);
29888             w -= this.el.getBorderWidth("rl");
29889             if(this.config.adjustments){
29890                 w += this.config.adjustments[0];
29891             }
29892         }
29893         if(h !== null){
29894             this.el.setHeight(h);
29895             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29896             h -= this.el.getBorderWidth("tb");
29897             if(this.config.adjustments){
29898                 h += this.config.adjustments[1];
29899             }
29900             this.bodyEl.setHeight(h);
29901             if(this.tabs){
29902                 h = this.tabs.syncHeight(h);
29903             }
29904         }
29905         if(this.panelSize){
29906             w = w !== null ? w : this.panelSize.width;
29907             h = h !== null ? h : this.panelSize.height;
29908         }
29909         if(this.activePanel){
29910             var el = this.activePanel.getEl();
29911             w = w !== null ? w : el.getWidth();
29912             h = h !== null ? h : el.getHeight();
29913             this.panelSize = {width: w, height: h};
29914             this.activePanel.setSize(w, h);
29915         }
29916         if(Roo.isIE && this.tabs){
29917             this.tabs.el.repaint();
29918         }
29919     },
29920
29921     /**
29922      * Returns the container element for this region.
29923      * @return {Roo.Element}
29924      */
29925     getEl : function(){
29926         return this.el;
29927     },
29928
29929     /**
29930      * Hides this region.
29931      */
29932     hide : function(){
29933         if(!this.collapsed){
29934             this.el.dom.style.left = "-2000px";
29935             this.el.hide();
29936         }else{
29937             this.collapsedEl.dom.style.left = "-2000px";
29938             this.collapsedEl.hide();
29939         }
29940         this.visible = false;
29941         this.fireEvent("visibilitychange", this, false);
29942     },
29943
29944     /**
29945      * Shows this region if it was previously hidden.
29946      */
29947     show : function(){
29948         if(!this.collapsed){
29949             this.el.show();
29950         }else{
29951             this.collapsedEl.show();
29952         }
29953         this.visible = true;
29954         this.fireEvent("visibilitychange", this, true);
29955     },
29956
29957     closeClicked : function(){
29958         if(this.activePanel){
29959             this.remove(this.activePanel);
29960         }
29961     },
29962
29963     collapseClick : function(e){
29964         if(this.isSlid){
29965            e.stopPropagation();
29966            this.slideIn();
29967         }else{
29968            e.stopPropagation();
29969            this.slideOut();
29970         }
29971     },
29972
29973     /**
29974      * Collapses this region.
29975      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29976      */
29977     collapse : function(skipAnim){
29978         if(this.collapsed) return;
29979         this.collapsed = true;
29980         if(this.split){
29981             this.split.el.hide();
29982         }
29983         if(this.config.animate && skipAnim !== true){
29984             this.fireEvent("invalidated", this);
29985             this.animateCollapse();
29986         }else{
29987             this.el.setLocation(-20000,-20000);
29988             this.el.hide();
29989             this.collapsedEl.show();
29990             this.fireEvent("collapsed", this);
29991             this.fireEvent("invalidated", this);
29992         }
29993     },
29994
29995     animateCollapse : function(){
29996         // overridden
29997     },
29998
29999     /**
30000      * Expands this region if it was previously collapsed.
30001      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30002      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30003      */
30004     expand : function(e, skipAnim){
30005         if(e) e.stopPropagation();
30006         if(!this.collapsed || this.el.hasActiveFx()) return;
30007         if(this.isSlid){
30008             this.afterSlideIn();
30009             skipAnim = true;
30010         }
30011         this.collapsed = false;
30012         if(this.config.animate && skipAnim !== true){
30013             this.animateExpand();
30014         }else{
30015             this.el.show();
30016             if(this.split){
30017                 this.split.el.show();
30018             }
30019             this.collapsedEl.setLocation(-2000,-2000);
30020             this.collapsedEl.hide();
30021             this.fireEvent("invalidated", this);
30022             this.fireEvent("expanded", this);
30023         }
30024     },
30025
30026     animateExpand : function(){
30027         // overridden
30028     },
30029
30030     initTabs : function(){
30031         this.bodyEl.setStyle("overflow", "hidden");
30032         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30033             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30034             disableTooltips: this.config.disableTabTips
30035         });
30036         if(this.config.hideTabs){
30037             ts.stripWrap.setDisplayed(false);
30038         }
30039         this.tabs = ts;
30040         ts.resizeTabs = this.config.resizeTabs === true;
30041         ts.minTabWidth = this.config.minTabWidth || 40;
30042         ts.maxTabWidth = this.config.maxTabWidth || 250;
30043         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30044         ts.monitorResize = false;
30045         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30046         ts.bodyEl.addClass('x-layout-tabs-body');
30047         this.panels.each(this.initPanelAsTab, this);
30048     },
30049
30050     initPanelAsTab : function(panel){
30051         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30052                     this.config.closeOnTab && panel.isClosable());
30053         if(panel.tabTip !== undefined){
30054             ti.setTooltip(panel.tabTip);
30055         }
30056         ti.on("activate", function(){
30057               this.setActivePanel(panel);
30058         }, this);
30059         if(this.config.closeOnTab){
30060             ti.on("beforeclose", function(t, e){
30061                 e.cancel = true;
30062                 this.remove(panel);
30063             }, this);
30064         }
30065         return ti;
30066     },
30067
30068     updatePanelTitle : function(panel, title){
30069         if(this.activePanel == panel){
30070             this.updateTitle(title);
30071         }
30072         if(this.tabs){
30073             var ti = this.tabs.getTab(panel.getEl().id);
30074             ti.setText(title);
30075             if(panel.tabTip !== undefined){
30076                 ti.setTooltip(panel.tabTip);
30077             }
30078         }
30079     },
30080
30081     updateTitle : function(title){
30082         if(this.titleTextEl && !this.config.title){
30083             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30084         }
30085     },
30086
30087     setActivePanel : function(panel){
30088         panel = this.getPanel(panel);
30089         if(this.activePanel && this.activePanel != panel){
30090             this.activePanel.setActiveState(false);
30091         }
30092         this.activePanel = panel;
30093         panel.setActiveState(true);
30094         if(this.panelSize){
30095             panel.setSize(this.panelSize.width, this.panelSize.height);
30096         }
30097         if(this.closeBtn){
30098             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30099         }
30100         this.updateTitle(panel.getTitle());
30101         if(this.tabs){
30102             this.fireEvent("invalidated", this);
30103         }
30104         this.fireEvent("panelactivated", this, panel);
30105     },
30106
30107     /**
30108      * Shows the specified panel.
30109      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30110      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30111      */
30112     showPanel : function(panel){
30113         if(panel = this.getPanel(panel)){
30114             if(this.tabs){
30115                 var tab = this.tabs.getTab(panel.getEl().id);
30116                 if(tab.isHidden()){
30117                     this.tabs.unhideTab(tab.id);
30118                 }
30119                 tab.activate();
30120             }else{
30121                 this.setActivePanel(panel);
30122             }
30123         }
30124         return panel;
30125     },
30126
30127     /**
30128      * Get the active panel for this region.
30129      * @return {Roo.ContentPanel} The active panel or null
30130      */
30131     getActivePanel : function(){
30132         return this.activePanel;
30133     },
30134
30135     validateVisibility : function(){
30136         if(this.panels.getCount() < 1){
30137             this.updateTitle("&#160;");
30138             this.closeBtn.hide();
30139             this.hide();
30140         }else{
30141             if(!this.isVisible()){
30142                 this.show();
30143             }
30144         }
30145     },
30146
30147     /**
30148      * Adds the passed ContentPanel(s) to this region.
30149      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30150      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30151      */
30152     add : function(panel){
30153         if(arguments.length > 1){
30154             for(var i = 0, len = arguments.length; i < len; i++) {
30155                 this.add(arguments[i]);
30156             }
30157             return null;
30158         }
30159         if(this.hasPanel(panel)){
30160             this.showPanel(panel);
30161             return panel;
30162         }
30163         panel.setRegion(this);
30164         this.panels.add(panel);
30165         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30166             this.bodyEl.dom.appendChild(panel.getEl().dom);
30167             if(panel.background !== true){
30168                 this.setActivePanel(panel);
30169             }
30170             this.fireEvent("paneladded", this, panel);
30171             return panel;
30172         }
30173         if(!this.tabs){
30174             this.initTabs();
30175         }else{
30176             this.initPanelAsTab(panel);
30177         }
30178         if(panel.background !== true){
30179             this.tabs.activate(panel.getEl().id);
30180         }
30181         this.fireEvent("paneladded", this, panel);
30182         return panel;
30183     },
30184
30185     /**
30186      * Hides the tab for the specified panel.
30187      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30188      */
30189     hidePanel : function(panel){
30190         if(this.tabs && (panel = this.getPanel(panel))){
30191             this.tabs.hideTab(panel.getEl().id);
30192         }
30193     },
30194
30195     /**
30196      * Unhides the tab for a previously hidden panel.
30197      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30198      */
30199     unhidePanel : function(panel){
30200         if(this.tabs && (panel = this.getPanel(panel))){
30201             this.tabs.unhideTab(panel.getEl().id);
30202         }
30203     },
30204
30205     clearPanels : function(){
30206         while(this.panels.getCount() > 0){
30207              this.remove(this.panels.first());
30208         }
30209     },
30210
30211     /**
30212      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30213      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30214      * @param {Boolean} preservePanel Overrides the config preservePanel option
30215      * @return {Roo.ContentPanel} The panel that was removed
30216      */
30217     remove : function(panel, preservePanel){
30218         panel = this.getPanel(panel);
30219         if(!panel){
30220             return null;
30221         }
30222         var e = {};
30223         this.fireEvent("beforeremove", this, panel, e);
30224         if(e.cancel === true){
30225             return null;
30226         }
30227         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30228         var panelId = panel.getId();
30229         this.panels.removeKey(panelId);
30230         if(preservePanel){
30231             document.body.appendChild(panel.getEl().dom);
30232         }
30233         if(this.tabs){
30234             this.tabs.removeTab(panel.getEl().id);
30235         }else if (!preservePanel){
30236             this.bodyEl.dom.removeChild(panel.getEl().dom);
30237         }
30238         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30239             var p = this.panels.first();
30240             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30241             tempEl.appendChild(p.getEl().dom);
30242             this.bodyEl.update("");
30243             this.bodyEl.dom.appendChild(p.getEl().dom);
30244             tempEl = null;
30245             this.updateTitle(p.getTitle());
30246             this.tabs = null;
30247             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30248             this.setActivePanel(p);
30249         }
30250         panel.setRegion(null);
30251         if(this.activePanel == panel){
30252             this.activePanel = null;
30253         }
30254         if(this.config.autoDestroy !== false && preservePanel !== true){
30255             try{panel.destroy();}catch(e){}
30256         }
30257         this.fireEvent("panelremoved", this, panel);
30258         return panel;
30259     },
30260
30261     /**
30262      * Returns the TabPanel component used by this region
30263      * @return {Roo.TabPanel}
30264      */
30265     getTabs : function(){
30266         return this.tabs;
30267     },
30268
30269     createTool : function(parentEl, className){
30270         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30271             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30272         btn.addClassOnOver("x-layout-tools-button-over");
30273         return btn;
30274     }
30275 });/*
30276  * Based on:
30277  * Ext JS Library 1.1.1
30278  * Copyright(c) 2006-2007, Ext JS, LLC.
30279  *
30280  * Originally Released Under LGPL - original licence link has changed is not relivant.
30281  *
30282  * Fork - LGPL
30283  * <script type="text/javascript">
30284  */
30285  
30286
30287
30288 /**
30289  * @class Roo.SplitLayoutRegion
30290  * @extends Roo.LayoutRegion
30291  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30292  */
30293 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30294     this.cursor = cursor;
30295     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30296 };
30297
30298 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30299     splitTip : "Drag to resize.",
30300     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30301     useSplitTips : false,
30302
30303     applyConfig : function(config){
30304         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30305         if(config.split){
30306             if(!this.split){
30307                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30308                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30309                 /** The SplitBar for this region 
30310                 * @type Roo.SplitBar */
30311                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30312                 this.split.on("moved", this.onSplitMove, this);
30313                 this.split.useShim = config.useShim === true;
30314                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30315                 if(this.useSplitTips){
30316                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30317                 }
30318                 if(config.collapsible){
30319                     this.split.el.on("dblclick", this.collapse,  this);
30320                 }
30321             }
30322             if(typeof config.minSize != "undefined"){
30323                 this.split.minSize = config.minSize;
30324             }
30325             if(typeof config.maxSize != "undefined"){
30326                 this.split.maxSize = config.maxSize;
30327             }
30328             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30329                 this.hideSplitter();
30330             }
30331         }
30332     },
30333
30334     getHMaxSize : function(){
30335          var cmax = this.config.maxSize || 10000;
30336          var center = this.mgr.getRegion("center");
30337          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30338     },
30339
30340     getVMaxSize : function(){
30341          var cmax = this.config.maxSize || 10000;
30342          var center = this.mgr.getRegion("center");
30343          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30344     },
30345
30346     onSplitMove : function(split, newSize){
30347         this.fireEvent("resized", this, newSize);
30348     },
30349     
30350     /** 
30351      * Returns the {@link Roo.SplitBar} for this region.
30352      * @return {Roo.SplitBar}
30353      */
30354     getSplitBar : function(){
30355         return this.split;
30356     },
30357     
30358     hide : function(){
30359         this.hideSplitter();
30360         Roo.SplitLayoutRegion.superclass.hide.call(this);
30361     },
30362
30363     hideSplitter : function(){
30364         if(this.split){
30365             this.split.el.setLocation(-2000,-2000);
30366             this.split.el.hide();
30367         }
30368     },
30369
30370     show : function(){
30371         if(this.split){
30372             this.split.el.show();
30373         }
30374         Roo.SplitLayoutRegion.superclass.show.call(this);
30375     },
30376     
30377     beforeSlide: function(){
30378         if(Roo.isGecko){// firefox overflow auto bug workaround
30379             this.bodyEl.clip();
30380             if(this.tabs) this.tabs.bodyEl.clip();
30381             if(this.activePanel){
30382                 this.activePanel.getEl().clip();
30383                 
30384                 if(this.activePanel.beforeSlide){
30385                     this.activePanel.beforeSlide();
30386                 }
30387             }
30388         }
30389     },
30390     
30391     afterSlide : function(){
30392         if(Roo.isGecko){// firefox overflow auto bug workaround
30393             this.bodyEl.unclip();
30394             if(this.tabs) this.tabs.bodyEl.unclip();
30395             if(this.activePanel){
30396                 this.activePanel.getEl().unclip();
30397                 if(this.activePanel.afterSlide){
30398                     this.activePanel.afterSlide();
30399                 }
30400             }
30401         }
30402     },
30403
30404     initAutoHide : function(){
30405         if(this.autoHide !== false){
30406             if(!this.autoHideHd){
30407                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30408                 this.autoHideHd = {
30409                     "mouseout": function(e){
30410                         if(!e.within(this.el, true)){
30411                             st.delay(500);
30412                         }
30413                     },
30414                     "mouseover" : function(e){
30415                         st.cancel();
30416                     },
30417                     scope : this
30418                 };
30419             }
30420             this.el.on(this.autoHideHd);
30421         }
30422     },
30423
30424     clearAutoHide : function(){
30425         if(this.autoHide !== false){
30426             this.el.un("mouseout", this.autoHideHd.mouseout);
30427             this.el.un("mouseover", this.autoHideHd.mouseover);
30428         }
30429     },
30430
30431     clearMonitor : function(){
30432         Roo.get(document).un("click", this.slideInIf, this);
30433     },
30434
30435     // these names are backwards but not changed for compat
30436     slideOut : function(){
30437         if(this.isSlid || this.el.hasActiveFx()){
30438             return;
30439         }
30440         this.isSlid = true;
30441         if(this.collapseBtn){
30442             this.collapseBtn.hide();
30443         }
30444         this.closeBtnState = this.closeBtn.getStyle('display');
30445         this.closeBtn.hide();
30446         if(this.stickBtn){
30447             this.stickBtn.show();
30448         }
30449         this.el.show();
30450         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30451         this.beforeSlide();
30452         this.el.setStyle("z-index", 10001);
30453         this.el.slideIn(this.getSlideAnchor(), {
30454             callback: function(){
30455                 this.afterSlide();
30456                 this.initAutoHide();
30457                 Roo.get(document).on("click", this.slideInIf, this);
30458                 this.fireEvent("slideshow", this);
30459             },
30460             scope: this,
30461             block: true
30462         });
30463     },
30464
30465     afterSlideIn : function(){
30466         this.clearAutoHide();
30467         this.isSlid = false;
30468         this.clearMonitor();
30469         this.el.setStyle("z-index", "");
30470         if(this.collapseBtn){
30471             this.collapseBtn.show();
30472         }
30473         this.closeBtn.setStyle('display', this.closeBtnState);
30474         if(this.stickBtn){
30475             this.stickBtn.hide();
30476         }
30477         this.fireEvent("slidehide", this);
30478     },
30479
30480     slideIn : function(cb){
30481         if(!this.isSlid || this.el.hasActiveFx()){
30482             Roo.callback(cb);
30483             return;
30484         }
30485         this.isSlid = false;
30486         this.beforeSlide();
30487         this.el.slideOut(this.getSlideAnchor(), {
30488             callback: function(){
30489                 this.el.setLeftTop(-10000, -10000);
30490                 this.afterSlide();
30491                 this.afterSlideIn();
30492                 Roo.callback(cb);
30493             },
30494             scope: this,
30495             block: true
30496         });
30497     },
30498     
30499     slideInIf : function(e){
30500         if(!e.within(this.el)){
30501             this.slideIn();
30502         }
30503     },
30504
30505     animateCollapse : function(){
30506         this.beforeSlide();
30507         this.el.setStyle("z-index", 20000);
30508         var anchor = this.getSlideAnchor();
30509         this.el.slideOut(anchor, {
30510             callback : function(){
30511                 this.el.setStyle("z-index", "");
30512                 this.collapsedEl.slideIn(anchor, {duration:.3});
30513                 this.afterSlide();
30514                 this.el.setLocation(-10000,-10000);
30515                 this.el.hide();
30516                 this.fireEvent("collapsed", this);
30517             },
30518             scope: this,
30519             block: true
30520         });
30521     },
30522
30523     animateExpand : function(){
30524         this.beforeSlide();
30525         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30526         this.el.setStyle("z-index", 20000);
30527         this.collapsedEl.hide({
30528             duration:.1
30529         });
30530         this.el.slideIn(this.getSlideAnchor(), {
30531             callback : function(){
30532                 this.el.setStyle("z-index", "");
30533                 this.afterSlide();
30534                 if(this.split){
30535                     this.split.el.show();
30536                 }
30537                 this.fireEvent("invalidated", this);
30538                 this.fireEvent("expanded", this);
30539             },
30540             scope: this,
30541             block: true
30542         });
30543     },
30544
30545     anchors : {
30546         "west" : "left",
30547         "east" : "right",
30548         "north" : "top",
30549         "south" : "bottom"
30550     },
30551
30552     sanchors : {
30553         "west" : "l",
30554         "east" : "r",
30555         "north" : "t",
30556         "south" : "b"
30557     },
30558
30559     canchors : {
30560         "west" : "tl-tr",
30561         "east" : "tr-tl",
30562         "north" : "tl-bl",
30563         "south" : "bl-tl"
30564     },
30565
30566     getAnchor : function(){
30567         return this.anchors[this.position];
30568     },
30569
30570     getCollapseAnchor : function(){
30571         return this.canchors[this.position];
30572     },
30573
30574     getSlideAnchor : function(){
30575         return this.sanchors[this.position];
30576     },
30577
30578     getAlignAdj : function(){
30579         var cm = this.cmargins;
30580         switch(this.position){
30581             case "west":
30582                 return [0, 0];
30583             break;
30584             case "east":
30585                 return [0, 0];
30586             break;
30587             case "north":
30588                 return [0, 0];
30589             break;
30590             case "south":
30591                 return [0, 0];
30592             break;
30593         }
30594     },
30595
30596     getExpandAdj : function(){
30597         var c = this.collapsedEl, cm = this.cmargins;
30598         switch(this.position){
30599             case "west":
30600                 return [-(cm.right+c.getWidth()+cm.left), 0];
30601             break;
30602             case "east":
30603                 return [cm.right+c.getWidth()+cm.left, 0];
30604             break;
30605             case "north":
30606                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30607             break;
30608             case "south":
30609                 return [0, cm.top+cm.bottom+c.getHeight()];
30610             break;
30611         }
30612     }
30613 });/*
30614  * Based on:
30615  * Ext JS Library 1.1.1
30616  * Copyright(c) 2006-2007, Ext JS, LLC.
30617  *
30618  * Originally Released Under LGPL - original licence link has changed is not relivant.
30619  *
30620  * Fork - LGPL
30621  * <script type="text/javascript">
30622  */
30623 /*
30624  * These classes are private internal classes
30625  */
30626 Roo.CenterLayoutRegion = function(mgr, config){
30627     Roo.LayoutRegion.call(this, mgr, config, "center");
30628     this.visible = true;
30629     this.minWidth = config.minWidth || 20;
30630     this.minHeight = config.minHeight || 20;
30631 };
30632
30633 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30634     hide : function(){
30635         // center panel can't be hidden
30636     },
30637     
30638     show : function(){
30639         // center panel can't be hidden
30640     },
30641     
30642     getMinWidth: function(){
30643         return this.minWidth;
30644     },
30645     
30646     getMinHeight: function(){
30647         return this.minHeight;
30648     }
30649 });
30650
30651
30652 Roo.NorthLayoutRegion = function(mgr, config){
30653     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30654     if(this.split){
30655         this.split.placement = Roo.SplitBar.TOP;
30656         this.split.orientation = Roo.SplitBar.VERTICAL;
30657         this.split.el.addClass("x-layout-split-v");
30658     }
30659     var size = config.initialSize || config.height;
30660     if(typeof size != "undefined"){
30661         this.el.setHeight(size);
30662     }
30663 };
30664 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30665     orientation: Roo.SplitBar.VERTICAL,
30666     getBox : function(){
30667         if(this.collapsed){
30668             return this.collapsedEl.getBox();
30669         }
30670         var box = this.el.getBox();
30671         if(this.split){
30672             box.height += this.split.el.getHeight();
30673         }
30674         return box;
30675     },
30676     
30677     updateBox : function(box){
30678         if(this.split && !this.collapsed){
30679             box.height -= this.split.el.getHeight();
30680             this.split.el.setLeft(box.x);
30681             this.split.el.setTop(box.y+box.height);
30682             this.split.el.setWidth(box.width);
30683         }
30684         if(this.collapsed){
30685             this.updateBody(box.width, null);
30686         }
30687         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30688     }
30689 });
30690
30691 Roo.SouthLayoutRegion = function(mgr, config){
30692     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30693     if(this.split){
30694         this.split.placement = Roo.SplitBar.BOTTOM;
30695         this.split.orientation = Roo.SplitBar.VERTICAL;
30696         this.split.el.addClass("x-layout-split-v");
30697     }
30698     var size = config.initialSize || config.height;
30699     if(typeof size != "undefined"){
30700         this.el.setHeight(size);
30701     }
30702 };
30703 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30704     orientation: Roo.SplitBar.VERTICAL,
30705     getBox : function(){
30706         if(this.collapsed){
30707             return this.collapsedEl.getBox();
30708         }
30709         var box = this.el.getBox();
30710         if(this.split){
30711             var sh = this.split.el.getHeight();
30712             box.height += sh;
30713             box.y -= sh;
30714         }
30715         return box;
30716     },
30717     
30718     updateBox : function(box){
30719         if(this.split && !this.collapsed){
30720             var sh = this.split.el.getHeight();
30721             box.height -= sh;
30722             box.y += sh;
30723             this.split.el.setLeft(box.x);
30724             this.split.el.setTop(box.y-sh);
30725             this.split.el.setWidth(box.width);
30726         }
30727         if(this.collapsed){
30728             this.updateBody(box.width, null);
30729         }
30730         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30731     }
30732 });
30733
30734 Roo.EastLayoutRegion = function(mgr, config){
30735     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30736     if(this.split){
30737         this.split.placement = Roo.SplitBar.RIGHT;
30738         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30739         this.split.el.addClass("x-layout-split-h");
30740     }
30741     var size = config.initialSize || config.width;
30742     if(typeof size != "undefined"){
30743         this.el.setWidth(size);
30744     }
30745 };
30746 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30747     orientation: Roo.SplitBar.HORIZONTAL,
30748     getBox : function(){
30749         if(this.collapsed){
30750             return this.collapsedEl.getBox();
30751         }
30752         var box = this.el.getBox();
30753         if(this.split){
30754             var sw = this.split.el.getWidth();
30755             box.width += sw;
30756             box.x -= sw;
30757         }
30758         return box;
30759     },
30760
30761     updateBox : function(box){
30762         if(this.split && !this.collapsed){
30763             var sw = this.split.el.getWidth();
30764             box.width -= sw;
30765             this.split.el.setLeft(box.x);
30766             this.split.el.setTop(box.y);
30767             this.split.el.setHeight(box.height);
30768             box.x += sw;
30769         }
30770         if(this.collapsed){
30771             this.updateBody(null, box.height);
30772         }
30773         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30774     }
30775 });
30776
30777 Roo.WestLayoutRegion = function(mgr, config){
30778     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30779     if(this.split){
30780         this.split.placement = Roo.SplitBar.LEFT;
30781         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30782         this.split.el.addClass("x-layout-split-h");
30783     }
30784     var size = config.initialSize || config.width;
30785     if(typeof size != "undefined"){
30786         this.el.setWidth(size);
30787     }
30788 };
30789 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30790     orientation: Roo.SplitBar.HORIZONTAL,
30791     getBox : function(){
30792         if(this.collapsed){
30793             return this.collapsedEl.getBox();
30794         }
30795         var box = this.el.getBox();
30796         if(this.split){
30797             box.width += this.split.el.getWidth();
30798         }
30799         return box;
30800     },
30801     
30802     updateBox : function(box){
30803         if(this.split && !this.collapsed){
30804             var sw = this.split.el.getWidth();
30805             box.width -= sw;
30806             this.split.el.setLeft(box.x+box.width);
30807             this.split.el.setTop(box.y);
30808             this.split.el.setHeight(box.height);
30809         }
30810         if(this.collapsed){
30811             this.updateBody(null, box.height);
30812         }
30813         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30814     }
30815 });
30816 /*
30817  * Based on:
30818  * Ext JS Library 1.1.1
30819  * Copyright(c) 2006-2007, Ext JS, LLC.
30820  *
30821  * Originally Released Under LGPL - original licence link has changed is not relivant.
30822  *
30823  * Fork - LGPL
30824  * <script type="text/javascript">
30825  */
30826  
30827  
30828 /*
30829  * Private internal class for reading and applying state
30830  */
30831 Roo.LayoutStateManager = function(layout){
30832      // default empty state
30833      this.state = {
30834         north: {},
30835         south: {},
30836         east: {},
30837         west: {}       
30838     };
30839 };
30840
30841 Roo.LayoutStateManager.prototype = {
30842     init : function(layout, provider){
30843         this.provider = provider;
30844         var state = provider.get(layout.id+"-layout-state");
30845         if(state){
30846             var wasUpdating = layout.isUpdating();
30847             if(!wasUpdating){
30848                 layout.beginUpdate();
30849             }
30850             for(var key in state){
30851                 if(typeof state[key] != "function"){
30852                     var rstate = state[key];
30853                     var r = layout.getRegion(key);
30854                     if(r && rstate){
30855                         if(rstate.size){
30856                             r.resizeTo(rstate.size);
30857                         }
30858                         if(rstate.collapsed == true){
30859                             r.collapse(true);
30860                         }else{
30861                             r.expand(null, true);
30862                         }
30863                     }
30864                 }
30865             }
30866             if(!wasUpdating){
30867                 layout.endUpdate();
30868             }
30869             this.state = state; 
30870         }
30871         this.layout = layout;
30872         layout.on("regionresized", this.onRegionResized, this);
30873         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30874         layout.on("regionexpanded", this.onRegionExpanded, this);
30875     },
30876     
30877     storeState : function(){
30878         this.provider.set(this.layout.id+"-layout-state", this.state);
30879     },
30880     
30881     onRegionResized : function(region, newSize){
30882         this.state[region.getPosition()].size = newSize;
30883         this.storeState();
30884     },
30885     
30886     onRegionCollapsed : function(region){
30887         this.state[region.getPosition()].collapsed = true;
30888         this.storeState();
30889     },
30890     
30891     onRegionExpanded : function(region){
30892         this.state[region.getPosition()].collapsed = false;
30893         this.storeState();
30894     }
30895 };/*
30896  * Based on:
30897  * Ext JS Library 1.1.1
30898  * Copyright(c) 2006-2007, Ext JS, LLC.
30899  *
30900  * Originally Released Under LGPL - original licence link has changed is not relivant.
30901  *
30902  * Fork - LGPL
30903  * <script type="text/javascript">
30904  */
30905 /**
30906  * @class Roo.ContentPanel
30907  * @extends Roo.util.Observable
30908  * A basic ContentPanel element.
30909  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30910  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30911  * @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
30912  * @cfg {Boolean} closable True if the panel can be closed/removed
30913  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30914  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30915  * @cfg {Toolbar} toolbar A toolbar for this panel
30916  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30917  * @cfg {String} title The title for this panel
30918  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30919  * @cfg {String} url Calls {@link #setUrl} with this value
30920  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30921  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30922  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30923  * @constructor
30924  * Create a new ContentPanel.
30925  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30926  * @param {String/Object} config A string to set only the title or a config object
30927  * @param {String} content (optional) Set the HTML content for this panel
30928  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30929  */
30930 Roo.ContentPanel = function(el, config, content){
30931     
30932      
30933     /*
30934     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30935         config = el;
30936         el = Roo.id();
30937     }
30938     if (config && config.parentLayout) { 
30939         el = config.parentLayout.el.createChild(); 
30940     }
30941     */
30942     if(el.autoCreate){ // xtype is available if this is called from factory
30943         config = el;
30944         el = Roo.id();
30945     }
30946     this.el = Roo.get(el);
30947     if(!this.el && config && config.autoCreate){
30948         if(typeof config.autoCreate == "object"){
30949             if(!config.autoCreate.id){
30950                 config.autoCreate.id = config.id||el;
30951             }
30952             this.el = Roo.DomHelper.append(document.body,
30953                         config.autoCreate, true);
30954         }else{
30955             this.el = Roo.DomHelper.append(document.body,
30956                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30957         }
30958     }
30959     this.closable = false;
30960     this.loaded = false;
30961     this.active = false;
30962     if(typeof config == "string"){
30963         this.title = config;
30964     }else{
30965         Roo.apply(this, config);
30966     }
30967     
30968     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30969         this.wrapEl = this.el.wrap();    
30970         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30971         
30972     }
30973     
30974     
30975     
30976     if(this.resizeEl){
30977         this.resizeEl = Roo.get(this.resizeEl, true);
30978     }else{
30979         this.resizeEl = this.el;
30980     }
30981     this.addEvents({
30982         /**
30983          * @event activate
30984          * Fires when this panel is activated. 
30985          * @param {Roo.ContentPanel} this
30986          */
30987         "activate" : true,
30988         /**
30989          * @event deactivate
30990          * Fires when this panel is activated. 
30991          * @param {Roo.ContentPanel} this
30992          */
30993         "deactivate" : true,
30994
30995         /**
30996          * @event resize
30997          * Fires when this panel is resized if fitToFrame is true.
30998          * @param {Roo.ContentPanel} this
30999          * @param {Number} width The width after any component adjustments
31000          * @param {Number} height The height after any component adjustments
31001          */
31002         "resize" : true
31003     });
31004     if(this.autoScroll){
31005         this.resizeEl.setStyle("overflow", "auto");
31006     } else {
31007         // fix randome scrolling
31008         this.el.on('scroll', function() {
31009             this.scrollTo('top',0); 
31010         });
31011     }
31012     content = content || this.content;
31013     if(content){
31014         this.setContent(content);
31015     }
31016     if(config && config.url){
31017         this.setUrl(this.url, this.params, this.loadOnce);
31018     }
31019     
31020     
31021     
31022     Roo.ContentPanel.superclass.constructor.call(this);
31023 };
31024
31025 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31026     tabTip:'',
31027     setRegion : function(region){
31028         this.region = region;
31029         if(region){
31030            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31031         }else{
31032            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31033         } 
31034     },
31035     
31036     /**
31037      * Returns the toolbar for this Panel if one was configured. 
31038      * @return {Roo.Toolbar} 
31039      */
31040     getToolbar : function(){
31041         return this.toolbar;
31042     },
31043     
31044     setActiveState : function(active){
31045         this.active = active;
31046         if(!active){
31047             this.fireEvent("deactivate", this);
31048         }else{
31049             this.fireEvent("activate", this);
31050         }
31051     },
31052     /**
31053      * Updates this panel's element
31054      * @param {String} content The new content
31055      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31056     */
31057     setContent : function(content, loadScripts){
31058         this.el.update(content, loadScripts);
31059     },
31060
31061     ignoreResize : function(w, h){
31062         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31063             return true;
31064         }else{
31065             this.lastSize = {width: w, height: h};
31066             return false;
31067         }
31068     },
31069     /**
31070      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31071      * @return {Roo.UpdateManager} The UpdateManager
31072      */
31073     getUpdateManager : function(){
31074         return this.el.getUpdateManager();
31075     },
31076      /**
31077      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31078      * @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:
31079 <pre><code>
31080 panel.load({
31081     url: "your-url.php",
31082     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31083     callback: yourFunction,
31084     scope: yourObject, //(optional scope)
31085     discardUrl: false,
31086     nocache: false,
31087     text: "Loading...",
31088     timeout: 30,
31089     scripts: false
31090 });
31091 </code></pre>
31092      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31093      * 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.
31094      * @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}
31095      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31096      * @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.
31097      * @return {Roo.ContentPanel} this
31098      */
31099     load : function(){
31100         var um = this.el.getUpdateManager();
31101         um.update.apply(um, arguments);
31102         return this;
31103     },
31104
31105
31106     /**
31107      * 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.
31108      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31109      * @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)
31110      * @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)
31111      * @return {Roo.UpdateManager} The UpdateManager
31112      */
31113     setUrl : function(url, params, loadOnce){
31114         if(this.refreshDelegate){
31115             this.removeListener("activate", this.refreshDelegate);
31116         }
31117         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31118         this.on("activate", this.refreshDelegate);
31119         return this.el.getUpdateManager();
31120     },
31121     
31122     _handleRefresh : function(url, params, loadOnce){
31123         if(!loadOnce || !this.loaded){
31124             var updater = this.el.getUpdateManager();
31125             updater.update(url, params, this._setLoaded.createDelegate(this));
31126         }
31127     },
31128     
31129     _setLoaded : function(){
31130         this.loaded = true;
31131     }, 
31132     
31133     /**
31134      * Returns this panel's id
31135      * @return {String} 
31136      */
31137     getId : function(){
31138         return this.el.id;
31139     },
31140     
31141     /** 
31142      * Returns this panel's element - used by regiosn to add.
31143      * @return {Roo.Element} 
31144      */
31145     getEl : function(){
31146         return this.wrapEl || this.el;
31147     },
31148     
31149     adjustForComponents : function(width, height){
31150         if(this.resizeEl != this.el){
31151             width -= this.el.getFrameWidth('lr');
31152             height -= this.el.getFrameWidth('tb');
31153         }
31154         if(this.toolbar){
31155             var te = this.toolbar.getEl();
31156             height -= te.getHeight();
31157             te.setWidth(width);
31158         }
31159         if(this.adjustments){
31160             width += this.adjustments[0];
31161             height += this.adjustments[1];
31162         }
31163         return {"width": width, "height": height};
31164     },
31165     
31166     setSize : function(width, height){
31167         if(this.fitToFrame && !this.ignoreResize(width, height)){
31168             if(this.fitContainer && this.resizeEl != this.el){
31169                 this.el.setSize(width, height);
31170             }
31171             var size = this.adjustForComponents(width, height);
31172             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31173             this.fireEvent('resize', this, size.width, size.height);
31174         }
31175     },
31176     
31177     /**
31178      * Returns this panel's title
31179      * @return {String} 
31180      */
31181     getTitle : function(){
31182         return this.title;
31183     },
31184     
31185     /**
31186      * Set this panel's title
31187      * @param {String} title
31188      */
31189     setTitle : function(title){
31190         this.title = title;
31191         if(this.region){
31192             this.region.updatePanelTitle(this, title);
31193         }
31194     },
31195     
31196     /**
31197      * Returns true is this panel was configured to be closable
31198      * @return {Boolean} 
31199      */
31200     isClosable : function(){
31201         return this.closable;
31202     },
31203     
31204     beforeSlide : function(){
31205         this.el.clip();
31206         this.resizeEl.clip();
31207     },
31208     
31209     afterSlide : function(){
31210         this.el.unclip();
31211         this.resizeEl.unclip();
31212     },
31213     
31214     /**
31215      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31216      *   Will fail silently if the {@link #setUrl} method has not been called.
31217      *   This does not activate the panel, just updates its content.
31218      */
31219     refresh : function(){
31220         if(this.refreshDelegate){
31221            this.loaded = false;
31222            this.refreshDelegate();
31223         }
31224     },
31225     
31226     /**
31227      * Destroys this panel
31228      */
31229     destroy : function(){
31230         this.el.removeAllListeners();
31231         var tempEl = document.createElement("span");
31232         tempEl.appendChild(this.el.dom);
31233         tempEl.innerHTML = "";
31234         this.el.remove();
31235         this.el = null;
31236     },
31237     
31238       /**
31239      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31240      * <pre><code>
31241
31242 layout.addxtype({
31243        xtype : 'Form',
31244        items: [ .... ]
31245    }
31246 );
31247
31248 </code></pre>
31249      * @param {Object} cfg Xtype definition of item to add.
31250      */
31251     
31252     addxtype : function(cfg) {
31253         // add form..
31254         if (cfg.xtype.match(/^Form$/)) {
31255             var el = this.el.createChild();
31256
31257             this.form = new  Roo.form.Form(cfg);
31258             
31259             
31260             if ( this.form.allItems.length) this.form.render(el.dom);
31261             return this.form;
31262         }
31263         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31264             // views..
31265             cfg.el = this.el.appendChild(document.createElement("div"));
31266             // factory?
31267             var ret = new Roo[cfg.xtype](cfg);
31268             ret.render(false, ''); // render blank..
31269             return ret;
31270             
31271         }
31272         return false;
31273         
31274     }
31275 });
31276
31277 /**
31278  * @class Roo.GridPanel
31279  * @extends Roo.ContentPanel
31280  * @constructor
31281  * Create a new GridPanel.
31282  * @param {Roo.grid.Grid} grid The grid for this panel
31283  * @param {String/Object} config A string to set only the panel's title, or a config object
31284  */
31285 Roo.GridPanel = function(grid, config){
31286     
31287   
31288     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31289         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31290         
31291     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31292     
31293     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31294     
31295     if(this.toolbar){
31296         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31297     }
31298     // xtype created footer. - not sure if will work as we normally have to render first..
31299     if (this.footer && !this.footer.el && this.footer.xtype) {
31300         
31301         this.footer.container = this.grid.getView().getFooterPanel(true);
31302         this.footer.dataSource = this.grid.dataSource;
31303         this.footer = Roo.factory(this.footer, Roo);
31304         
31305     }
31306     
31307     grid.monitorWindowResize = false; // turn off autosizing
31308     grid.autoHeight = false;
31309     grid.autoWidth = false;
31310     this.grid = grid;
31311     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31312 };
31313
31314 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31315     getId : function(){
31316         return this.grid.id;
31317     },
31318     
31319     /**
31320      * Returns the grid for this panel
31321      * @return {Roo.grid.Grid} 
31322      */
31323     getGrid : function(){
31324         return this.grid;    
31325     },
31326     
31327     setSize : function(width, height){
31328         if(!this.ignoreResize(width, height)){
31329             var grid = this.grid;
31330             var size = this.adjustForComponents(width, height);
31331             grid.getGridEl().setSize(size.width, size.height);
31332             grid.autoSize();
31333         }
31334     },
31335     
31336     beforeSlide : function(){
31337         this.grid.getView().scroller.clip();
31338     },
31339     
31340     afterSlide : function(){
31341         this.grid.getView().scroller.unclip();
31342     },
31343     
31344     destroy : function(){
31345         this.grid.destroy();
31346         delete this.grid;
31347         Roo.GridPanel.superclass.destroy.call(this); 
31348     }
31349 });
31350
31351
31352 /**
31353  * @class Roo.NestedLayoutPanel
31354  * @extends Roo.ContentPanel
31355  * @constructor
31356  * Create a new NestedLayoutPanel.
31357  * 
31358  * 
31359  * @param {Roo.BorderLayout} layout The layout for this panel
31360  * @param {String/Object} config A string to set only the title or a config object
31361  */
31362 Roo.NestedLayoutPanel = function(layout, config)
31363 {
31364     // construct with only one argument..
31365     /* FIXME - implement nicer consturctors
31366     if (layout.layout) {
31367         config = layout;
31368         layout = config.layout;
31369         delete config.layout;
31370     }
31371     if (layout.xtype && !layout.getEl) {
31372         // then layout needs constructing..
31373         layout = Roo.factory(layout, Roo);
31374     }
31375     */
31376     
31377     
31378     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31379     
31380     layout.monitorWindowResize = false; // turn off autosizing
31381     this.layout = layout;
31382     this.layout.getEl().addClass("x-layout-nested-layout");
31383     
31384     
31385     
31386     
31387 };
31388
31389 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31390
31391     setSize : function(width, height){
31392         if(!this.ignoreResize(width, height)){
31393             var size = this.adjustForComponents(width, height);
31394             var el = this.layout.getEl();
31395             el.setSize(size.width, size.height);
31396             var touch = el.dom.offsetWidth;
31397             this.layout.layout();
31398             // ie requires a double layout on the first pass
31399             if(Roo.isIE && !this.initialized){
31400                 this.initialized = true;
31401                 this.layout.layout();
31402             }
31403         }
31404     },
31405     
31406     // activate all subpanels if not currently active..
31407     
31408     setActiveState : function(active){
31409         this.active = active;
31410         if(!active){
31411             this.fireEvent("deactivate", this);
31412             return;
31413         }
31414         
31415         this.fireEvent("activate", this);
31416         // not sure if this should happen before or after..
31417         if (!this.layout) {
31418             return; // should not happen..
31419         }
31420         var reg = false;
31421         for (var r in this.layout.regions) {
31422             reg = this.layout.getRegion(r);
31423             if (reg.getActivePanel()) {
31424                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31425                 reg.setActivePanel(reg.getActivePanel());
31426                 continue;
31427             }
31428             if (!reg.panels.length) {
31429                 continue;
31430             }
31431             reg.showPanel(reg.getPanel(0));
31432         }
31433         
31434         
31435         
31436         
31437     },
31438     
31439     /**
31440      * Returns the nested BorderLayout for this panel
31441      * @return {Roo.BorderLayout} 
31442      */
31443     getLayout : function(){
31444         return this.layout;
31445     },
31446     
31447      /**
31448      * Adds a xtype elements to the layout of the nested panel
31449      * <pre><code>
31450
31451 panel.addxtype({
31452        xtype : 'ContentPanel',
31453        region: 'west',
31454        items: [ .... ]
31455    }
31456 );
31457
31458 panel.addxtype({
31459         xtype : 'NestedLayoutPanel',
31460         region: 'west',
31461         layout: {
31462            center: { },
31463            west: { }   
31464         },
31465         items : [ ... list of content panels or nested layout panels.. ]
31466    }
31467 );
31468 </code></pre>
31469      * @param {Object} cfg Xtype definition of item to add.
31470      */
31471     addxtype : function(cfg) {
31472         return this.layout.addxtype(cfg);
31473     
31474     }
31475 });
31476
31477 Roo.ScrollPanel = function(el, config, content){
31478     config = config || {};
31479     config.fitToFrame = true;
31480     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31481     
31482     this.el.dom.style.overflow = "hidden";
31483     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31484     this.el.removeClass("x-layout-inactive-content");
31485     this.el.on("mousewheel", this.onWheel, this);
31486
31487     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31488     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31489     up.unselectable(); down.unselectable();
31490     up.on("click", this.scrollUp, this);
31491     down.on("click", this.scrollDown, this);
31492     up.addClassOnOver("x-scroller-btn-over");
31493     down.addClassOnOver("x-scroller-btn-over");
31494     up.addClassOnClick("x-scroller-btn-click");
31495     down.addClassOnClick("x-scroller-btn-click");
31496     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31497
31498     this.resizeEl = this.el;
31499     this.el = wrap; this.up = up; this.down = down;
31500 };
31501
31502 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31503     increment : 100,
31504     wheelIncrement : 5,
31505     scrollUp : function(){
31506         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31507     },
31508
31509     scrollDown : function(){
31510         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31511     },
31512
31513     afterScroll : function(){
31514         var el = this.resizeEl;
31515         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31516         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31517         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31518     },
31519
31520     setSize : function(){
31521         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31522         this.afterScroll();
31523     },
31524
31525     onWheel : function(e){
31526         var d = e.getWheelDelta();
31527         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31528         this.afterScroll();
31529         e.stopEvent();
31530     },
31531
31532     setContent : function(content, loadScripts){
31533         this.resizeEl.update(content, loadScripts);
31534     }
31535
31536 });
31537
31538
31539
31540
31541
31542
31543
31544
31545
31546 /**
31547  * @class Roo.TreePanel
31548  * @extends Roo.ContentPanel
31549  * @constructor
31550  * Create a new TreePanel. - defaults to fit/scoll contents.
31551  * @param {String/Object} config A string to set only the panel's title, or a config object
31552  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31553  */
31554 Roo.TreePanel = function(config){
31555     var el = config.el;
31556     var tree = config.tree;
31557     delete config.tree; 
31558     delete config.el; // hopefull!
31559     
31560     // wrapper for IE7 strict & safari scroll issue
31561     
31562     var treeEl = el.createChild();
31563     config.resizeEl = treeEl;
31564     
31565     
31566     
31567     Roo.TreePanel.superclass.constructor.call(this, el, config);
31568  
31569  
31570     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31571     //console.log(tree);
31572     this.on('activate', function()
31573     {
31574         if (this.tree.rendered) {
31575             return;
31576         }
31577         //console.log('render tree');
31578         this.tree.render();
31579     });
31580     
31581     this.on('resize',  function (cp, w, h) {
31582             this.tree.innerCt.setWidth(w);
31583             this.tree.innerCt.setHeight(h);
31584             this.tree.innerCt.setStyle('overflow-y', 'auto');
31585     });
31586
31587         
31588     
31589 };
31590
31591 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31592     fitToFrame : true,
31593     autoScroll : true
31594 });
31595
31596
31597
31598
31599
31600
31601
31602
31603
31604
31605
31606 /*
31607  * Based on:
31608  * Ext JS Library 1.1.1
31609  * Copyright(c) 2006-2007, Ext JS, LLC.
31610  *
31611  * Originally Released Under LGPL - original licence link has changed is not relivant.
31612  *
31613  * Fork - LGPL
31614  * <script type="text/javascript">
31615  */
31616  
31617
31618 /**
31619  * @class Roo.ReaderLayout
31620  * @extends Roo.BorderLayout
31621  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31622  * center region containing two nested regions (a top one for a list view and one for item preview below),
31623  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31624  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31625  * expedites the setup of the overall layout and regions for this common application style.
31626  * Example:
31627  <pre><code>
31628 var reader = new Roo.ReaderLayout();
31629 var CP = Roo.ContentPanel;  // shortcut for adding
31630
31631 reader.beginUpdate();
31632 reader.add("north", new CP("north", "North"));
31633 reader.add("west", new CP("west", {title: "West"}));
31634 reader.add("east", new CP("east", {title: "East"}));
31635
31636 reader.regions.listView.add(new CP("listView", "List"));
31637 reader.regions.preview.add(new CP("preview", "Preview"));
31638 reader.endUpdate();
31639 </code></pre>
31640 * @constructor
31641 * Create a new ReaderLayout
31642 * @param {Object} config Configuration options
31643 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31644 * document.body if omitted)
31645 */
31646 Roo.ReaderLayout = function(config, renderTo){
31647     var c = config || {size:{}};
31648     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31649         north: c.north !== false ? Roo.apply({
31650             split:false,
31651             initialSize: 32,
31652             titlebar: false
31653         }, c.north) : false,
31654         west: c.west !== false ? Roo.apply({
31655             split:true,
31656             initialSize: 200,
31657             minSize: 175,
31658             maxSize: 400,
31659             titlebar: true,
31660             collapsible: true,
31661             animate: true,
31662             margins:{left:5,right:0,bottom:5,top:5},
31663             cmargins:{left:5,right:5,bottom:5,top:5}
31664         }, c.west) : false,
31665         east: c.east !== false ? Roo.apply({
31666             split:true,
31667             initialSize: 200,
31668             minSize: 175,
31669             maxSize: 400,
31670             titlebar: true,
31671             collapsible: true,
31672             animate: true,
31673             margins:{left:0,right:5,bottom:5,top:5},
31674             cmargins:{left:5,right:5,bottom:5,top:5}
31675         }, c.east) : false,
31676         center: Roo.apply({
31677             tabPosition: 'top',
31678             autoScroll:false,
31679             closeOnTab: true,
31680             titlebar:false,
31681             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31682         }, c.center)
31683     });
31684
31685     this.el.addClass('x-reader');
31686
31687     this.beginUpdate();
31688
31689     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31690         south: c.preview !== false ? Roo.apply({
31691             split:true,
31692             initialSize: 200,
31693             minSize: 100,
31694             autoScroll:true,
31695             collapsible:true,
31696             titlebar: true,
31697             cmargins:{top:5,left:0, right:0, bottom:0}
31698         }, c.preview) : false,
31699         center: Roo.apply({
31700             autoScroll:false,
31701             titlebar:false,
31702             minHeight:200
31703         }, c.listView)
31704     });
31705     this.add('center', new Roo.NestedLayoutPanel(inner,
31706             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31707
31708     this.endUpdate();
31709
31710     this.regions.preview = inner.getRegion('south');
31711     this.regions.listView = inner.getRegion('center');
31712 };
31713
31714 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31715  * Based on:
31716  * Ext JS Library 1.1.1
31717  * Copyright(c) 2006-2007, Ext JS, LLC.
31718  *
31719  * Originally Released Under LGPL - original licence link has changed is not relivant.
31720  *
31721  * Fork - LGPL
31722  * <script type="text/javascript">
31723  */
31724  
31725 /**
31726  * @class Roo.grid.Grid
31727  * @extends Roo.util.Observable
31728  * This class represents the primary interface of a component based grid control.
31729  * <br><br>Usage:<pre><code>
31730  var grid = new Roo.grid.Grid("my-container-id", {
31731      ds: myDataStore,
31732      cm: myColModel,
31733      selModel: mySelectionModel,
31734      autoSizeColumns: true,
31735      monitorWindowResize: false,
31736      trackMouseOver: true
31737  });
31738  // set any options
31739  grid.render();
31740  * </code></pre>
31741  * <b>Common Problems:</b><br/>
31742  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31743  * element will correct this<br/>
31744  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31745  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31746  * are unpredictable.<br/>
31747  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31748  * grid to calculate dimensions/offsets.<br/>
31749   * @constructor
31750  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31751  * The container MUST have some type of size defined for the grid to fill. The container will be
31752  * automatically set to position relative if it isn't already.
31753  * @param {Object} config A config object that sets properties on this grid.
31754  */
31755 Roo.grid.Grid = function(container, config){
31756         // initialize the container
31757         this.container = Roo.get(container);
31758         this.container.update("");
31759         this.container.setStyle("overflow", "hidden");
31760     this.container.addClass('x-grid-container');
31761
31762     this.id = this.container.id;
31763
31764     Roo.apply(this, config);
31765     // check and correct shorthanded configs
31766     if(this.ds){
31767         this.dataSource = this.ds;
31768         delete this.ds;
31769     }
31770     if(this.cm){
31771         this.colModel = this.cm;
31772         delete this.cm;
31773     }
31774     if(this.sm){
31775         this.selModel = this.sm;
31776         delete this.sm;
31777     }
31778
31779     if (this.selModel) {
31780         this.selModel = Roo.factory(this.selModel, Roo.grid);
31781         this.sm = this.selModel;
31782         this.sm.xmodule = this.xmodule || false;
31783     }
31784     if (typeof(this.colModel.config) == 'undefined') {
31785         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31786         this.cm = this.colModel;
31787         this.cm.xmodule = this.xmodule || false;
31788     }
31789     if (this.dataSource) {
31790         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31791         this.ds = this.dataSource;
31792         this.ds.xmodule = this.xmodule || false;
31793         
31794     }
31795     
31796     
31797     
31798     if(this.width){
31799         this.container.setWidth(this.width);
31800     }
31801
31802     if(this.height){
31803         this.container.setHeight(this.height);
31804     }
31805     /** @private */
31806         this.addEvents({
31807             // raw events
31808             /**
31809              * @event click
31810              * The raw click event for the entire grid.
31811              * @param {Roo.EventObject} e
31812              */
31813             "click" : true,
31814             /**
31815              * @event dblclick
31816              * The raw dblclick event for the entire grid.
31817              * @param {Roo.EventObject} e
31818              */
31819             "dblclick" : true,
31820             /**
31821              * @event contextmenu
31822              * The raw contextmenu event for the entire grid.
31823              * @param {Roo.EventObject} e
31824              */
31825             "contextmenu" : true,
31826             /**
31827              * @event mousedown
31828              * The raw mousedown event for the entire grid.
31829              * @param {Roo.EventObject} e
31830              */
31831             "mousedown" : true,
31832             /**
31833              * @event mouseup
31834              * The raw mouseup event for the entire grid.
31835              * @param {Roo.EventObject} e
31836              */
31837             "mouseup" : true,
31838             /**
31839              * @event mouseover
31840              * The raw mouseover event for the entire grid.
31841              * @param {Roo.EventObject} e
31842              */
31843             "mouseover" : true,
31844             /**
31845              * @event mouseout
31846              * The raw mouseout event for the entire grid.
31847              * @param {Roo.EventObject} e
31848              */
31849             "mouseout" : true,
31850             /**
31851              * @event keypress
31852              * The raw keypress event for the entire grid.
31853              * @param {Roo.EventObject} e
31854              */
31855             "keypress" : true,
31856             /**
31857              * @event keydown
31858              * The raw keydown event for the entire grid.
31859              * @param {Roo.EventObject} e
31860              */
31861             "keydown" : true,
31862
31863             // custom events
31864
31865             /**
31866              * @event cellclick
31867              * Fires when a cell is clicked
31868              * @param {Grid} this
31869              * @param {Number} rowIndex
31870              * @param {Number} columnIndex
31871              * @param {Roo.EventObject} e
31872              */
31873             "cellclick" : true,
31874             /**
31875              * @event celldblclick
31876              * Fires when a cell is double clicked
31877              * @param {Grid} this
31878              * @param {Number} rowIndex
31879              * @param {Number} columnIndex
31880              * @param {Roo.EventObject} e
31881              */
31882             "celldblclick" : true,
31883             /**
31884              * @event rowclick
31885              * Fires when a row is clicked
31886              * @param {Grid} this
31887              * @param {Number} rowIndex
31888              * @param {Roo.EventObject} e
31889              */
31890             "rowclick" : true,
31891             /**
31892              * @event rowdblclick
31893              * Fires when a row is double clicked
31894              * @param {Grid} this
31895              * @param {Number} rowIndex
31896              * @param {Roo.EventObject} e
31897              */
31898             "rowdblclick" : true,
31899             /**
31900              * @event headerclick
31901              * Fires when a header is clicked
31902              * @param {Grid} this
31903              * @param {Number} columnIndex
31904              * @param {Roo.EventObject} e
31905              */
31906             "headerclick" : true,
31907             /**
31908              * @event headerdblclick
31909              * Fires when a header cell is double clicked
31910              * @param {Grid} this
31911              * @param {Number} columnIndex
31912              * @param {Roo.EventObject} e
31913              */
31914             "headerdblclick" : true,
31915             /**
31916              * @event rowcontextmenu
31917              * Fires when a row is right clicked
31918              * @param {Grid} this
31919              * @param {Number} rowIndex
31920              * @param {Roo.EventObject} e
31921              */
31922             "rowcontextmenu" : true,
31923             /**
31924          * @event cellcontextmenu
31925          * Fires when a cell is right clicked
31926          * @param {Grid} this
31927          * @param {Number} rowIndex
31928          * @param {Number} cellIndex
31929          * @param {Roo.EventObject} e
31930          */
31931          "cellcontextmenu" : true,
31932             /**
31933              * @event headercontextmenu
31934              * Fires when a header is right clicked
31935              * @param {Grid} this
31936              * @param {Number} columnIndex
31937              * @param {Roo.EventObject} e
31938              */
31939             "headercontextmenu" : true,
31940             /**
31941              * @event bodyscroll
31942              * Fires when the body element is scrolled
31943              * @param {Number} scrollLeft
31944              * @param {Number} scrollTop
31945              */
31946             "bodyscroll" : true,
31947             /**
31948              * @event columnresize
31949              * Fires when the user resizes a column
31950              * @param {Number} columnIndex
31951              * @param {Number} newSize
31952              */
31953             "columnresize" : true,
31954             /**
31955              * @event columnmove
31956              * Fires when the user moves a column
31957              * @param {Number} oldIndex
31958              * @param {Number} newIndex
31959              */
31960             "columnmove" : true,
31961             /**
31962              * @event startdrag
31963              * Fires when row(s) start being dragged
31964              * @param {Grid} this
31965              * @param {Roo.GridDD} dd The drag drop object
31966              * @param {event} e The raw browser event
31967              */
31968             "startdrag" : true,
31969             /**
31970              * @event enddrag
31971              * Fires when a drag operation is complete
31972              * @param {Grid} this
31973              * @param {Roo.GridDD} dd The drag drop object
31974              * @param {event} e The raw browser event
31975              */
31976             "enddrag" : true,
31977             /**
31978              * @event dragdrop
31979              * Fires when dragged row(s) are dropped on a valid DD target
31980              * @param {Grid} this
31981              * @param {Roo.GridDD} dd The drag drop object
31982              * @param {String} targetId The target drag drop object
31983              * @param {event} e The raw browser event
31984              */
31985             "dragdrop" : true,
31986             /**
31987              * @event dragover
31988              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31989              * @param {Grid} this
31990              * @param {Roo.GridDD} dd The drag drop object
31991              * @param {String} targetId The target drag drop object
31992              * @param {event} e The raw browser event
31993              */
31994             "dragover" : true,
31995             /**
31996              * @event dragenter
31997              *  Fires when the dragged row(s) first cross another DD target while being dragged
31998              * @param {Grid} this
31999              * @param {Roo.GridDD} dd The drag drop object
32000              * @param {String} targetId The target drag drop object
32001              * @param {event} e The raw browser event
32002              */
32003             "dragenter" : true,
32004             /**
32005              * @event dragout
32006              * Fires when the dragged row(s) leave another DD target while being dragged
32007              * @param {Grid} this
32008              * @param {Roo.GridDD} dd The drag drop object
32009              * @param {String} targetId The target drag drop object
32010              * @param {event} e The raw browser event
32011              */
32012             "dragout" : true,
32013         /**
32014          * @event render
32015          * Fires when the grid is rendered
32016          * @param {Grid} grid
32017          */
32018         render : true
32019     });
32020
32021     Roo.grid.Grid.superclass.constructor.call(this);
32022 };
32023 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32024     
32025     /**
32026      * @cfg {String} ddGroup - drag drop group.
32027          */
32028     
32029     /**
32030      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32031          */
32032         minColumnWidth : 25,
32033
32034     /**
32035          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32036          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32037          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32038          */
32039         autoSizeColumns : false,
32040
32041         /**
32042          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32043          */
32044         autoSizeHeaders : true,
32045
32046         /**
32047          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32048          */
32049         monitorWindowResize : true,
32050
32051         /**
32052          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32053          * rows measured to get a columns size. Default is 0 (all rows).
32054          */
32055         maxRowsToMeasure : 0,
32056
32057         /**
32058          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32059          */
32060         trackMouseOver : true,
32061
32062     /**
32063          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32064          */
32065     
32066         /**
32067          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32068          */
32069         enableDragDrop : false,
32070
32071         /**
32072          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32073          */
32074         enableColumnMove : true,
32075
32076         /**
32077          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32078          */
32079         enableColumnHide : true,
32080
32081         /**
32082          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32083          */
32084         enableRowHeightSync : false,
32085
32086         /**
32087          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32088          */
32089         stripeRows : true,
32090
32091         /**
32092          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32093          */
32094         autoHeight : false,
32095
32096     /**
32097      * @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.
32098      */
32099     autoExpandColumn : false,
32100
32101     /**
32102     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32103     * Default is 50.
32104     */
32105     autoExpandMin : 50,
32106
32107     /**
32108     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32109     */
32110     autoExpandMax : 1000,
32111
32112     /**
32113          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32114          */
32115         view : null,
32116
32117         /**
32118      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32119          */
32120         loadMask : false,
32121     /**
32122      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32123          */
32124         dropTarget: false,
32125     // private
32126     rendered : false,
32127
32128     /**
32129     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32130     * of a fixed width. Default is false.
32131     */
32132     /**
32133     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32134     */
32135     /**
32136      * Called once after all setup has been completed and the grid is ready to be rendered.
32137      * @return {Roo.grid.Grid} this
32138      */
32139     render : function(){
32140         var c = this.container;
32141         // try to detect autoHeight/width mode
32142         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32143             this.autoHeight = true;
32144         }
32145         var view = this.getView();
32146         view.init(this);
32147
32148         c.on("click", this.onClick, this);
32149         c.on("dblclick", this.onDblClick, this);
32150         c.on("contextmenu", this.onContextMenu, this);
32151         c.on("keydown", this.onKeyDown, this);
32152
32153         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32154
32155         this.getSelectionModel().init(this);
32156
32157         view.render();
32158
32159         if(this.loadMask){
32160             this.loadMask = new Roo.LoadMask(this.container,
32161                     Roo.apply({store:this.dataSource}, this.loadMask));
32162         }
32163         
32164         
32165         if (this.toolbar && this.toolbar.xtype) {
32166             this.toolbar.container = this.getView().getHeaderPanel(true);
32167             this.toolbar = new Ext.Toolbar(this.toolbar);
32168         }
32169         if (this.footer && this.footer.xtype) {
32170             this.footer.dataSource = this.getDataSource();
32171             this.footer.container = this.getView().getFooterPanel(true);
32172             this.footer = Roo.factory(this.footer, Roo);
32173         }
32174         if (this.dropTarget && this.dropTarget.xtype) {
32175             delete this.dropTarget.xtype;
32176             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32177         }
32178         
32179         
32180         this.rendered = true;
32181         this.fireEvent('render', this);
32182         return this;
32183     },
32184
32185         /**
32186          * Reconfigures the grid to use a different Store and Column Model.
32187          * The View will be bound to the new objects and refreshed.
32188          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32189          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32190          */
32191     reconfigure : function(dataSource, colModel){
32192         if(this.loadMask){
32193             this.loadMask.destroy();
32194             this.loadMask = new Roo.LoadMask(this.container,
32195                     Roo.apply({store:dataSource}, this.loadMask));
32196         }
32197         this.view.bind(dataSource, colModel);
32198         this.dataSource = dataSource;
32199         this.colModel = colModel;
32200         this.view.refresh(true);
32201     },
32202
32203     // private
32204     onKeyDown : function(e){
32205         this.fireEvent("keydown", e);
32206     },
32207
32208     /**
32209      * Destroy this grid.
32210      * @param {Boolean} removeEl True to remove the element
32211      */
32212     destroy : function(removeEl, keepListeners){
32213         if(this.loadMask){
32214             this.loadMask.destroy();
32215         }
32216         var c = this.container;
32217         c.removeAllListeners();
32218         this.view.destroy();
32219         this.colModel.purgeListeners();
32220         if(!keepListeners){
32221             this.purgeListeners();
32222         }
32223         c.update("");
32224         if(removeEl === true){
32225             c.remove();
32226         }
32227     },
32228
32229     // private
32230     processEvent : function(name, e){
32231         this.fireEvent(name, e);
32232         var t = e.getTarget();
32233         var v = this.view;
32234         var header = v.findHeaderIndex(t);
32235         if(header !== false){
32236             this.fireEvent("header" + name, this, header, e);
32237         }else{
32238             var row = v.findRowIndex(t);
32239             var cell = v.findCellIndex(t);
32240             if(row !== false){
32241                 this.fireEvent("row" + name, this, row, e);
32242                 if(cell !== false){
32243                     this.fireEvent("cell" + name, this, row, cell, e);
32244                 }
32245             }
32246         }
32247     },
32248
32249     // private
32250     onClick : function(e){
32251         this.processEvent("click", e);
32252     },
32253
32254     // private
32255     onContextMenu : function(e, t){
32256         this.processEvent("contextmenu", e);
32257     },
32258
32259     // private
32260     onDblClick : function(e){
32261         this.processEvent("dblclick", e);
32262     },
32263
32264     // private
32265     walkCells : function(row, col, step, fn, scope){
32266         var cm = this.colModel, clen = cm.getColumnCount();
32267         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32268         if(step < 0){
32269             if(col < 0){
32270                 row--;
32271                 first = false;
32272             }
32273             while(row >= 0){
32274                 if(!first){
32275                     col = clen-1;
32276                 }
32277                 first = false;
32278                 while(col >= 0){
32279                     if(fn.call(scope || this, row, col, cm) === true){
32280                         return [row, col];
32281                     }
32282                     col--;
32283                 }
32284                 row--;
32285             }
32286         } else {
32287             if(col >= clen){
32288                 row++;
32289                 first = false;
32290             }
32291             while(row < rlen){
32292                 if(!first){
32293                     col = 0;
32294                 }
32295                 first = false;
32296                 while(col < clen){
32297                     if(fn.call(scope || this, row, col, cm) === true){
32298                         return [row, col];
32299                     }
32300                     col++;
32301                 }
32302                 row++;
32303             }
32304         }
32305         return null;
32306     },
32307
32308     // private
32309     getSelections : function(){
32310         return this.selModel.getSelections();
32311     },
32312
32313     /**
32314      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32315      * but if manual update is required this method will initiate it.
32316      */
32317     autoSize : function(){
32318         if(this.rendered){
32319             this.view.layout();
32320             if(this.view.adjustForScroll){
32321                 this.view.adjustForScroll();
32322             }
32323         }
32324     },
32325
32326     /**
32327      * Returns the grid's underlying element.
32328      * @return {Element} The element
32329      */
32330     getGridEl : function(){
32331         return this.container;
32332     },
32333
32334     // private for compatibility, overridden by editor grid
32335     stopEditing : function(){},
32336
32337     /**
32338      * Returns the grid's SelectionModel.
32339      * @return {SelectionModel}
32340      */
32341     getSelectionModel : function(){
32342         if(!this.selModel){
32343             this.selModel = new Roo.grid.RowSelectionModel();
32344         }
32345         return this.selModel;
32346     },
32347
32348     /**
32349      * Returns the grid's DataSource.
32350      * @return {DataSource}
32351      */
32352     getDataSource : function(){
32353         return this.dataSource;
32354     },
32355
32356     /**
32357      * Returns the grid's ColumnModel.
32358      * @return {ColumnModel}
32359      */
32360     getColumnModel : function(){
32361         return this.colModel;
32362     },
32363
32364     /**
32365      * Returns the grid's GridView object.
32366      * @return {GridView}
32367      */
32368     getView : function(){
32369         if(!this.view){
32370             this.view = new Roo.grid.GridView(this.viewConfig);
32371         }
32372         return this.view;
32373     },
32374     /**
32375      * Called to get grid's drag proxy text, by default returns this.ddText.
32376      * @return {String}
32377      */
32378     getDragDropText : function(){
32379         var count = this.selModel.getCount();
32380         return String.format(this.ddText, count, count == 1 ? '' : 's');
32381     }
32382 });
32383 /**
32384  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32385  * %0 is replaced with the number of selected rows.
32386  * @type String
32387  */
32388 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32389  * Based on:
32390  * Ext JS Library 1.1.1
32391  * Copyright(c) 2006-2007, Ext JS, LLC.
32392  *
32393  * Originally Released Under LGPL - original licence link has changed is not relivant.
32394  *
32395  * Fork - LGPL
32396  * <script type="text/javascript">
32397  */
32398  
32399 Roo.grid.AbstractGridView = function(){
32400         this.grid = null;
32401         
32402         this.events = {
32403             "beforerowremoved" : true,
32404             "beforerowsinserted" : true,
32405             "beforerefresh" : true,
32406             "rowremoved" : true,
32407             "rowsinserted" : true,
32408             "rowupdated" : true,
32409             "refresh" : true
32410         };
32411     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32412 };
32413
32414 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32415     rowClass : "x-grid-row",
32416     cellClass : "x-grid-cell",
32417     tdClass : "x-grid-td",
32418     hdClass : "x-grid-hd",
32419     splitClass : "x-grid-hd-split",
32420     
32421         init: function(grid){
32422         this.grid = grid;
32423                 var cid = this.grid.getGridEl().id;
32424         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32425         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32426         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32427         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32428         },
32429         
32430         getColumnRenderers : function(){
32431         var renderers = [];
32432         var cm = this.grid.colModel;
32433         var colCount = cm.getColumnCount();
32434         for(var i = 0; i < colCount; i++){
32435             renderers[i] = cm.getRenderer(i);
32436         }
32437         return renderers;
32438     },
32439     
32440     getColumnIds : function(){
32441         var ids = [];
32442         var cm = this.grid.colModel;
32443         var colCount = cm.getColumnCount();
32444         for(var i = 0; i < colCount; i++){
32445             ids[i] = cm.getColumnId(i);
32446         }
32447         return ids;
32448     },
32449     
32450     getDataIndexes : function(){
32451         if(!this.indexMap){
32452             this.indexMap = this.buildIndexMap();
32453         }
32454         return this.indexMap.colToData;
32455     },
32456     
32457     getColumnIndexByDataIndex : function(dataIndex){
32458         if(!this.indexMap){
32459             this.indexMap = this.buildIndexMap();
32460         }
32461         return this.indexMap.dataToCol[dataIndex];
32462     },
32463     
32464     /**
32465      * Set a css style for a column dynamically. 
32466      * @param {Number} colIndex The index of the column
32467      * @param {String} name The css property name
32468      * @param {String} value The css value
32469      */
32470     setCSSStyle : function(colIndex, name, value){
32471         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32472         Roo.util.CSS.updateRule(selector, name, value);
32473     },
32474     
32475     generateRules : function(cm){
32476         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32477         Roo.util.CSS.removeStyleSheet(rulesId);
32478         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32479             var cid = cm.getColumnId(i);
32480             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32481                          this.tdSelector, cid, " {\n}\n",
32482                          this.hdSelector, cid, " {\n}\n",
32483                          this.splitSelector, cid, " {\n}\n");
32484         }
32485         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32486     }
32487 });/*
32488  * Based on:
32489  * Ext JS Library 1.1.1
32490  * Copyright(c) 2006-2007, Ext JS, LLC.
32491  *
32492  * Originally Released Under LGPL - original licence link has changed is not relivant.
32493  *
32494  * Fork - LGPL
32495  * <script type="text/javascript">
32496  */
32497
32498 // private
32499 // This is a support class used internally by the Grid components
32500 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32501     this.grid = grid;
32502     this.view = grid.getView();
32503     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32504     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32505     if(hd2){
32506         this.setHandleElId(Roo.id(hd));
32507         this.setOuterHandleElId(Roo.id(hd2));
32508     }
32509     this.scroll = false;
32510 };
32511 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32512     maxDragWidth: 120,
32513     getDragData : function(e){
32514         var t = Roo.lib.Event.getTarget(e);
32515         var h = this.view.findHeaderCell(t);
32516         if(h){
32517             return {ddel: h.firstChild, header:h};
32518         }
32519         return false;
32520     },
32521
32522     onInitDrag : function(e){
32523         this.view.headersDisabled = true;
32524         var clone = this.dragData.ddel.cloneNode(true);
32525         clone.id = Roo.id();
32526         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32527         this.proxy.update(clone);
32528         return true;
32529     },
32530
32531     afterValidDrop : function(){
32532         var v = this.view;
32533         setTimeout(function(){
32534             v.headersDisabled = false;
32535         }, 50);
32536     },
32537
32538     afterInvalidDrop : function(){
32539         var v = this.view;
32540         setTimeout(function(){
32541             v.headersDisabled = false;
32542         }, 50);
32543     }
32544 });
32545 /*
32546  * Based on:
32547  * Ext JS Library 1.1.1
32548  * Copyright(c) 2006-2007, Ext JS, LLC.
32549  *
32550  * Originally Released Under LGPL - original licence link has changed is not relivant.
32551  *
32552  * Fork - LGPL
32553  * <script type="text/javascript">
32554  */
32555 // private
32556 // This is a support class used internally by the Grid components
32557 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32558     this.grid = grid;
32559     this.view = grid.getView();
32560     // split the proxies so they don't interfere with mouse events
32561     this.proxyTop = Roo.DomHelper.append(document.body, {
32562         cls:"col-move-top", html:"&#160;"
32563     }, true);
32564     this.proxyBottom = Roo.DomHelper.append(document.body, {
32565         cls:"col-move-bottom", html:"&#160;"
32566     }, true);
32567     this.proxyTop.hide = this.proxyBottom.hide = function(){
32568         this.setLeftTop(-100,-100);
32569         this.setStyle("visibility", "hidden");
32570     };
32571     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32572     // temporarily disabled
32573     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32574     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32575 };
32576 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32577     proxyOffsets : [-4, -9],
32578     fly: Roo.Element.fly,
32579
32580     getTargetFromEvent : function(e){
32581         var t = Roo.lib.Event.getTarget(e);
32582         var cindex = this.view.findCellIndex(t);
32583         if(cindex !== false){
32584             return this.view.getHeaderCell(cindex);
32585         }
32586     },
32587
32588     nextVisible : function(h){
32589         var v = this.view, cm = this.grid.colModel;
32590         h = h.nextSibling;
32591         while(h){
32592             if(!cm.isHidden(v.getCellIndex(h))){
32593                 return h;
32594             }
32595             h = h.nextSibling;
32596         }
32597         return null;
32598     },
32599
32600     prevVisible : function(h){
32601         var v = this.view, cm = this.grid.colModel;
32602         h = h.prevSibling;
32603         while(h){
32604             if(!cm.isHidden(v.getCellIndex(h))){
32605                 return h;
32606             }
32607             h = h.prevSibling;
32608         }
32609         return null;
32610     },
32611
32612     positionIndicator : function(h, n, e){
32613         var x = Roo.lib.Event.getPageX(e);
32614         var r = Roo.lib.Dom.getRegion(n.firstChild);
32615         var px, pt, py = r.top + this.proxyOffsets[1];
32616         if((r.right - x) <= (r.right-r.left)/2){
32617             px = r.right+this.view.borderWidth;
32618             pt = "after";
32619         }else{
32620             px = r.left;
32621             pt = "before";
32622         }
32623         var oldIndex = this.view.getCellIndex(h);
32624         var newIndex = this.view.getCellIndex(n);
32625
32626         if(this.grid.colModel.isFixed(newIndex)){
32627             return false;
32628         }
32629
32630         var locked = this.grid.colModel.isLocked(newIndex);
32631
32632         if(pt == "after"){
32633             newIndex++;
32634         }
32635         if(oldIndex < newIndex){
32636             newIndex--;
32637         }
32638         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32639             return false;
32640         }
32641         px +=  this.proxyOffsets[0];
32642         this.proxyTop.setLeftTop(px, py);
32643         this.proxyTop.show();
32644         if(!this.bottomOffset){
32645             this.bottomOffset = this.view.mainHd.getHeight();
32646         }
32647         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32648         this.proxyBottom.show();
32649         return pt;
32650     },
32651
32652     onNodeEnter : function(n, dd, e, data){
32653         if(data.header != n){
32654             this.positionIndicator(data.header, n, e);
32655         }
32656     },
32657
32658     onNodeOver : function(n, dd, e, data){
32659         var result = false;
32660         if(data.header != n){
32661             result = this.positionIndicator(data.header, n, e);
32662         }
32663         if(!result){
32664             this.proxyTop.hide();
32665             this.proxyBottom.hide();
32666         }
32667         return result ? this.dropAllowed : this.dropNotAllowed;
32668     },
32669
32670     onNodeOut : function(n, dd, e, data){
32671         this.proxyTop.hide();
32672         this.proxyBottom.hide();
32673     },
32674
32675     onNodeDrop : function(n, dd, e, data){
32676         var h = data.header;
32677         if(h != n){
32678             var cm = this.grid.colModel;
32679             var x = Roo.lib.Event.getPageX(e);
32680             var r = Roo.lib.Dom.getRegion(n.firstChild);
32681             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32682             var oldIndex = this.view.getCellIndex(h);
32683             var newIndex = this.view.getCellIndex(n);
32684             var locked = cm.isLocked(newIndex);
32685             if(pt == "after"){
32686                 newIndex++;
32687             }
32688             if(oldIndex < newIndex){
32689                 newIndex--;
32690             }
32691             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32692                 return false;
32693             }
32694             cm.setLocked(oldIndex, locked, true);
32695             cm.moveColumn(oldIndex, newIndex);
32696             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32697             return true;
32698         }
32699         return false;
32700     }
32701 });
32702 /*
32703  * Based on:
32704  * Ext JS Library 1.1.1
32705  * Copyright(c) 2006-2007, Ext JS, LLC.
32706  *
32707  * Originally Released Under LGPL - original licence link has changed is not relivant.
32708  *
32709  * Fork - LGPL
32710  * <script type="text/javascript">
32711  */
32712   
32713 /**
32714  * @class Roo.grid.GridView
32715  * @extends Roo.util.Observable
32716  *
32717  * @constructor
32718  * @param {Object} config
32719  */
32720 Roo.grid.GridView = function(config){
32721     Roo.grid.GridView.superclass.constructor.call(this);
32722     this.el = null;
32723
32724     Roo.apply(this, config);
32725 };
32726
32727 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32728
32729     /**
32730      * Override this function to apply custom css classes to rows during rendering
32731      * @param {Record} record The record
32732      * @param {Number} index
32733      * @method getRowClass
32734      */
32735     rowClass : "x-grid-row",
32736
32737     cellClass : "x-grid-col",
32738
32739     tdClass : "x-grid-td",
32740
32741     hdClass : "x-grid-hd",
32742
32743     splitClass : "x-grid-split",
32744
32745     sortClasses : ["sort-asc", "sort-desc"],
32746
32747     enableMoveAnim : false,
32748
32749     hlColor: "C3DAF9",
32750
32751     dh : Roo.DomHelper,
32752
32753     fly : Roo.Element.fly,
32754
32755     css : Roo.util.CSS,
32756
32757     borderWidth: 1,
32758
32759     splitOffset: 3,
32760
32761     scrollIncrement : 22,
32762
32763     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32764
32765     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32766
32767     bind : function(ds, cm){
32768         if(this.ds){
32769             this.ds.un("load", this.onLoad, this);
32770             this.ds.un("datachanged", this.onDataChange, this);
32771             this.ds.un("add", this.onAdd, this);
32772             this.ds.un("remove", this.onRemove, this);
32773             this.ds.un("update", this.onUpdate, this);
32774             this.ds.un("clear", this.onClear, this);
32775         }
32776         if(ds){
32777             ds.on("load", this.onLoad, this);
32778             ds.on("datachanged", this.onDataChange, this);
32779             ds.on("add", this.onAdd, this);
32780             ds.on("remove", this.onRemove, this);
32781             ds.on("update", this.onUpdate, this);
32782             ds.on("clear", this.onClear, this);
32783         }
32784         this.ds = ds;
32785
32786         if(this.cm){
32787             this.cm.un("widthchange", this.onColWidthChange, this);
32788             this.cm.un("headerchange", this.onHeaderChange, this);
32789             this.cm.un("hiddenchange", this.onHiddenChange, this);
32790             this.cm.un("columnmoved", this.onColumnMove, this);
32791             this.cm.un("columnlockchange", this.onColumnLock, this);
32792         }
32793         if(cm){
32794             this.generateRules(cm);
32795             cm.on("widthchange", this.onColWidthChange, this);
32796             cm.on("headerchange", this.onHeaderChange, this);
32797             cm.on("hiddenchange", this.onHiddenChange, this);
32798             cm.on("columnmoved", this.onColumnMove, this);
32799             cm.on("columnlockchange", this.onColumnLock, this);
32800         }
32801         this.cm = cm;
32802     },
32803
32804     init: function(grid){
32805                 Roo.grid.GridView.superclass.init.call(this, grid);
32806
32807                 this.bind(grid.dataSource, grid.colModel);
32808
32809             grid.on("headerclick", this.handleHeaderClick, this);
32810
32811         if(grid.trackMouseOver){
32812             grid.on("mouseover", this.onRowOver, this);
32813                 grid.on("mouseout", this.onRowOut, this);
32814             }
32815             grid.cancelTextSelection = function(){};
32816                 this.gridId = grid.id;
32817
32818                 var tpls = this.templates || {};
32819
32820                 if(!tpls.master){
32821                     tpls.master = new Roo.Template(
32822                        '<div class="x-grid" hidefocus="true">',
32823                           '<div class="x-grid-topbar"></div>',
32824                           '<div class="x-grid-scroller"><div></div></div>',
32825                           '<div class="x-grid-locked">',
32826                               '<div class="x-grid-header">{lockedHeader}</div>',
32827                               '<div class="x-grid-body">{lockedBody}</div>',
32828                           "</div>",
32829                           '<div class="x-grid-viewport">',
32830                               '<div class="x-grid-header">{header}</div>',
32831                               '<div class="x-grid-body">{body}</div>',
32832                           "</div>",
32833                           '<div class="x-grid-bottombar"></div>',
32834                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32835                           '<div class="x-grid-resize-proxy">&#160;</div>',
32836                        "</div>"
32837                     );
32838                     tpls.master.disableformats = true;
32839                 }
32840
32841                 if(!tpls.header){
32842                     tpls.header = new Roo.Template(
32843                        '<table border="0" cellspacing="0" cellpadding="0">',
32844                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32845                        "</table>{splits}"
32846                     );
32847                     tpls.header.disableformats = true;
32848                 }
32849                 tpls.header.compile();
32850
32851                 if(!tpls.hcell){
32852                     tpls.hcell = new Roo.Template(
32853                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32854                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32855                         "</div></td>"
32856                      );
32857                      tpls.hcell.disableFormats = true;
32858                 }
32859                 tpls.hcell.compile();
32860
32861                 if(!tpls.hsplit){
32862                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32863                     tpls.hsplit.disableFormats = true;
32864                 }
32865                 tpls.hsplit.compile();
32866
32867                 if(!tpls.body){
32868                     tpls.body = new Roo.Template(
32869                        '<table border="0" cellspacing="0" cellpadding="0">',
32870                        "<tbody>{rows}</tbody>",
32871                        "</table>"
32872                     );
32873                     tpls.body.disableFormats = true;
32874                 }
32875                 tpls.body.compile();
32876
32877                 if(!tpls.row){
32878                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32879                     tpls.row.disableFormats = true;
32880                 }
32881                 tpls.row.compile();
32882
32883                 if(!tpls.cell){
32884                     tpls.cell = new Roo.Template(
32885                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32886                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32887                         "</td>"
32888                     );
32889             tpls.cell.disableFormats = true;
32890         }
32891                 tpls.cell.compile();
32892
32893                 this.templates = tpls;
32894         },
32895
32896         // remap these for backwards compat
32897     onColWidthChange : function(){
32898         this.updateColumns.apply(this, arguments);
32899     },
32900     onHeaderChange : function(){
32901         this.updateHeaders.apply(this, arguments);
32902     }, 
32903     onHiddenChange : function(){
32904         this.handleHiddenChange.apply(this, arguments);
32905     },
32906     onColumnMove : function(){
32907         this.handleColumnMove.apply(this, arguments);
32908     },
32909     onColumnLock : function(){
32910         this.handleLockChange.apply(this, arguments);
32911     },
32912
32913     onDataChange : function(){
32914         this.refresh();
32915         this.updateHeaderSortState();
32916     },
32917
32918         onClear : function(){
32919         this.refresh();
32920     },
32921
32922         onUpdate : function(ds, record){
32923         this.refreshRow(record);
32924     },
32925
32926     refreshRow : function(record){
32927         var ds = this.ds, index;
32928         if(typeof record == 'number'){
32929             index = record;
32930             record = ds.getAt(index);
32931         }else{
32932             index = ds.indexOf(record);
32933         }
32934         this.insertRows(ds, index, index, true);
32935         this.onRemove(ds, record, index+1, true);
32936         this.syncRowHeights(index, index);
32937         this.layout();
32938         this.fireEvent("rowupdated", this, index, record);
32939     },
32940
32941     onAdd : function(ds, records, index){
32942         this.insertRows(ds, index, index + (records.length-1));
32943     },
32944
32945     onRemove : function(ds, record, index, isUpdate){
32946         if(isUpdate !== true){
32947             this.fireEvent("beforerowremoved", this, index, record);
32948         }
32949         var bt = this.getBodyTable(), lt = this.getLockedTable();
32950         if(bt.rows[index]){
32951             bt.firstChild.removeChild(bt.rows[index]);
32952         }
32953         if(lt.rows[index]){
32954             lt.firstChild.removeChild(lt.rows[index]);
32955         }
32956         if(isUpdate !== true){
32957             this.stripeRows(index);
32958             this.syncRowHeights(index, index);
32959             this.layout();
32960             this.fireEvent("rowremoved", this, index, record);
32961         }
32962     },
32963
32964     onLoad : function(){
32965         this.scrollToTop();
32966     },
32967
32968     /**
32969      * Scrolls the grid to the top
32970      */
32971     scrollToTop : function(){
32972         if(this.scroller){
32973             this.scroller.dom.scrollTop = 0;
32974             this.syncScroll();
32975         }
32976     },
32977
32978     /**
32979      * Gets a panel in the header of the grid that can be used for toolbars etc.
32980      * After modifying the contents of this panel a call to grid.autoSize() may be
32981      * required to register any changes in size.
32982      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32983      * @return Roo.Element
32984      */
32985     getHeaderPanel : function(doShow){
32986         if(doShow){
32987             this.headerPanel.show();
32988         }
32989         return this.headerPanel;
32990         },
32991
32992         /**
32993      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32994      * After modifying the contents of this panel a call to grid.autoSize() may be
32995      * required to register any changes in size.
32996      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32997      * @return Roo.Element
32998      */
32999     getFooterPanel : function(doShow){
33000         if(doShow){
33001             this.footerPanel.show();
33002         }
33003         return this.footerPanel;
33004         },
33005
33006         initElements : function(){
33007             var E = Roo.Element;
33008             var el = this.grid.getGridEl().dom.firstChild;
33009             var cs = el.childNodes;
33010
33011             this.el = new E(el);
33012             this.headerPanel = new E(el.firstChild);
33013             this.headerPanel.enableDisplayMode("block");
33014
33015         this.scroller = new E(cs[1]);
33016             this.scrollSizer = new E(this.scroller.dom.firstChild);
33017
33018             this.lockedWrap = new E(cs[2]);
33019             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33020             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33021
33022             this.mainWrap = new E(cs[3]);
33023             this.mainHd = new E(this.mainWrap.dom.firstChild);
33024             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33025
33026             this.footerPanel = new E(cs[4]);
33027             this.footerPanel.enableDisplayMode("block");
33028
33029         this.focusEl = new E(cs[5]);
33030         this.focusEl.swallowEvent("click", true);
33031         this.resizeProxy = new E(cs[6]);
33032
33033             this.headerSelector = String.format(
33034                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33035                this.lockedHd.id, this.mainHd.id
33036             );
33037
33038             this.splitterSelector = String.format(
33039                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33040                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33041             );
33042     },
33043     idToCssName : function(s)
33044     {
33045         return s.replace(/[^a-z0-9]+/ig, '-');
33046     },
33047
33048         getHeaderCell : function(index){
33049             return Roo.DomQuery.select(this.headerSelector)[index];
33050         },
33051
33052         getHeaderCellMeasure : function(index){
33053             return this.getHeaderCell(index).firstChild;
33054         },
33055
33056         getHeaderCellText : function(index){
33057             return this.getHeaderCell(index).firstChild.firstChild;
33058         },
33059
33060         getLockedTable : function(){
33061             return this.lockedBody.dom.firstChild;
33062         },
33063
33064         getBodyTable : function(){
33065             return this.mainBody.dom.firstChild;
33066         },
33067
33068         getLockedRow : function(index){
33069             return this.getLockedTable().rows[index];
33070         },
33071
33072         getRow : function(index){
33073             return this.getBodyTable().rows[index];
33074         },
33075
33076         getRowComposite : function(index){
33077             if(!this.rowEl){
33078                 this.rowEl = new Roo.CompositeElementLite();
33079             }
33080         var els = [], lrow, mrow;
33081         if(lrow = this.getLockedRow(index)){
33082             els.push(lrow);
33083         }
33084         if(mrow = this.getRow(index)){
33085             els.push(mrow);
33086         }
33087         this.rowEl.elements = els;
33088             return this.rowEl;
33089         },
33090
33091         getCell : function(rowIndex, colIndex){
33092             var locked = this.cm.getLockedCount();
33093             var source;
33094             if(colIndex < locked){
33095                 source = this.lockedBody.dom.firstChild;
33096             }else{
33097                 source = this.mainBody.dom.firstChild;
33098                 colIndex -= locked;
33099             }
33100         return source.rows[rowIndex].childNodes[colIndex];
33101         },
33102
33103         getCellText : function(rowIndex, colIndex){
33104             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33105         },
33106
33107         getCellBox : function(cell){
33108             var b = this.fly(cell).getBox();
33109         if(Roo.isOpera){ // opera fails to report the Y
33110             b.y = cell.offsetTop + this.mainBody.getY();
33111         }
33112         return b;
33113     },
33114
33115     getCellIndex : function(cell){
33116         var id = String(cell.className).match(this.cellRE);
33117         if(id){
33118             return parseInt(id[1], 10);
33119         }
33120         return 0;
33121     },
33122
33123     findHeaderIndex : function(n){
33124         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33125         return r ? this.getCellIndex(r) : false;
33126     },
33127
33128     findHeaderCell : function(n){
33129         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33130         return r ? r : false;
33131     },
33132
33133     findRowIndex : function(n){
33134         if(!n){
33135             return false;
33136         }
33137         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33138         return r ? r.rowIndex : false;
33139     },
33140
33141     findCellIndex : function(node){
33142         var stop = this.el.dom;
33143         while(node && node != stop){
33144             if(this.findRE.test(node.className)){
33145                 return this.getCellIndex(node);
33146             }
33147             node = node.parentNode;
33148         }
33149         return false;
33150     },
33151
33152     getColumnId : function(index){
33153             return this.cm.getColumnId(index);
33154         },
33155
33156         getSplitters : function(){
33157             if(this.splitterSelector){
33158                return Roo.DomQuery.select(this.splitterSelector);
33159             }else{
33160                 return null;
33161             }
33162         },
33163
33164         getSplitter : function(index){
33165             return this.getSplitters()[index];
33166         },
33167
33168     onRowOver : function(e, t){
33169         var row;
33170         if((row = this.findRowIndex(t)) !== false){
33171             this.getRowComposite(row).addClass("x-grid-row-over");
33172         }
33173     },
33174
33175     onRowOut : function(e, t){
33176         var row;
33177         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33178             this.getRowComposite(row).removeClass("x-grid-row-over");
33179         }
33180     },
33181
33182     renderHeaders : function(){
33183             var cm = this.cm;
33184         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33185         var cb = [], lb = [], sb = [], lsb = [], p = {};
33186         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33187             p.cellId = "x-grid-hd-0-" + i;
33188             p.splitId = "x-grid-csplit-0-" + i;
33189             p.id = cm.getColumnId(i);
33190             p.title = cm.getColumnTooltip(i) || "";
33191             p.value = cm.getColumnHeader(i) || "";
33192             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33193             if(!cm.isLocked(i)){
33194                 cb[cb.length] = ct.apply(p);
33195                 sb[sb.length] = st.apply(p);
33196             }else{
33197                 lb[lb.length] = ct.apply(p);
33198                 lsb[lsb.length] = st.apply(p);
33199             }
33200         }
33201         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33202                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33203         },
33204
33205         updateHeaders : function(){
33206         var html = this.renderHeaders();
33207         this.lockedHd.update(html[0]);
33208         this.mainHd.update(html[1]);
33209     },
33210
33211     /**
33212      * Focuses the specified row.
33213      * @param {Number} row The row index
33214      */
33215     focusRow : function(row){
33216         var x = this.scroller.dom.scrollLeft;
33217         this.focusCell(row, 0, false);
33218         this.scroller.dom.scrollLeft = x;
33219     },
33220
33221     /**
33222      * Focuses the specified cell.
33223      * @param {Number} row The row index
33224      * @param {Number} col The column index
33225      * @param {Boolean} hscroll false to disable horizontal scrolling
33226      */
33227     focusCell : function(row, col, hscroll){
33228         var el = this.ensureVisible(row, col, hscroll);
33229         this.focusEl.alignTo(el, "tl-tl");
33230         if(Roo.isGecko){
33231             this.focusEl.focus();
33232         }else{
33233             this.focusEl.focus.defer(1, this.focusEl);
33234         }
33235     },
33236
33237     /**
33238      * Scrolls the specified cell into view
33239      * @param {Number} row The row index
33240      * @param {Number} col The column index
33241      * @param {Boolean} hscroll false to disable horizontal scrolling
33242      */
33243     ensureVisible : function(row, col, hscroll){
33244         if(typeof row != "number"){
33245             row = row.rowIndex;
33246         }
33247         if(row < 0 && row >= this.ds.getCount()){
33248             return;
33249         }
33250         col = (col !== undefined ? col : 0);
33251         var cm = this.grid.colModel;
33252         while(cm.isHidden(col)){
33253             col++;
33254         }
33255
33256         var el = this.getCell(row, col);
33257         if(!el){
33258             return;
33259         }
33260         var c = this.scroller.dom;
33261
33262         var ctop = parseInt(el.offsetTop, 10);
33263         var cleft = parseInt(el.offsetLeft, 10);
33264         var cbot = ctop + el.offsetHeight;
33265         var cright = cleft + el.offsetWidth;
33266
33267         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33268         var stop = parseInt(c.scrollTop, 10);
33269         var sleft = parseInt(c.scrollLeft, 10);
33270         var sbot = stop + ch;
33271         var sright = sleft + c.clientWidth;
33272
33273         if(ctop < stop){
33274                 c.scrollTop = ctop;
33275         }else if(cbot > sbot){
33276             c.scrollTop = cbot-ch;
33277         }
33278
33279         if(hscroll !== false){
33280             if(cleft < sleft){
33281                 c.scrollLeft = cleft;
33282             }else if(cright > sright){
33283                 c.scrollLeft = cright-c.clientWidth;
33284             }
33285         }
33286         return el;
33287     },
33288
33289     updateColumns : function(){
33290         this.grid.stopEditing();
33291         var cm = this.grid.colModel, colIds = this.getColumnIds();
33292         //var totalWidth = cm.getTotalWidth();
33293         var pos = 0;
33294         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33295             //if(cm.isHidden(i)) continue;
33296             var w = cm.getColumnWidth(i);
33297             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33298             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33299         }
33300         this.updateSplitters();
33301     },
33302
33303     generateRules : function(cm){
33304         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33305         Roo.util.CSS.removeStyleSheet(rulesId);
33306         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33307             var cid = cm.getColumnId(i);
33308             var align = '';
33309             if(cm.config[i].align){
33310                 align = 'text-align:'+cm.config[i].align+';';
33311             }
33312             var hidden = '';
33313             if(cm.isHidden(i)){
33314                 hidden = 'display:none;';
33315             }
33316             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33317             ruleBuf.push(
33318                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33319                     this.hdSelector, cid, " {\n", align, width, "}\n",
33320                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33321                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33322         }
33323         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33324     },
33325
33326     updateSplitters : function(){
33327         var cm = this.cm, s = this.getSplitters();
33328         if(s){ // splitters not created yet
33329             var pos = 0, locked = true;
33330             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33331                 if(cm.isHidden(i)) continue;
33332                 var w = cm.getColumnWidth(i);
33333                 if(!cm.isLocked(i) && locked){
33334                     pos = 0;
33335                     locked = false;
33336                 }
33337                 pos += w;
33338                 s[i].style.left = (pos-this.splitOffset) + "px";
33339             }
33340         }
33341     },
33342
33343     handleHiddenChange : function(colModel, colIndex, hidden){
33344         if(hidden){
33345             this.hideColumn(colIndex);
33346         }else{
33347             this.unhideColumn(colIndex);
33348         }
33349     },
33350
33351     hideColumn : function(colIndex){
33352         var cid = this.getColumnId(colIndex);
33353         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33354         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33355         if(Roo.isSafari){
33356             this.updateHeaders();
33357         }
33358         this.updateSplitters();
33359         this.layout();
33360     },
33361
33362     unhideColumn : function(colIndex){
33363         var cid = this.getColumnId(colIndex);
33364         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33365         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33366
33367         if(Roo.isSafari){
33368             this.updateHeaders();
33369         }
33370         this.updateSplitters();
33371         this.layout();
33372     },
33373
33374     insertRows : function(dm, firstRow, lastRow, isUpdate){
33375         if(firstRow == 0 && lastRow == dm.getCount()-1){
33376             this.refresh();
33377         }else{
33378             if(!isUpdate){
33379                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33380             }
33381             var s = this.getScrollState();
33382             var markup = this.renderRows(firstRow, lastRow);
33383             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33384             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33385             this.restoreScroll(s);
33386             if(!isUpdate){
33387                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33388                 this.syncRowHeights(firstRow, lastRow);
33389                 this.stripeRows(firstRow);
33390                 this.layout();
33391             }
33392         }
33393     },
33394
33395     bufferRows : function(markup, target, index){
33396         var before = null, trows = target.rows, tbody = target.tBodies[0];
33397         if(index < trows.length){
33398             before = trows[index];
33399         }
33400         var b = document.createElement("div");
33401         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33402         var rows = b.firstChild.rows;
33403         for(var i = 0, len = rows.length; i < len; i++){
33404             if(before){
33405                 tbody.insertBefore(rows[0], before);
33406             }else{
33407                 tbody.appendChild(rows[0]);
33408             }
33409         }
33410         b.innerHTML = "";
33411         b = null;
33412     },
33413
33414     deleteRows : function(dm, firstRow, lastRow){
33415         if(dm.getRowCount()<1){
33416             this.fireEvent("beforerefresh", this);
33417             this.mainBody.update("");
33418             this.lockedBody.update("");
33419             this.fireEvent("refresh", this);
33420         }else{
33421             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33422             var bt = this.getBodyTable();
33423             var tbody = bt.firstChild;
33424             var rows = bt.rows;
33425             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33426                 tbody.removeChild(rows[firstRow]);
33427             }
33428             this.stripeRows(firstRow);
33429             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33430         }
33431     },
33432
33433     updateRows : function(dataSource, firstRow, lastRow){
33434         var s = this.getScrollState();
33435         this.refresh();
33436         this.restoreScroll(s);
33437     },
33438
33439     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33440         if(!noRefresh){
33441            this.refresh();
33442         }
33443         this.updateHeaderSortState();
33444     },
33445
33446     getScrollState : function(){
33447         var sb = this.scroller.dom;
33448         return {left: sb.scrollLeft, top: sb.scrollTop};
33449     },
33450
33451     stripeRows : function(startRow){
33452         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33453             return;
33454         }
33455         startRow = startRow || 0;
33456         var rows = this.getBodyTable().rows;
33457         var lrows = this.getLockedTable().rows;
33458         var cls = ' x-grid-row-alt ';
33459         for(var i = startRow, len = rows.length; i < len; i++){
33460             var row = rows[i], lrow = lrows[i];
33461             var isAlt = ((i+1) % 2 == 0);
33462             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33463             if(isAlt == hasAlt){
33464                 continue;
33465             }
33466             if(isAlt){
33467                 row.className += " x-grid-row-alt";
33468             }else{
33469                 row.className = row.className.replace("x-grid-row-alt", "");
33470             }
33471             if(lrow){
33472                 lrow.className = row.className;
33473             }
33474         }
33475     },
33476
33477     restoreScroll : function(state){
33478         var sb = this.scroller.dom;
33479         sb.scrollLeft = state.left;
33480         sb.scrollTop = state.top;
33481         this.syncScroll();
33482     },
33483
33484     syncScroll : function(){
33485         var sb = this.scroller.dom;
33486         var sh = this.mainHd.dom;
33487         var bs = this.mainBody.dom;
33488         var lv = this.lockedBody.dom;
33489         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33490         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33491     },
33492
33493     handleScroll : function(e){
33494         this.syncScroll();
33495         var sb = this.scroller.dom;
33496         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33497         e.stopEvent();
33498     },
33499
33500     handleWheel : function(e){
33501         var d = e.getWheelDelta();
33502         this.scroller.dom.scrollTop -= d*22;
33503         // set this here to prevent jumpy scrolling on large tables
33504         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33505         e.stopEvent();
33506     },
33507
33508     renderRows : function(startRow, endRow){
33509         // pull in all the crap needed to render rows
33510         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33511         var colCount = cm.getColumnCount();
33512
33513         if(ds.getCount() < 1){
33514             return ["", ""];
33515         }
33516
33517         // build a map for all the columns
33518         var cs = [];
33519         for(var i = 0; i < colCount; i++){
33520             var name = cm.getDataIndex(i);
33521             cs[i] = {
33522                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33523                 renderer : cm.getRenderer(i),
33524                 id : cm.getColumnId(i),
33525                 locked : cm.isLocked(i)
33526             };
33527         }
33528
33529         startRow = startRow || 0;
33530         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33531
33532         // records to render
33533         var rs = ds.getRange(startRow, endRow);
33534
33535         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33536     },
33537
33538     // As much as I hate to duplicate code, this was branched because FireFox really hates
33539     // [].join("") on strings. The performance difference was substantial enough to
33540     // branch this function
33541     doRender : Roo.isGecko ?
33542             function(cs, rs, ds, startRow, colCount, stripe){
33543                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33544                 // buffers
33545                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33546                 for(var j = 0, len = rs.length; j < len; j++){
33547                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33548                     for(var i = 0; i < colCount; i++){
33549                         c = cs[i];
33550                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33551                         p.id = c.id;
33552                         p.css = p.attr = "";
33553                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33554                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33555                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33556                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33557                         }
33558                         var markup = ct.apply(p);
33559                         if(!c.locked){
33560                             cb+= markup;
33561                         }else{
33562                             lcb+= markup;
33563                         }
33564                     }
33565                     var alt = [];
33566                     if(stripe && ((rowIndex+1) % 2 == 0)){
33567                         alt[0] = "x-grid-row-alt";
33568                     }
33569                     if(r.dirty){
33570                         alt[1] = " x-grid-dirty-row";
33571                     }
33572                     rp.cells = lcb;
33573                     if(this.getRowClass){
33574                         alt[2] = this.getRowClass(r, rowIndex);
33575                     }
33576                     rp.alt = alt.join(" ");
33577                     lbuf+= rt.apply(rp);
33578                     rp.cells = cb;
33579                     buf+=  rt.apply(rp);
33580                 }
33581                 return [lbuf, buf];
33582             } :
33583             function(cs, rs, ds, startRow, colCount, stripe){
33584                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33585                 // buffers
33586                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33587                 for(var j = 0, len = rs.length; j < len; j++){
33588                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33589                     for(var i = 0; i < colCount; i++){
33590                         c = cs[i];
33591                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33592                         p.id = c.id;
33593                         p.css = p.attr = "";
33594                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33595                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33596                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33597                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33598                         }
33599                         var markup = ct.apply(p);
33600                         if(!c.locked){
33601                             cb[cb.length] = markup;
33602                         }else{
33603                             lcb[lcb.length] = markup;
33604                         }
33605                     }
33606                     var alt = [];
33607                     if(stripe && ((rowIndex+1) % 2 == 0)){
33608                         alt[0] = "x-grid-row-alt";
33609                     }
33610                     if(r.dirty){
33611                         alt[1] = " x-grid-dirty-row";
33612                     }
33613                     rp.cells = lcb;
33614                     if(this.getRowClass){
33615                         alt[2] = this.getRowClass(r, rowIndex);
33616                     }
33617                     rp.alt = alt.join(" ");
33618                     rp.cells = lcb.join("");
33619                     lbuf[lbuf.length] = rt.apply(rp);
33620                     rp.cells = cb.join("");
33621                     buf[buf.length] =  rt.apply(rp);
33622                 }
33623                 return [lbuf.join(""), buf.join("")];
33624             },
33625
33626     renderBody : function(){
33627         var markup = this.renderRows();
33628         var bt = this.templates.body;
33629         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33630     },
33631
33632     /**
33633      * Refreshes the grid
33634      * @param {Boolean} headersToo
33635      */
33636     refresh : function(headersToo){
33637         this.fireEvent("beforerefresh", this);
33638         this.grid.stopEditing();
33639         var result = this.renderBody();
33640         this.lockedBody.update(result[0]);
33641         this.mainBody.update(result[1]);
33642         if(headersToo === true){
33643             this.updateHeaders();
33644             this.updateColumns();
33645             this.updateSplitters();
33646             this.updateHeaderSortState();
33647         }
33648         this.syncRowHeights();
33649         this.layout();
33650         this.fireEvent("refresh", this);
33651     },
33652
33653     handleColumnMove : function(cm, oldIndex, newIndex){
33654         this.indexMap = null;
33655         var s = this.getScrollState();
33656         this.refresh(true);
33657         this.restoreScroll(s);
33658         this.afterMove(newIndex);
33659     },
33660
33661     afterMove : function(colIndex){
33662         if(this.enableMoveAnim && Roo.enableFx){
33663             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33664         }
33665     },
33666
33667     updateCell : function(dm, rowIndex, dataIndex){
33668         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33669         if(typeof colIndex == "undefined"){ // not present in grid
33670             return;
33671         }
33672         var cm = this.grid.colModel;
33673         var cell = this.getCell(rowIndex, colIndex);
33674         var cellText = this.getCellText(rowIndex, colIndex);
33675
33676         var p = {
33677             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33678             id : cm.getColumnId(colIndex),
33679             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33680         };
33681         var renderer = cm.getRenderer(colIndex);
33682         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33683         if(typeof val == "undefined" || val === "") val = "&#160;";
33684         cellText.innerHTML = val;
33685         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33686         this.syncRowHeights(rowIndex, rowIndex);
33687     },
33688
33689     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33690         var maxWidth = 0;
33691         if(this.grid.autoSizeHeaders){
33692             var h = this.getHeaderCellMeasure(colIndex);
33693             maxWidth = Math.max(maxWidth, h.scrollWidth);
33694         }
33695         var tb, index;
33696         if(this.cm.isLocked(colIndex)){
33697             tb = this.getLockedTable();
33698             index = colIndex;
33699         }else{
33700             tb = this.getBodyTable();
33701             index = colIndex - this.cm.getLockedCount();
33702         }
33703         if(tb && tb.rows){
33704             var rows = tb.rows;
33705             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33706             for(var i = 0; i < stopIndex; i++){
33707                 var cell = rows[i].childNodes[index].firstChild;
33708                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33709             }
33710         }
33711         return maxWidth + /*margin for error in IE*/ 5;
33712     },
33713     /**
33714      * Autofit a column to its content.
33715      * @param {Number} colIndex
33716      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33717      */
33718      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33719          if(this.cm.isHidden(colIndex)){
33720              return; // can't calc a hidden column
33721          }
33722         if(forceMinSize){
33723             var cid = this.cm.getColumnId(colIndex);
33724             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33725            if(this.grid.autoSizeHeaders){
33726                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33727            }
33728         }
33729         var newWidth = this.calcColumnWidth(colIndex);
33730         this.cm.setColumnWidth(colIndex,
33731             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33732         if(!suppressEvent){
33733             this.grid.fireEvent("columnresize", colIndex, newWidth);
33734         }
33735     },
33736
33737     /**
33738      * Autofits all columns to their content and then expands to fit any extra space in the grid
33739      */
33740      autoSizeColumns : function(){
33741         var cm = this.grid.colModel;
33742         var colCount = cm.getColumnCount();
33743         for(var i = 0; i < colCount; i++){
33744             this.autoSizeColumn(i, true, true);
33745         }
33746         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33747             this.fitColumns();
33748         }else{
33749             this.updateColumns();
33750             this.layout();
33751         }
33752     },
33753
33754     /**
33755      * Autofits all columns to the grid's width proportionate with their current size
33756      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33757      */
33758     fitColumns : function(reserveScrollSpace){
33759         var cm = this.grid.colModel;
33760         var colCount = cm.getColumnCount();
33761         var cols = [];
33762         var width = 0;
33763         var i, w;
33764         for (i = 0; i < colCount; i++){
33765             if(!cm.isHidden(i) && !cm.isFixed(i)){
33766                 w = cm.getColumnWidth(i);
33767                 cols.push(i);
33768                 cols.push(w);
33769                 width += w;
33770             }
33771         }
33772         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33773         if(reserveScrollSpace){
33774             avail -= 17;
33775         }
33776         var frac = (avail - cm.getTotalWidth())/width;
33777         while (cols.length){
33778             w = cols.pop();
33779             i = cols.pop();
33780             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33781         }
33782         this.updateColumns();
33783         this.layout();
33784     },
33785
33786     onRowSelect : function(rowIndex){
33787         var row = this.getRowComposite(rowIndex);
33788         row.addClass("x-grid-row-selected");
33789     },
33790
33791     onRowDeselect : function(rowIndex){
33792         var row = this.getRowComposite(rowIndex);
33793         row.removeClass("x-grid-row-selected");
33794     },
33795
33796     onCellSelect : function(row, col){
33797         var cell = this.getCell(row, col);
33798         if(cell){
33799             Roo.fly(cell).addClass("x-grid-cell-selected");
33800         }
33801     },
33802
33803     onCellDeselect : function(row, col){
33804         var cell = this.getCell(row, col);
33805         if(cell){
33806             Roo.fly(cell).removeClass("x-grid-cell-selected");
33807         }
33808     },
33809
33810     updateHeaderSortState : function(){
33811         var state = this.ds.getSortState();
33812         if(!state){
33813             return;
33814         }
33815         this.sortState = state;
33816         var sortColumn = this.cm.findColumnIndex(state.field);
33817         if(sortColumn != -1){
33818             var sortDir = state.direction;
33819             var sc = this.sortClasses;
33820             var hds = this.el.select(this.headerSelector).removeClass(sc);
33821             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33822         }
33823     },
33824
33825     handleHeaderClick : function(g, index){
33826         if(this.headersDisabled){
33827             return;
33828         }
33829         var dm = g.dataSource, cm = g.colModel;
33830             if(!cm.isSortable(index)){
33831             return;
33832         }
33833             g.stopEditing();
33834         dm.sort(cm.getDataIndex(index));
33835     },
33836
33837
33838     destroy : function(){
33839         if(this.colMenu){
33840             this.colMenu.removeAll();
33841             Roo.menu.MenuMgr.unregister(this.colMenu);
33842             this.colMenu.getEl().remove();
33843             delete this.colMenu;
33844         }
33845         if(this.hmenu){
33846             this.hmenu.removeAll();
33847             Roo.menu.MenuMgr.unregister(this.hmenu);
33848             this.hmenu.getEl().remove();
33849             delete this.hmenu;
33850         }
33851         if(this.grid.enableColumnMove){
33852             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33853             if(dds){
33854                 for(var dd in dds){
33855                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33856                         var elid = dds[dd].dragElId;
33857                         dds[dd].unreg();
33858                         Roo.get(elid).remove();
33859                     } else if(dds[dd].config.isTarget){
33860                         dds[dd].proxyTop.remove();
33861                         dds[dd].proxyBottom.remove();
33862                         dds[dd].unreg();
33863                     }
33864                     if(Roo.dd.DDM.locationCache[dd]){
33865                         delete Roo.dd.DDM.locationCache[dd];
33866                     }
33867                 }
33868                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33869             }
33870         }
33871         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33872         this.bind(null, null);
33873         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33874     },
33875
33876     handleLockChange : function(){
33877         this.refresh(true);
33878     },
33879
33880     onDenyColumnLock : function(){
33881
33882     },
33883
33884     onDenyColumnHide : function(){
33885
33886     },
33887
33888     handleHdMenuClick : function(item){
33889         var index = this.hdCtxIndex;
33890         var cm = this.cm, ds = this.ds;
33891         switch(item.id){
33892             case "asc":
33893                 ds.sort(cm.getDataIndex(index), "ASC");
33894                 break;
33895             case "desc":
33896                 ds.sort(cm.getDataIndex(index), "DESC");
33897                 break;
33898             case "lock":
33899                 var lc = cm.getLockedCount();
33900                 if(cm.getColumnCount(true) <= lc+1){
33901                     this.onDenyColumnLock();
33902                     return;
33903                 }
33904                 if(lc != index){
33905                     cm.setLocked(index, true, true);
33906                     cm.moveColumn(index, lc);
33907                     this.grid.fireEvent("columnmove", index, lc);
33908                 }else{
33909                     cm.setLocked(index, true);
33910                 }
33911             break;
33912             case "unlock":
33913                 var lc = cm.getLockedCount();
33914                 if((lc-1) != index){
33915                     cm.setLocked(index, false, true);
33916                     cm.moveColumn(index, lc-1);
33917                     this.grid.fireEvent("columnmove", index, lc-1);
33918                 }else{
33919                     cm.setLocked(index, false);
33920                 }
33921             break;
33922             default:
33923                 index = cm.getIndexById(item.id.substr(4));
33924                 if(index != -1){
33925                     if(item.checked && cm.getColumnCount(true) <= 1){
33926                         this.onDenyColumnHide();
33927                         return false;
33928                     }
33929                     cm.setHidden(index, item.checked);
33930                 }
33931         }
33932         return true;
33933     },
33934
33935     beforeColMenuShow : function(){
33936         var cm = this.cm,  colCount = cm.getColumnCount();
33937         this.colMenu.removeAll();
33938         for(var i = 0; i < colCount; i++){
33939             this.colMenu.add(new Roo.menu.CheckItem({
33940                 id: "col-"+cm.getColumnId(i),
33941                 text: cm.getColumnHeader(i),
33942                 checked: !cm.isHidden(i),
33943                 hideOnClick:false
33944             }));
33945         }
33946     },
33947
33948     handleHdCtx : function(g, index, e){
33949         e.stopEvent();
33950         var hd = this.getHeaderCell(index);
33951         this.hdCtxIndex = index;
33952         var ms = this.hmenu.items, cm = this.cm;
33953         ms.get("asc").setDisabled(!cm.isSortable(index));
33954         ms.get("desc").setDisabled(!cm.isSortable(index));
33955         if(this.grid.enableColLock !== false){
33956             ms.get("lock").setDisabled(cm.isLocked(index));
33957             ms.get("unlock").setDisabled(!cm.isLocked(index));
33958         }
33959         this.hmenu.show(hd, "tl-bl");
33960     },
33961
33962     handleHdOver : function(e){
33963         var hd = this.findHeaderCell(e.getTarget());
33964         if(hd && !this.headersDisabled){
33965             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33966                this.fly(hd).addClass("x-grid-hd-over");
33967             }
33968         }
33969     },
33970
33971     handleHdOut : function(e){
33972         var hd = this.findHeaderCell(e.getTarget());
33973         if(hd){
33974             this.fly(hd).removeClass("x-grid-hd-over");
33975         }
33976     },
33977
33978     handleSplitDblClick : function(e, t){
33979         var i = this.getCellIndex(t);
33980         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33981             this.autoSizeColumn(i, true);
33982             this.layout();
33983         }
33984     },
33985
33986     render : function(){
33987
33988         var cm = this.cm;
33989         var colCount = cm.getColumnCount();
33990
33991         if(this.grid.monitorWindowResize === true){
33992             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33993         }
33994         var header = this.renderHeaders();
33995         var body = this.templates.body.apply({rows:""});
33996         var html = this.templates.master.apply({
33997             lockedBody: body,
33998             body: body,
33999             lockedHeader: header[0],
34000             header: header[1]
34001         });
34002
34003         //this.updateColumns();
34004
34005         this.grid.getGridEl().dom.innerHTML = html;
34006
34007         this.initElements();
34008         
34009         // a kludge to fix the random scolling effect in webkit
34010         this.el.on("scroll", function() {
34011             this.el.dom.scrollTop=0; // hopefully not recursive..
34012         },this);
34013
34014         this.scroller.on("scroll", this.handleScroll, this);
34015         this.lockedBody.on("mousewheel", this.handleWheel, this);
34016         this.mainBody.on("mousewheel", this.handleWheel, this);
34017
34018         this.mainHd.on("mouseover", this.handleHdOver, this);
34019         this.mainHd.on("mouseout", this.handleHdOut, this);
34020         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34021                 {delegate: "."+this.splitClass});
34022
34023         this.lockedHd.on("mouseover", this.handleHdOver, this);
34024         this.lockedHd.on("mouseout", this.handleHdOut, this);
34025         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34026                 {delegate: "."+this.splitClass});
34027
34028         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34029             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34030         }
34031
34032         this.updateSplitters();
34033
34034         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34035             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34036             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34037         }
34038
34039         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34040             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34041             this.hmenu.add(
34042                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34043                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34044             );
34045             if(this.grid.enableColLock !== false){
34046                 this.hmenu.add('-',
34047                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34048                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34049                 );
34050             }
34051             if(this.grid.enableColumnHide !== false){
34052
34053                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34054                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34055                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34056
34057                 this.hmenu.add('-',
34058                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34059                 );
34060             }
34061             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34062
34063             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34064         }
34065
34066         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34067             this.dd = new Roo.grid.GridDragZone(this.grid, {
34068                 ddGroup : this.grid.ddGroup || 'GridDD'
34069             });
34070         }
34071
34072         /*
34073         for(var i = 0; i < colCount; i++){
34074             if(cm.isHidden(i)){
34075                 this.hideColumn(i);
34076             }
34077             if(cm.config[i].align){
34078                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34079                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34080             }
34081         }*/
34082         
34083         this.updateHeaderSortState();
34084
34085         this.beforeInitialResize();
34086         this.layout(true);
34087
34088         // two part rendering gives faster view to the user
34089         this.renderPhase2.defer(1, this);
34090     },
34091
34092     renderPhase2 : function(){
34093         // render the rows now
34094         this.refresh();
34095         if(this.grid.autoSizeColumns){
34096             this.autoSizeColumns();
34097         }
34098     },
34099
34100     beforeInitialResize : function(){
34101
34102     },
34103
34104     onColumnSplitterMoved : function(i, w){
34105         this.userResized = true;
34106         var cm = this.grid.colModel;
34107         cm.setColumnWidth(i, w, true);
34108         var cid = cm.getColumnId(i);
34109         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34110         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34111         this.updateSplitters();
34112         this.layout();
34113         this.grid.fireEvent("columnresize", i, w);
34114     },
34115
34116     syncRowHeights : function(startIndex, endIndex){
34117         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34118             startIndex = startIndex || 0;
34119             var mrows = this.getBodyTable().rows;
34120             var lrows = this.getLockedTable().rows;
34121             var len = mrows.length-1;
34122             endIndex = Math.min(endIndex || len, len);
34123             for(var i = startIndex; i <= endIndex; i++){
34124                 var m = mrows[i], l = lrows[i];
34125                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34126                 m.style.height = l.style.height = h + "px";
34127             }
34128         }
34129     },
34130
34131     layout : function(initialRender, is2ndPass){
34132         var g = this.grid;
34133         var auto = g.autoHeight;
34134         var scrollOffset = 16;
34135         var c = g.getGridEl(), cm = this.cm,
34136                 expandCol = g.autoExpandColumn,
34137                 gv = this;
34138         //c.beginMeasure();
34139
34140         if(!c.dom.offsetWidth){ // display:none?
34141             if(initialRender){
34142                 this.lockedWrap.show();
34143                 this.mainWrap.show();
34144             }
34145             return;
34146         }
34147
34148         var hasLock = this.cm.isLocked(0);
34149
34150         var tbh = this.headerPanel.getHeight();
34151         var bbh = this.footerPanel.getHeight();
34152
34153         if(auto){
34154             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34155             var newHeight = ch + c.getBorderWidth("tb");
34156             if(g.maxHeight){
34157                 newHeight = Math.min(g.maxHeight, newHeight);
34158             }
34159             c.setHeight(newHeight);
34160         }
34161
34162         if(g.autoWidth){
34163             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34164         }
34165
34166         var s = this.scroller;
34167
34168         var csize = c.getSize(true);
34169
34170         this.el.setSize(csize.width, csize.height);
34171
34172         this.headerPanel.setWidth(csize.width);
34173         this.footerPanel.setWidth(csize.width);
34174
34175         var hdHeight = this.mainHd.getHeight();
34176         var vw = csize.width;
34177         var vh = csize.height - (tbh + bbh);
34178
34179         s.setSize(vw, vh);
34180
34181         var bt = this.getBodyTable();
34182         var ltWidth = hasLock ?
34183                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34184
34185         var scrollHeight = bt.offsetHeight;
34186         var scrollWidth = ltWidth + bt.offsetWidth;
34187         var vscroll = false, hscroll = false;
34188
34189         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34190
34191         var lw = this.lockedWrap, mw = this.mainWrap;
34192         var lb = this.lockedBody, mb = this.mainBody;
34193
34194         setTimeout(function(){
34195             var t = s.dom.offsetTop;
34196             var w = s.dom.clientWidth,
34197                 h = s.dom.clientHeight;
34198
34199             lw.setTop(t);
34200             lw.setSize(ltWidth, h);
34201
34202             mw.setLeftTop(ltWidth, t);
34203             mw.setSize(w-ltWidth, h);
34204
34205             lb.setHeight(h-hdHeight);
34206             mb.setHeight(h-hdHeight);
34207
34208             if(is2ndPass !== true && !gv.userResized && expandCol){
34209                 // high speed resize without full column calculation
34210                 
34211                 var ci = cm.getIndexById(expandCol);
34212                 if (ci < 0) {
34213                     ci = cm.findColumnIndex(expandCol);
34214                 }
34215                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34216                 var expandId = cm.getColumnId(ci);
34217                 var  tw = cm.getTotalWidth(false);
34218                 var currentWidth = cm.getColumnWidth(ci);
34219                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34220                 if(currentWidth != cw){
34221                     cm.setColumnWidth(ci, cw, true);
34222                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34223                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34224                     gv.updateSplitters();
34225                     gv.layout(false, true);
34226                 }
34227             }
34228
34229             if(initialRender){
34230                 lw.show();
34231                 mw.show();
34232             }
34233             //c.endMeasure();
34234         }, 10);
34235     },
34236
34237     onWindowResize : function(){
34238         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34239             return;
34240         }
34241         this.layout();
34242     },
34243
34244     appendFooter : function(parentEl){
34245         return null;
34246     },
34247
34248     sortAscText : "Sort Ascending",
34249     sortDescText : "Sort Descending",
34250     lockText : "Lock Column",
34251     unlockText : "Unlock Column",
34252     columnsText : "Columns"
34253 });
34254
34255
34256 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34257     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34258     this.proxy.el.addClass('x-grid3-col-dd');
34259 };
34260
34261 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34262     handleMouseDown : function(e){
34263
34264     },
34265
34266     callHandleMouseDown : function(e){
34267         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34268     }
34269 });
34270 /*
34271  * Based on:
34272  * Ext JS Library 1.1.1
34273  * Copyright(c) 2006-2007, Ext JS, LLC.
34274  *
34275  * Originally Released Under LGPL - original licence link has changed is not relivant.
34276  *
34277  * Fork - LGPL
34278  * <script type="text/javascript">
34279  */
34280  
34281 // private
34282 // This is a support class used internally by the Grid components
34283 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34284     this.grid = grid;
34285     this.view = grid.getView();
34286     this.proxy = this.view.resizeProxy;
34287     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34288         "gridSplitters" + this.grid.getGridEl().id, {
34289         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34290     });
34291     this.setHandleElId(Roo.id(hd));
34292     this.setOuterHandleElId(Roo.id(hd2));
34293     this.scroll = false;
34294 };
34295 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34296     fly: Roo.Element.fly,
34297
34298     b4StartDrag : function(x, y){
34299         this.view.headersDisabled = true;
34300         this.proxy.setHeight(this.view.mainWrap.getHeight());
34301         var w = this.cm.getColumnWidth(this.cellIndex);
34302         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34303         this.resetConstraints();
34304         this.setXConstraint(minw, 1000);
34305         this.setYConstraint(0, 0);
34306         this.minX = x - minw;
34307         this.maxX = x + 1000;
34308         this.startPos = x;
34309         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34310     },
34311
34312
34313     handleMouseDown : function(e){
34314         ev = Roo.EventObject.setEvent(e);
34315         var t = this.fly(ev.getTarget());
34316         if(t.hasClass("x-grid-split")){
34317             this.cellIndex = this.view.getCellIndex(t.dom);
34318             this.split = t.dom;
34319             this.cm = this.grid.colModel;
34320             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34321                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34322             }
34323         }
34324     },
34325
34326     endDrag : function(e){
34327         this.view.headersDisabled = false;
34328         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34329         var diff = endX - this.startPos;
34330         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34331     },
34332
34333     autoOffset : function(){
34334         this.setDelta(0,0);
34335     }
34336 });/*
34337  * Based on:
34338  * Ext JS Library 1.1.1
34339  * Copyright(c) 2006-2007, Ext JS, LLC.
34340  *
34341  * Originally Released Under LGPL - original licence link has changed is not relivant.
34342  *
34343  * Fork - LGPL
34344  * <script type="text/javascript">
34345  */
34346  
34347 // private
34348 // This is a support class used internally by the Grid components
34349 Roo.grid.GridDragZone = function(grid, config){
34350     this.view = grid.getView();
34351     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34352     if(this.view.lockedBody){
34353         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34354         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34355     }
34356     this.scroll = false;
34357     this.grid = grid;
34358     this.ddel = document.createElement('div');
34359     this.ddel.className = 'x-grid-dd-wrap';
34360 };
34361
34362 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34363     ddGroup : "GridDD",
34364
34365     getDragData : function(e){
34366         var t = Roo.lib.Event.getTarget(e);
34367         var rowIndex = this.view.findRowIndex(t);
34368         if(rowIndex !== false){
34369             var sm = this.grid.selModel;
34370             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34371               //  sm.mouseDown(e, t);
34372             //}
34373             if (e.hasModifier()){
34374                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34375             }
34376             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34377         }
34378         return false;
34379     },
34380
34381     onInitDrag : function(e){
34382         var data = this.dragData;
34383         this.ddel.innerHTML = this.grid.getDragDropText();
34384         this.proxy.update(this.ddel);
34385         // fire start drag?
34386     },
34387
34388     afterRepair : function(){
34389         this.dragging = false;
34390     },
34391
34392     getRepairXY : function(e, data){
34393         return false;
34394     },
34395
34396     onEndDrag : function(data, e){
34397         // fire end drag?
34398     },
34399
34400     onValidDrop : function(dd, e, id){
34401         // fire drag drop?
34402         this.hideProxy();
34403     },
34404
34405     beforeInvalidDrop : function(e, id){
34406
34407     }
34408 });/*
34409  * Based on:
34410  * Ext JS Library 1.1.1
34411  * Copyright(c) 2006-2007, Ext JS, LLC.
34412  *
34413  * Originally Released Under LGPL - original licence link has changed is not relivant.
34414  *
34415  * Fork - LGPL
34416  * <script type="text/javascript">
34417  */
34418  
34419
34420 /**
34421  * @class Roo.grid.ColumnModel
34422  * @extends Roo.util.Observable
34423  * This is the default implementation of a ColumnModel used by the Grid. It defines
34424  * the columns in the grid.
34425  * <br>Usage:<br>
34426  <pre><code>
34427  var colModel = new Roo.grid.ColumnModel([
34428         {header: "Ticker", width: 60, sortable: true, locked: true},
34429         {header: "Company Name", width: 150, sortable: true},
34430         {header: "Market Cap.", width: 100, sortable: true},
34431         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34432         {header: "Employees", width: 100, sortable: true, resizable: false}
34433  ]);
34434  </code></pre>
34435  * <p>
34436  
34437  * The config options listed for this class are options which may appear in each
34438  * individual column definition.
34439  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34440  * @constructor
34441  * @param {Object} config An Array of column config objects. See this class's
34442  * config objects for details.
34443 */
34444 Roo.grid.ColumnModel = function(config){
34445         /**
34446      * The config passed into the constructor
34447      */
34448     this.config = config;
34449     this.lookup = {};
34450
34451     // if no id, create one
34452     // if the column does not have a dataIndex mapping,
34453     // map it to the order it is in the config
34454     for(var i = 0, len = config.length; i < len; i++){
34455         var c = config[i];
34456         if(typeof c.dataIndex == "undefined"){
34457             c.dataIndex = i;
34458         }
34459         if(typeof c.renderer == "string"){
34460             c.renderer = Roo.util.Format[c.renderer];
34461         }
34462         if(typeof c.id == "undefined"){
34463             c.id = Roo.id();
34464         }
34465         if(c.editor && c.editor.xtype){
34466             c.editor  = Roo.factory(c.editor, Roo.grid);
34467         }
34468         if(c.editor && c.editor.isFormField){
34469             c.editor = new Roo.grid.GridEditor(c.editor);
34470         }
34471         this.lookup[c.id] = c;
34472     }
34473
34474     /**
34475      * The width of columns which have no width specified (defaults to 100)
34476      * @type Number
34477      */
34478     this.defaultWidth = 100;
34479
34480     /**
34481      * Default sortable of columns which have no sortable specified (defaults to false)
34482      * @type Boolean
34483      */
34484     this.defaultSortable = false;
34485
34486     this.addEvents({
34487         /**
34488              * @event widthchange
34489              * Fires when the width of a column changes.
34490              * @param {ColumnModel} this
34491              * @param {Number} columnIndex The column index
34492              * @param {Number} newWidth The new width
34493              */
34494             "widthchange": true,
34495         /**
34496              * @event headerchange
34497              * Fires when the text of a header changes.
34498              * @param {ColumnModel} this
34499              * @param {Number} columnIndex The column index
34500              * @param {Number} newText The new header text
34501              */
34502             "headerchange": true,
34503         /**
34504              * @event hiddenchange
34505              * Fires when a column is hidden or "unhidden".
34506              * @param {ColumnModel} this
34507              * @param {Number} columnIndex The column index
34508              * @param {Boolean} hidden true if hidden, false otherwise
34509              */
34510             "hiddenchange": true,
34511             /**
34512          * @event columnmoved
34513          * Fires when a column is moved.
34514          * @param {ColumnModel} this
34515          * @param {Number} oldIndex
34516          * @param {Number} newIndex
34517          */
34518         "columnmoved" : true,
34519         /**
34520          * @event columlockchange
34521          * Fires when a column's locked state is changed
34522          * @param {ColumnModel} this
34523          * @param {Number} colIndex
34524          * @param {Boolean} locked true if locked
34525          */
34526         "columnlockchange" : true
34527     });
34528     Roo.grid.ColumnModel.superclass.constructor.call(this);
34529 };
34530 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34531     /**
34532      * @cfg {String} header The header text to display in the Grid view.
34533      */
34534     /**
34535      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34536      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34537      * specified, the column's index is used as an index into the Record's data Array.
34538      */
34539     /**
34540      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34541      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34542      */
34543     /**
34544      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34545      * Defaults to the value of the {@link #defaultSortable} property.
34546      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34547      */
34548     /**
34549      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34550      */
34551     /**
34552      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34553      */
34554     /**
34555      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34556      */
34557     /**
34558      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34559      */
34560     /**
34561      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34562      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34563      * default renderer uses the raw data value.
34564      */
34565        /**
34566      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34567      */
34568     /**
34569      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34570      */
34571
34572     /**
34573      * Returns the id of the column at the specified index.
34574      * @param {Number} index The column index
34575      * @return {String} the id
34576      */
34577     getColumnId : function(index){
34578         return this.config[index].id;
34579     },
34580
34581     /**
34582      * Returns the column for a specified id.
34583      * @param {String} id The column id
34584      * @return {Object} the column
34585      */
34586     getColumnById : function(id){
34587         return this.lookup[id];
34588     },
34589
34590     
34591     /**
34592      * Returns the column for a specified dataIndex.
34593      * @param {String} dataIndex The column dataIndex
34594      * @return {Object|Boolean} the column or false if not found
34595      */
34596     getColumnByDataIndex: function(dataIndex){
34597         var index = this.findColumnIndex(dataIndex);
34598         return index > -1 ? this.config[index] : false;
34599     },
34600     
34601     /**
34602      * Returns the index for a specified column id.
34603      * @param {String} id The column id
34604      * @return {Number} the index, or -1 if not found
34605      */
34606     getIndexById : function(id){
34607         for(var i = 0, len = this.config.length; i < len; i++){
34608             if(this.config[i].id == id){
34609                 return i;
34610             }
34611         }
34612         return -1;
34613     },
34614     
34615     /**
34616      * Returns the index for a specified column dataIndex.
34617      * @param {String} dataIndex The column dataIndex
34618      * @return {Number} the index, or -1 if not found
34619      */
34620     
34621     findColumnIndex : function(dataIndex){
34622         for(var i = 0, len = this.config.length; i < len; i++){
34623             if(this.config[i].dataIndex == dataIndex){
34624                 return i;
34625             }
34626         }
34627         return -1;
34628     },
34629     
34630     
34631     moveColumn : function(oldIndex, newIndex){
34632         var c = this.config[oldIndex];
34633         this.config.splice(oldIndex, 1);
34634         this.config.splice(newIndex, 0, c);
34635         this.dataMap = null;
34636         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34637     },
34638
34639     isLocked : function(colIndex){
34640         return this.config[colIndex].locked === true;
34641     },
34642
34643     setLocked : function(colIndex, value, suppressEvent){
34644         if(this.isLocked(colIndex) == value){
34645             return;
34646         }
34647         this.config[colIndex].locked = value;
34648         if(!suppressEvent){
34649             this.fireEvent("columnlockchange", this, colIndex, value);
34650         }
34651     },
34652
34653     getTotalLockedWidth : function(){
34654         var totalWidth = 0;
34655         for(var i = 0; i < this.config.length; i++){
34656             if(this.isLocked(i) && !this.isHidden(i)){
34657                 this.totalWidth += this.getColumnWidth(i);
34658             }
34659         }
34660         return totalWidth;
34661     },
34662
34663     getLockedCount : function(){
34664         for(var i = 0, len = this.config.length; i < len; i++){
34665             if(!this.isLocked(i)){
34666                 return i;
34667             }
34668         }
34669     },
34670
34671     /**
34672      * Returns the number of columns.
34673      * @return {Number}
34674      */
34675     getColumnCount : function(visibleOnly){
34676         if(visibleOnly === true){
34677             var c = 0;
34678             for(var i = 0, len = this.config.length; i < len; i++){
34679                 if(!this.isHidden(i)){
34680                     c++;
34681                 }
34682             }
34683             return c;
34684         }
34685         return this.config.length;
34686     },
34687
34688     /**
34689      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34690      * @param {Function} fn
34691      * @param {Object} scope (optional)
34692      * @return {Array} result
34693      */
34694     getColumnsBy : function(fn, scope){
34695         var r = [];
34696         for(var i = 0, len = this.config.length; i < len; i++){
34697             var c = this.config[i];
34698             if(fn.call(scope||this, c, i) === true){
34699                 r[r.length] = c;
34700             }
34701         }
34702         return r;
34703     },
34704
34705     /**
34706      * Returns true if the specified column is sortable.
34707      * @param {Number} col The column index
34708      * @return {Boolean}
34709      */
34710     isSortable : function(col){
34711         if(typeof this.config[col].sortable == "undefined"){
34712             return this.defaultSortable;
34713         }
34714         return this.config[col].sortable;
34715     },
34716
34717     /**
34718      * Returns the rendering (formatting) function defined for the column.
34719      * @param {Number} col The column index.
34720      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34721      */
34722     getRenderer : function(col){
34723         if(!this.config[col].renderer){
34724             return Roo.grid.ColumnModel.defaultRenderer;
34725         }
34726         return this.config[col].renderer;
34727     },
34728
34729     /**
34730      * Sets the rendering (formatting) function for a column.
34731      * @param {Number} col The column index
34732      * @param {Function} fn The function to use to process the cell's raw data
34733      * to return HTML markup for the grid view. The render function is called with
34734      * the following parameters:<ul>
34735      * <li>Data value.</li>
34736      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34737      * <li>css A CSS style string to apply to the table cell.</li>
34738      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34739      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34740      * <li>Row index</li>
34741      * <li>Column index</li>
34742      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34743      */
34744     setRenderer : function(col, fn){
34745         this.config[col].renderer = fn;
34746     },
34747
34748     /**
34749      * Returns the width for the specified column.
34750      * @param {Number} col The column index
34751      * @return {Number}
34752      */
34753     getColumnWidth : function(col){
34754         return this.config[col].width || this.defaultWidth;
34755     },
34756
34757     /**
34758      * Sets the width for a column.
34759      * @param {Number} col The column index
34760      * @param {Number} width The new width
34761      */
34762     setColumnWidth : function(col, width, suppressEvent){
34763         this.config[col].width = width;
34764         this.totalWidth = null;
34765         if(!suppressEvent){
34766              this.fireEvent("widthchange", this, col, width);
34767         }
34768     },
34769
34770     /**
34771      * Returns the total width of all columns.
34772      * @param {Boolean} includeHidden True to include hidden column widths
34773      * @return {Number}
34774      */
34775     getTotalWidth : function(includeHidden){
34776         if(!this.totalWidth){
34777             this.totalWidth = 0;
34778             for(var i = 0, len = this.config.length; i < len; i++){
34779                 if(includeHidden || !this.isHidden(i)){
34780                     this.totalWidth += this.getColumnWidth(i);
34781                 }
34782             }
34783         }
34784         return this.totalWidth;
34785     },
34786
34787     /**
34788      * Returns the header for the specified column.
34789      * @param {Number} col The column index
34790      * @return {String}
34791      */
34792     getColumnHeader : function(col){
34793         return this.config[col].header;
34794     },
34795
34796     /**
34797      * Sets the header for a column.
34798      * @param {Number} col The column index
34799      * @param {String} header The new header
34800      */
34801     setColumnHeader : function(col, header){
34802         this.config[col].header = header;
34803         this.fireEvent("headerchange", this, col, header);
34804     },
34805
34806     /**
34807      * Returns the tooltip for the specified column.
34808      * @param {Number} col The column index
34809      * @return {String}
34810      */
34811     getColumnTooltip : function(col){
34812             return this.config[col].tooltip;
34813     },
34814     /**
34815      * Sets the tooltip for a column.
34816      * @param {Number} col The column index
34817      * @param {String} tooltip The new tooltip
34818      */
34819     setColumnTooltip : function(col, tooltip){
34820             this.config[col].tooltip = tooltip;
34821     },
34822
34823     /**
34824      * Returns the dataIndex for the specified column.
34825      * @param {Number} col The column index
34826      * @return {Number}
34827      */
34828     getDataIndex : function(col){
34829         return this.config[col].dataIndex;
34830     },
34831
34832     /**
34833      * Sets the dataIndex for a column.
34834      * @param {Number} col The column index
34835      * @param {Number} dataIndex The new dataIndex
34836      */
34837     setDataIndex : function(col, dataIndex){
34838         this.config[col].dataIndex = dataIndex;
34839     },
34840
34841     
34842     
34843     /**
34844      * Returns true if the cell is editable.
34845      * @param {Number} colIndex The column index
34846      * @param {Number} rowIndex The row index
34847      * @return {Boolean}
34848      */
34849     isCellEditable : function(colIndex, rowIndex){
34850         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34851     },
34852
34853     /**
34854      * Returns the editor defined for the cell/column.
34855      * return false or null to disable editing.
34856      * @param {Number} colIndex The column index
34857      * @param {Number} rowIndex The row index
34858      * @return {Object}
34859      */
34860     getCellEditor : function(colIndex, rowIndex){
34861         return this.config[colIndex].editor;
34862     },
34863
34864     /**
34865      * Sets if a column is editable.
34866      * @param {Number} col The column index
34867      * @param {Boolean} editable True if the column is editable
34868      */
34869     setEditable : function(col, editable){
34870         this.config[col].editable = editable;
34871     },
34872
34873
34874     /**
34875      * Returns true if the column is hidden.
34876      * @param {Number} colIndex The column index
34877      * @return {Boolean}
34878      */
34879     isHidden : function(colIndex){
34880         return this.config[colIndex].hidden;
34881     },
34882
34883
34884     /**
34885      * Returns true if the column width cannot be changed
34886      */
34887     isFixed : function(colIndex){
34888         return this.config[colIndex].fixed;
34889     },
34890
34891     /**
34892      * Returns true if the column can be resized
34893      * @return {Boolean}
34894      */
34895     isResizable : function(colIndex){
34896         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34897     },
34898     /**
34899      * Sets if a column is hidden.
34900      * @param {Number} colIndex The column index
34901      * @param {Boolean} hidden True if the column is hidden
34902      */
34903     setHidden : function(colIndex, hidden){
34904         this.config[colIndex].hidden = hidden;
34905         this.totalWidth = null;
34906         this.fireEvent("hiddenchange", this, colIndex, hidden);
34907     },
34908
34909     /**
34910      * Sets the editor for a column.
34911      * @param {Number} col The column index
34912      * @param {Object} editor The editor object
34913      */
34914     setEditor : function(col, editor){
34915         this.config[col].editor = editor;
34916     }
34917 });
34918
34919 Roo.grid.ColumnModel.defaultRenderer = function(value){
34920         if(typeof value == "string" && value.length < 1){
34921             return "&#160;";
34922         }
34923         return value;
34924 };
34925
34926 // Alias for backwards compatibility
34927 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34928 /*
34929  * Based on:
34930  * Ext JS Library 1.1.1
34931  * Copyright(c) 2006-2007, Ext JS, LLC.
34932  *
34933  * Originally Released Under LGPL - original licence link has changed is not relivant.
34934  *
34935  * Fork - LGPL
34936  * <script type="text/javascript">
34937  */
34938
34939 /**
34940  * @class Roo.grid.AbstractSelectionModel
34941  * @extends Roo.util.Observable
34942  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34943  * implemented by descendant classes.  This class should not be directly instantiated.
34944  * @constructor
34945  */
34946 Roo.grid.AbstractSelectionModel = function(){
34947     this.locked = false;
34948     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34949 };
34950
34951 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34952     /** @ignore Called by the grid automatically. Do not call directly. */
34953     init : function(grid){
34954         this.grid = grid;
34955         this.initEvents();
34956     },
34957
34958     /**
34959      * Locks the selections.
34960      */
34961     lock : function(){
34962         this.locked = true;
34963     },
34964
34965     /**
34966      * Unlocks the selections.
34967      */
34968     unlock : function(){
34969         this.locked = false;
34970     },
34971
34972     /**
34973      * Returns true if the selections are locked.
34974      * @return {Boolean}
34975      */
34976     isLocked : function(){
34977         return this.locked;
34978     }
34979 });/*
34980  * Based on:
34981  * Ext JS Library 1.1.1
34982  * Copyright(c) 2006-2007, Ext JS, LLC.
34983  *
34984  * Originally Released Under LGPL - original licence link has changed is not relivant.
34985  *
34986  * Fork - LGPL
34987  * <script type="text/javascript">
34988  */
34989 /**
34990  * @extends Roo.grid.AbstractSelectionModel
34991  * @class Roo.grid.RowSelectionModel
34992  * The default SelectionModel used by {@link Roo.grid.Grid}.
34993  * It supports multiple selections and keyboard selection/navigation. 
34994  * @constructor
34995  * @param {Object} config
34996  */
34997 Roo.grid.RowSelectionModel = function(config){
34998     Roo.apply(this, config);
34999     this.selections = new Roo.util.MixedCollection(false, function(o){
35000         return o.id;
35001     });
35002
35003     this.last = false;
35004     this.lastActive = false;
35005
35006     this.addEvents({
35007         /**
35008              * @event selectionchange
35009              * Fires when the selection changes
35010              * @param {SelectionModel} this
35011              */
35012             "selectionchange" : true,
35013         /**
35014              * @event afterselectionchange
35015              * Fires after the selection changes (eg. by key press or clicking)
35016              * @param {SelectionModel} this
35017              */
35018             "afterselectionchange" : true,
35019         /**
35020              * @event beforerowselect
35021              * Fires when a row is selected being selected, return false to cancel.
35022              * @param {SelectionModel} this
35023              * @param {Number} rowIndex The selected index
35024              * @param {Boolean} keepExisting False if other selections will be cleared
35025              */
35026             "beforerowselect" : true,
35027         /**
35028              * @event rowselect
35029              * Fires when a row is selected.
35030              * @param {SelectionModel} this
35031              * @param {Number} rowIndex The selected index
35032              * @param {Roo.data.Record} r The record
35033              */
35034             "rowselect" : true,
35035         /**
35036              * @event rowdeselect
35037              * Fires when a row is deselected.
35038              * @param {SelectionModel} this
35039              * @param {Number} rowIndex The selected index
35040              */
35041         "rowdeselect" : true
35042     });
35043     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35044     this.locked = false;
35045 };
35046
35047 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35048     /**
35049      * @cfg {Boolean} singleSelect
35050      * True to allow selection of only one row at a time (defaults to false)
35051      */
35052     singleSelect : false,
35053
35054     // private
35055     initEvents : function(){
35056
35057         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35058             this.grid.on("mousedown", this.handleMouseDown, this);
35059         }else{ // allow click to work like normal
35060             this.grid.on("rowclick", this.handleDragableRowClick, this);
35061         }
35062
35063         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35064             "up" : function(e){
35065                 if(!e.shiftKey){
35066                     this.selectPrevious(e.shiftKey);
35067                 }else if(this.last !== false && this.lastActive !== false){
35068                     var last = this.last;
35069                     this.selectRange(this.last,  this.lastActive-1);
35070                     this.grid.getView().focusRow(this.lastActive);
35071                     if(last !== false){
35072                         this.last = last;
35073                     }
35074                 }else{
35075                     this.selectFirstRow();
35076                 }
35077                 this.fireEvent("afterselectionchange", this);
35078             },
35079             "down" : function(e){
35080                 if(!e.shiftKey){
35081                     this.selectNext(e.shiftKey);
35082                 }else if(this.last !== false && this.lastActive !== false){
35083                     var last = this.last;
35084                     this.selectRange(this.last,  this.lastActive+1);
35085                     this.grid.getView().focusRow(this.lastActive);
35086                     if(last !== false){
35087                         this.last = last;
35088                     }
35089                 }else{
35090                     this.selectFirstRow();
35091                 }
35092                 this.fireEvent("afterselectionchange", this);
35093             },
35094             scope: this
35095         });
35096
35097         var view = this.grid.view;
35098         view.on("refresh", this.onRefresh, this);
35099         view.on("rowupdated", this.onRowUpdated, this);
35100         view.on("rowremoved", this.onRemove, this);
35101     },
35102
35103     // private
35104     onRefresh : function(){
35105         var ds = this.grid.dataSource, i, v = this.grid.view;
35106         var s = this.selections;
35107         s.each(function(r){
35108             if((i = ds.indexOfId(r.id)) != -1){
35109                 v.onRowSelect(i);
35110             }else{
35111                 s.remove(r);
35112             }
35113         });
35114     },
35115
35116     // private
35117     onRemove : function(v, index, r){
35118         this.selections.remove(r);
35119     },
35120
35121     // private
35122     onRowUpdated : function(v, index, r){
35123         if(this.isSelected(r)){
35124             v.onRowSelect(index);
35125         }
35126     },
35127
35128     /**
35129      * Select records.
35130      * @param {Array} records The records to select
35131      * @param {Boolean} keepExisting (optional) True to keep existing selections
35132      */
35133     selectRecords : function(records, keepExisting){
35134         if(!keepExisting){
35135             this.clearSelections();
35136         }
35137         var ds = this.grid.dataSource;
35138         for(var i = 0, len = records.length; i < len; i++){
35139             this.selectRow(ds.indexOf(records[i]), true);
35140         }
35141     },
35142
35143     /**
35144      * Gets the number of selected rows.
35145      * @return {Number}
35146      */
35147     getCount : function(){
35148         return this.selections.length;
35149     },
35150
35151     /**
35152      * Selects the first row in the grid.
35153      */
35154     selectFirstRow : function(){
35155         this.selectRow(0);
35156     },
35157
35158     /**
35159      * Select the last row.
35160      * @param {Boolean} keepExisting (optional) True to keep existing selections
35161      */
35162     selectLastRow : function(keepExisting){
35163         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35164     },
35165
35166     /**
35167      * Selects the row immediately following the last selected row.
35168      * @param {Boolean} keepExisting (optional) True to keep existing selections
35169      */
35170     selectNext : function(keepExisting){
35171         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35172             this.selectRow(this.last+1, keepExisting);
35173             this.grid.getView().focusRow(this.last);
35174         }
35175     },
35176
35177     /**
35178      * Selects the row that precedes the last selected row.
35179      * @param {Boolean} keepExisting (optional) True to keep existing selections
35180      */
35181     selectPrevious : function(keepExisting){
35182         if(this.last){
35183             this.selectRow(this.last-1, keepExisting);
35184             this.grid.getView().focusRow(this.last);
35185         }
35186     },
35187
35188     /**
35189      * Returns the selected records
35190      * @return {Array} Array of selected records
35191      */
35192     getSelections : function(){
35193         return [].concat(this.selections.items);
35194     },
35195
35196     /**
35197      * Returns the first selected record.
35198      * @return {Record}
35199      */
35200     getSelected : function(){
35201         return this.selections.itemAt(0);
35202     },
35203
35204
35205     /**
35206      * Clears all selections.
35207      */
35208     clearSelections : function(fast){
35209         if(this.locked) return;
35210         if(fast !== true){
35211             var ds = this.grid.dataSource;
35212             var s = this.selections;
35213             s.each(function(r){
35214                 this.deselectRow(ds.indexOfId(r.id));
35215             }, this);
35216             s.clear();
35217         }else{
35218             this.selections.clear();
35219         }
35220         this.last = false;
35221     },
35222
35223
35224     /**
35225      * Selects all rows.
35226      */
35227     selectAll : function(){
35228         if(this.locked) return;
35229         this.selections.clear();
35230         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35231             this.selectRow(i, true);
35232         }
35233     },
35234
35235     /**
35236      * Returns True if there is a selection.
35237      * @return {Boolean}
35238      */
35239     hasSelection : function(){
35240         return this.selections.length > 0;
35241     },
35242
35243     /**
35244      * Returns True if the specified row is selected.
35245      * @param {Number/Record} record The record or index of the record to check
35246      * @return {Boolean}
35247      */
35248     isSelected : function(index){
35249         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35250         return (r && this.selections.key(r.id) ? true : false);
35251     },
35252
35253     /**
35254      * Returns True if the specified record id is selected.
35255      * @param {String} id The id of record to check
35256      * @return {Boolean}
35257      */
35258     isIdSelected : function(id){
35259         return (this.selections.key(id) ? true : false);
35260     },
35261
35262     // private
35263     handleMouseDown : function(e, t){
35264         var view = this.grid.getView(), rowIndex;
35265         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35266             return;
35267         };
35268         if(e.shiftKey && this.last !== false){
35269             var last = this.last;
35270             this.selectRange(last, rowIndex, e.ctrlKey);
35271             this.last = last; // reset the last
35272             view.focusRow(rowIndex);
35273         }else{
35274             var isSelected = this.isSelected(rowIndex);
35275             if(e.button !== 0 && isSelected){
35276                 view.focusRow(rowIndex);
35277             }else if(e.ctrlKey && isSelected){
35278                 this.deselectRow(rowIndex);
35279             }else if(!isSelected){
35280                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35281                 view.focusRow(rowIndex);
35282             }
35283         }
35284         this.fireEvent("afterselectionchange", this);
35285     },
35286     // private
35287     handleDragableRowClick :  function(grid, rowIndex, e) 
35288     {
35289         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35290             this.selectRow(rowIndex, false);
35291             grid.view.focusRow(rowIndex);
35292              this.fireEvent("afterselectionchange", this);
35293         }
35294     },
35295     
35296     /**
35297      * Selects multiple rows.
35298      * @param {Array} rows Array of the indexes of the row to select
35299      * @param {Boolean} keepExisting (optional) True to keep existing selections
35300      */
35301     selectRows : function(rows, keepExisting){
35302         if(!keepExisting){
35303             this.clearSelections();
35304         }
35305         for(var i = 0, len = rows.length; i < len; i++){
35306             this.selectRow(rows[i], true);
35307         }
35308     },
35309
35310     /**
35311      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35312      * @param {Number} startRow The index of the first row in the range
35313      * @param {Number} endRow The index of the last row in the range
35314      * @param {Boolean} keepExisting (optional) True to retain existing selections
35315      */
35316     selectRange : function(startRow, endRow, keepExisting){
35317         if(this.locked) return;
35318         if(!keepExisting){
35319             this.clearSelections();
35320         }
35321         if(startRow <= endRow){
35322             for(var i = startRow; i <= endRow; i++){
35323                 this.selectRow(i, true);
35324             }
35325         }else{
35326             for(var i = startRow; i >= endRow; i--){
35327                 this.selectRow(i, true);
35328             }
35329         }
35330     },
35331
35332     /**
35333      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35334      * @param {Number} startRow The index of the first row in the range
35335      * @param {Number} endRow The index of the last row in the range
35336      */
35337     deselectRange : function(startRow, endRow, preventViewNotify){
35338         if(this.locked) return;
35339         for(var i = startRow; i <= endRow; i++){
35340             this.deselectRow(i, preventViewNotify);
35341         }
35342     },
35343
35344     /**
35345      * Selects a row.
35346      * @param {Number} row The index of the row to select
35347      * @param {Boolean} keepExisting (optional) True to keep existing selections
35348      */
35349     selectRow : function(index, keepExisting, preventViewNotify){
35350         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35351         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35352             if(!keepExisting || this.singleSelect){
35353                 this.clearSelections();
35354             }
35355             var r = this.grid.dataSource.getAt(index);
35356             this.selections.add(r);
35357             this.last = this.lastActive = index;
35358             if(!preventViewNotify){
35359                 this.grid.getView().onRowSelect(index);
35360             }
35361             this.fireEvent("rowselect", this, index, r);
35362             this.fireEvent("selectionchange", this);
35363         }
35364     },
35365
35366     /**
35367      * Deselects a row.
35368      * @param {Number} row The index of the row to deselect
35369      */
35370     deselectRow : function(index, preventViewNotify){
35371         if(this.locked) return;
35372         if(this.last == index){
35373             this.last = false;
35374         }
35375         if(this.lastActive == index){
35376             this.lastActive = false;
35377         }
35378         var r = this.grid.dataSource.getAt(index);
35379         this.selections.remove(r);
35380         if(!preventViewNotify){
35381             this.grid.getView().onRowDeselect(index);
35382         }
35383         this.fireEvent("rowdeselect", this, index);
35384         this.fireEvent("selectionchange", this);
35385     },
35386
35387     // private
35388     restoreLast : function(){
35389         if(this._last){
35390             this.last = this._last;
35391         }
35392     },
35393
35394     // private
35395     acceptsNav : function(row, col, cm){
35396         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35397     },
35398
35399     // private
35400     onEditorKey : function(field, e){
35401         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35402         if(k == e.TAB){
35403             e.stopEvent();
35404             ed.completeEdit();
35405             if(e.shiftKey){
35406                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35407             }else{
35408                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35409             }
35410         }else if(k == e.ENTER && !e.ctrlKey){
35411             e.stopEvent();
35412             ed.completeEdit();
35413             if(e.shiftKey){
35414                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35415             }else{
35416                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35417             }
35418         }else if(k == e.ESC){
35419             ed.cancelEdit();
35420         }
35421         if(newCell){
35422             g.startEditing(newCell[0], newCell[1]);
35423         }
35424     }
35425 });/*
35426  * Based on:
35427  * Ext JS Library 1.1.1
35428  * Copyright(c) 2006-2007, Ext JS, LLC.
35429  *
35430  * Originally Released Under LGPL - original licence link has changed is not relivant.
35431  *
35432  * Fork - LGPL
35433  * <script type="text/javascript">
35434  */
35435 /**
35436  * @class Roo.grid.CellSelectionModel
35437  * @extends Roo.grid.AbstractSelectionModel
35438  * This class provides the basic implementation for cell selection in a grid.
35439  * @constructor
35440  * @param {Object} config The object containing the configuration of this model.
35441  */
35442 Roo.grid.CellSelectionModel = function(config){
35443     Roo.apply(this, config);
35444
35445     this.selection = null;
35446
35447     this.addEvents({
35448         /**
35449              * @event beforerowselect
35450              * Fires before a cell is selected.
35451              * @param {SelectionModel} this
35452              * @param {Number} rowIndex The selected row index
35453              * @param {Number} colIndex The selected cell index
35454              */
35455             "beforecellselect" : true,
35456         /**
35457              * @event cellselect
35458              * Fires when a cell is selected.
35459              * @param {SelectionModel} this
35460              * @param {Number} rowIndex The selected row index
35461              * @param {Number} colIndex The selected cell index
35462              */
35463             "cellselect" : true,
35464         /**
35465              * @event selectionchange
35466              * Fires when the active selection changes.
35467              * @param {SelectionModel} this
35468              * @param {Object} selection null for no selection or an object (o) with two properties
35469                 <ul>
35470                 <li>o.record: the record object for the row the selection is in</li>
35471                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35472                 </ul>
35473              */
35474             "selectionchange" : true
35475     });
35476     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35477 };
35478
35479 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35480
35481     /** @ignore */
35482     initEvents : function(){
35483         this.grid.on("mousedown", this.handleMouseDown, this);
35484         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35485         var view = this.grid.view;
35486         view.on("refresh", this.onViewChange, this);
35487         view.on("rowupdated", this.onRowUpdated, this);
35488         view.on("beforerowremoved", this.clearSelections, this);
35489         view.on("beforerowsinserted", this.clearSelections, this);
35490         if(this.grid.isEditor){
35491             this.grid.on("beforeedit", this.beforeEdit,  this);
35492         }
35493     },
35494
35495         //private
35496     beforeEdit : function(e){
35497         this.select(e.row, e.column, false, true, e.record);
35498     },
35499
35500         //private
35501     onRowUpdated : function(v, index, r){
35502         if(this.selection && this.selection.record == r){
35503             v.onCellSelect(index, this.selection.cell[1]);
35504         }
35505     },
35506
35507         //private
35508     onViewChange : function(){
35509         this.clearSelections(true);
35510     },
35511
35512         /**
35513          * Returns the currently selected cell,.
35514          * @return {Array} The selected cell (row, column) or null if none selected.
35515          */
35516     getSelectedCell : function(){
35517         return this.selection ? this.selection.cell : null;
35518     },
35519
35520     /**
35521      * Clears all selections.
35522      * @param {Boolean} true to prevent the gridview from being notified about the change.
35523      */
35524     clearSelections : function(preventNotify){
35525         var s = this.selection;
35526         if(s){
35527             if(preventNotify !== true){
35528                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35529             }
35530             this.selection = null;
35531             this.fireEvent("selectionchange", this, null);
35532         }
35533     },
35534
35535     /**
35536      * Returns true if there is a selection.
35537      * @return {Boolean}
35538      */
35539     hasSelection : function(){
35540         return this.selection ? true : false;
35541     },
35542
35543     /** @ignore */
35544     handleMouseDown : function(e, t){
35545         var v = this.grid.getView();
35546         if(this.isLocked()){
35547             return;
35548         };
35549         var row = v.findRowIndex(t);
35550         var cell = v.findCellIndex(t);
35551         if(row !== false && cell !== false){
35552             this.select(row, cell);
35553         }
35554     },
35555
35556     /**
35557      * Selects a cell.
35558      * @param {Number} rowIndex
35559      * @param {Number} collIndex
35560      */
35561     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35562         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35563             this.clearSelections();
35564             r = r || this.grid.dataSource.getAt(rowIndex);
35565             this.selection = {
35566                 record : r,
35567                 cell : [rowIndex, colIndex]
35568             };
35569             if(!preventViewNotify){
35570                 var v = this.grid.getView();
35571                 v.onCellSelect(rowIndex, colIndex);
35572                 if(preventFocus !== true){
35573                     v.focusCell(rowIndex, colIndex);
35574                 }
35575             }
35576             this.fireEvent("cellselect", this, rowIndex, colIndex);
35577             this.fireEvent("selectionchange", this, this.selection);
35578         }
35579     },
35580
35581         //private
35582     isSelectable : function(rowIndex, colIndex, cm){
35583         return !cm.isHidden(colIndex);
35584     },
35585
35586     /** @ignore */
35587     handleKeyDown : function(e){
35588         Roo.log('Cell Sel Model handleKeyDown');
35589         if(!e.isNavKeyPress()){
35590             return;
35591         }
35592         var g = this.grid, s = this.selection;
35593         if(!s){
35594             e.stopEvent();
35595             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35596             if(cell){
35597                 this.select(cell[0], cell[1]);
35598             }
35599             return;
35600         }
35601         var sm = this;
35602         var walk = function(row, col, step){
35603             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35604         };
35605         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35606         var newCell;
35607
35608         switch(k){
35609             case e.TAB:
35610                 // handled by onEditorKey
35611                 if (g.isEditor && g.editing) {
35612                     return;
35613                 }
35614                 if(e.shiftKey){
35615                      newCell = walk(r, c-1, -1);
35616                 }else{
35617                      newCell = walk(r, c+1, 1);
35618                 }
35619              break;
35620              case e.DOWN:
35621                  newCell = walk(r+1, c, 1);
35622              break;
35623              case e.UP:
35624                  newCell = walk(r-1, c, -1);
35625              break;
35626              case e.RIGHT:
35627                  newCell = walk(r, c+1, 1);
35628              break;
35629              case e.LEFT:
35630                  newCell = walk(r, c-1, -1);
35631              break;
35632              case e.ENTER:
35633                  if(g.isEditor && !g.editing){
35634                     g.startEditing(r, c);
35635                     e.stopEvent();
35636                     return;
35637                 }
35638              break;
35639         };
35640         if(newCell){
35641             this.select(newCell[0], newCell[1]);
35642             e.stopEvent();
35643         }
35644     },
35645
35646     acceptsNav : function(row, col, cm){
35647         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35648     },
35649
35650     onEditorKey : function(field, e){
35651         
35652         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35653         ///Roo.log('onEditorKey' + k);
35654         
35655         if(k == e.TAB){
35656             if(e.shiftKey){
35657                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35658             }else{
35659                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35660             }
35661             e.stopEvent();
35662         }else if(k == e.ENTER && !e.ctrlKey){
35663             ed.completeEdit();
35664             e.stopEvent();
35665             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35666         }else if(k == e.ESC){
35667             ed.cancelEdit();
35668         }
35669         
35670         
35671         if(newCell){
35672             //Roo.log('next cell after edit');
35673             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35674         }
35675     }
35676 });/*
35677  * Based on:
35678  * Ext JS Library 1.1.1
35679  * Copyright(c) 2006-2007, Ext JS, LLC.
35680  *
35681  * Originally Released Under LGPL - original licence link has changed is not relivant.
35682  *
35683  * Fork - LGPL
35684  * <script type="text/javascript">
35685  */
35686  
35687 /**
35688  * @class Roo.grid.EditorGrid
35689  * @extends Roo.grid.Grid
35690  * Class for creating and editable grid.
35691  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35692  * The container MUST have some type of size defined for the grid to fill. The container will be 
35693  * automatically set to position relative if it isn't already.
35694  * @param {Object} dataSource The data model to bind to
35695  * @param {Object} colModel The column model with info about this grid's columns
35696  */
35697 Roo.grid.EditorGrid = function(container, config){
35698     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35699     this.getGridEl().addClass("xedit-grid");
35700
35701     if(!this.selModel){
35702         this.selModel = new Roo.grid.CellSelectionModel();
35703     }
35704
35705     this.activeEditor = null;
35706
35707         this.addEvents({
35708             /**
35709              * @event beforeedit
35710              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35711              * <ul style="padding:5px;padding-left:16px;">
35712              * <li>grid - This grid</li>
35713              * <li>record - The record being edited</li>
35714              * <li>field - The field name being edited</li>
35715              * <li>value - The value for the field being edited.</li>
35716              * <li>row - The grid row index</li>
35717              * <li>column - The grid column index</li>
35718              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35719              * </ul>
35720              * @param {Object} e An edit event (see above for description)
35721              */
35722             "beforeedit" : true,
35723             /**
35724              * @event afteredit
35725              * Fires after a cell is edited. <br />
35726              * <ul style="padding:5px;padding-left:16px;">
35727              * <li>grid - This grid</li>
35728              * <li>record - The record being edited</li>
35729              * <li>field - The field name being edited</li>
35730              * <li>value - The value being set</li>
35731              * <li>originalValue - The original value for the field, before the edit.</li>
35732              * <li>row - The grid row index</li>
35733              * <li>column - The grid column index</li>
35734              * </ul>
35735              * @param {Object} e An edit event (see above for description)
35736              */
35737             "afteredit" : true,
35738             /**
35739              * @event validateedit
35740              * Fires after a cell is edited, but before the value is set in the record. 
35741          * You can use this to modify the value being set in the field, Return false
35742              * to cancel the change. The edit event object has the following properties <br />
35743              * <ul style="padding:5px;padding-left:16px;">
35744          * <li>editor - This editor</li>
35745              * <li>grid - This grid</li>
35746              * <li>record - The record being edited</li>
35747              * <li>field - The field name being edited</li>
35748              * <li>value - The value being set</li>
35749              * <li>originalValue - The original value for the field, before the edit.</li>
35750              * <li>row - The grid row index</li>
35751              * <li>column - The grid column index</li>
35752              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35753              * </ul>
35754              * @param {Object} e An edit event (see above for description)
35755              */
35756             "validateedit" : true
35757         });
35758     this.on("bodyscroll", this.stopEditing,  this);
35759     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35760 };
35761
35762 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35763     /**
35764      * @cfg {Number} clicksToEdit
35765      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35766      */
35767     clicksToEdit: 2,
35768
35769     // private
35770     isEditor : true,
35771     // private
35772     trackMouseOver: false, // causes very odd FF errors
35773
35774     onCellDblClick : function(g, row, col){
35775         this.startEditing(row, col);
35776     },
35777
35778     onEditComplete : function(ed, value, startValue){
35779         this.editing = false;
35780         this.activeEditor = null;
35781         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35782         var r = ed.record;
35783         var field = this.colModel.getDataIndex(ed.col);
35784         var e = {
35785             grid: this,
35786             record: r,
35787             field: field,
35788             originalValue: startValue,
35789             value: value,
35790             row: ed.row,
35791             column: ed.col,
35792             cancel:false,
35793             editor: ed
35794         };
35795         if(String(value) !== String(startValue)){
35796             
35797             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35798                 r.set(field, e.value);
35799                 // if we are dealing with a combo box..
35800                 // then we also set the 'name' colum to be the displayField
35801                 if (ed.field.displayField && ed.field.name) {
35802                     r.set(ed.field.name, ed.field.el.dom.value);
35803                 }
35804                 
35805                 delete e.cancel; //?? why!!!
35806                 this.fireEvent("afteredit", e);
35807             }
35808         } else {
35809             this.fireEvent("afteredit", e); // always fire it!
35810         }
35811         this.view.focusCell(ed.row, ed.col);
35812     },
35813
35814     /**
35815      * Starts editing the specified for the specified row/column
35816      * @param {Number} rowIndex
35817      * @param {Number} colIndex
35818      */
35819     startEditing : function(row, col){
35820         this.stopEditing();
35821         if(this.colModel.isCellEditable(col, row)){
35822             this.view.ensureVisible(row, col, true);
35823             var r = this.dataSource.getAt(row);
35824             var field = this.colModel.getDataIndex(col);
35825             var e = {
35826                 grid: this,
35827                 record: r,
35828                 field: field,
35829                 value: r.data[field],
35830                 row: row,
35831                 column: col,
35832                 cancel:false
35833             };
35834             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35835                 this.editing = true;
35836                 var ed = this.colModel.getCellEditor(col, row);
35837                 
35838                 if (!ed) {
35839                     return;
35840                 }
35841                 if(!ed.rendered){
35842                     ed.render(ed.parentEl || document.body);
35843                 }
35844                 ed.field.reset();
35845                 (function(){ // complex but required for focus issues in safari, ie and opera
35846                     ed.row = row;
35847                     ed.col = col;
35848                     ed.record = r;
35849                     ed.on("complete", this.onEditComplete, this, {single: true});
35850                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35851                     this.activeEditor = ed;
35852                     var v = r.data[field];
35853                     ed.startEdit(this.view.getCell(row, col), v);
35854                     // combo's with 'displayField and name set
35855                     if (ed.field.displayField && ed.field.name) {
35856                         ed.field.el.dom.value = r.data[ed.field.name];
35857                     }
35858                     
35859                     
35860                 }).defer(50, this);
35861             }
35862         }
35863     },
35864         
35865     /**
35866      * Stops any active editing
35867      */
35868     stopEditing : function(){
35869         if(this.activeEditor){
35870             this.activeEditor.completeEdit();
35871         }
35872         this.activeEditor = null;
35873     }
35874 });/*
35875  * Based on:
35876  * Ext JS Library 1.1.1
35877  * Copyright(c) 2006-2007, Ext JS, LLC.
35878  *
35879  * Originally Released Under LGPL - original licence link has changed is not relivant.
35880  *
35881  * Fork - LGPL
35882  * <script type="text/javascript">
35883  */
35884
35885 // private - not really -- you end up using it !
35886 // This is a support class used internally by the Grid components
35887
35888 /**
35889  * @class Roo.grid.GridEditor
35890  * @extends Roo.Editor
35891  * Class for creating and editable grid elements.
35892  * @param {Object} config any settings (must include field)
35893  */
35894 Roo.grid.GridEditor = function(field, config){
35895     if (!config && field.field) {
35896         config = field;
35897         field = Roo.factory(config.field, Roo.form);
35898     }
35899     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35900     field.monitorTab = false;
35901 };
35902
35903 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35904     
35905     /**
35906      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35907      */
35908     
35909     alignment: "tl-tl",
35910     autoSize: "width",
35911     hideEl : false,
35912     cls: "x-small-editor x-grid-editor",
35913     shim:false,
35914     shadow:"frame"
35915 });/*
35916  * Based on:
35917  * Ext JS Library 1.1.1
35918  * Copyright(c) 2006-2007, Ext JS, LLC.
35919  *
35920  * Originally Released Under LGPL - original licence link has changed is not relivant.
35921  *
35922  * Fork - LGPL
35923  * <script type="text/javascript">
35924  */
35925   
35926
35927   
35928 Roo.grid.PropertyRecord = Roo.data.Record.create([
35929     {name:'name',type:'string'},  'value'
35930 ]);
35931
35932
35933 Roo.grid.PropertyStore = function(grid, source){
35934     this.grid = grid;
35935     this.store = new Roo.data.Store({
35936         recordType : Roo.grid.PropertyRecord
35937     });
35938     this.store.on('update', this.onUpdate,  this);
35939     if(source){
35940         this.setSource(source);
35941     }
35942     Roo.grid.PropertyStore.superclass.constructor.call(this);
35943 };
35944
35945
35946
35947 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35948     setSource : function(o){
35949         this.source = o;
35950         this.store.removeAll();
35951         var data = [];
35952         for(var k in o){
35953             if(this.isEditableValue(o[k])){
35954                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35955             }
35956         }
35957         this.store.loadRecords({records: data}, {}, true);
35958     },
35959
35960     onUpdate : function(ds, record, type){
35961         if(type == Roo.data.Record.EDIT){
35962             var v = record.data['value'];
35963             var oldValue = record.modified['value'];
35964             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35965                 this.source[record.id] = v;
35966                 record.commit();
35967                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35968             }else{
35969                 record.reject();
35970             }
35971         }
35972     },
35973
35974     getProperty : function(row){
35975        return this.store.getAt(row);
35976     },
35977
35978     isEditableValue: function(val){
35979         if(val && val instanceof Date){
35980             return true;
35981         }else if(typeof val == 'object' || typeof val == 'function'){
35982             return false;
35983         }
35984         return true;
35985     },
35986
35987     setValue : function(prop, value){
35988         this.source[prop] = value;
35989         this.store.getById(prop).set('value', value);
35990     },
35991
35992     getSource : function(){
35993         return this.source;
35994     }
35995 });
35996
35997 Roo.grid.PropertyColumnModel = function(grid, store){
35998     this.grid = grid;
35999     var g = Roo.grid;
36000     g.PropertyColumnModel.superclass.constructor.call(this, [
36001         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36002         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36003     ]);
36004     this.store = store;
36005     this.bselect = Roo.DomHelper.append(document.body, {
36006         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36007             {tag: 'option', value: 'true', html: 'true'},
36008             {tag: 'option', value: 'false', html: 'false'}
36009         ]
36010     });
36011     Roo.id(this.bselect);
36012     var f = Roo.form;
36013     this.editors = {
36014         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36015         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36016         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36017         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36018         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36019     };
36020     this.renderCellDelegate = this.renderCell.createDelegate(this);
36021     this.renderPropDelegate = this.renderProp.createDelegate(this);
36022 };
36023
36024 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36025     
36026     
36027     nameText : 'Name',
36028     valueText : 'Value',
36029     
36030     dateFormat : 'm/j/Y',
36031     
36032     
36033     renderDate : function(dateVal){
36034         return dateVal.dateFormat(this.dateFormat);
36035     },
36036
36037     renderBool : function(bVal){
36038         return bVal ? 'true' : 'false';
36039     },
36040
36041     isCellEditable : function(colIndex, rowIndex){
36042         return colIndex == 1;
36043     },
36044
36045     getRenderer : function(col){
36046         return col == 1 ?
36047             this.renderCellDelegate : this.renderPropDelegate;
36048     },
36049
36050     renderProp : function(v){
36051         return this.getPropertyName(v);
36052     },
36053
36054     renderCell : function(val){
36055         var rv = val;
36056         if(val instanceof Date){
36057             rv = this.renderDate(val);
36058         }else if(typeof val == 'boolean'){
36059             rv = this.renderBool(val);
36060         }
36061         return Roo.util.Format.htmlEncode(rv);
36062     },
36063
36064     getPropertyName : function(name){
36065         var pn = this.grid.propertyNames;
36066         return pn && pn[name] ? pn[name] : name;
36067     },
36068
36069     getCellEditor : function(colIndex, rowIndex){
36070         var p = this.store.getProperty(rowIndex);
36071         var n = p.data['name'], val = p.data['value'];
36072         
36073         if(typeof(this.grid.customEditors[n]) == 'string'){
36074             return this.editors[this.grid.customEditors[n]];
36075         }
36076         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36077             return this.grid.customEditors[n];
36078         }
36079         if(val instanceof Date){
36080             return this.editors['date'];
36081         }else if(typeof val == 'number'){
36082             return this.editors['number'];
36083         }else if(typeof val == 'boolean'){
36084             return this.editors['boolean'];
36085         }else{
36086             return this.editors['string'];
36087         }
36088     }
36089 });
36090
36091 /**
36092  * @class Roo.grid.PropertyGrid
36093  * @extends Roo.grid.EditorGrid
36094  * This class represents the  interface of a component based property grid control.
36095  * <br><br>Usage:<pre><code>
36096  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36097       
36098  });
36099  // set any options
36100  grid.render();
36101  * </code></pre>
36102   
36103  * @constructor
36104  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36105  * The container MUST have some type of size defined for the grid to fill. The container will be
36106  * automatically set to position relative if it isn't already.
36107  * @param {Object} config A config object that sets properties on this grid.
36108  */
36109 Roo.grid.PropertyGrid = function(container, config){
36110     config = config || {};
36111     var store = new Roo.grid.PropertyStore(this);
36112     this.store = store;
36113     var cm = new Roo.grid.PropertyColumnModel(this, store);
36114     store.store.sort('name', 'ASC');
36115     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36116         ds: store.store,
36117         cm: cm,
36118         enableColLock:false,
36119         enableColumnMove:false,
36120         stripeRows:false,
36121         trackMouseOver: false,
36122         clicksToEdit:1
36123     }, config));
36124     this.getGridEl().addClass('x-props-grid');
36125     this.lastEditRow = null;
36126     this.on('columnresize', this.onColumnResize, this);
36127     this.addEvents({
36128          /**
36129              * @event beforepropertychange
36130              * Fires before a property changes (return false to stop?)
36131              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36132              * @param {String} id Record Id
36133              * @param {String} newval New Value
36134          * @param {String} oldval Old Value
36135              */
36136         "beforepropertychange": true,
36137         /**
36138              * @event propertychange
36139              * Fires after a property changes
36140              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36141              * @param {String} id Record Id
36142              * @param {String} newval New Value
36143          * @param {String} oldval Old Value
36144              */
36145         "propertychange": true
36146     });
36147     this.customEditors = this.customEditors || {};
36148 };
36149 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36150     
36151      /**
36152      * @cfg {Object} customEditors map of colnames=> custom editors.
36153      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36154      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36155      * false disables editing of the field.
36156          */
36157     
36158       /**
36159      * @cfg {Object} propertyNames map of property Names to their displayed value
36160          */
36161     
36162     render : function(){
36163         Roo.grid.PropertyGrid.superclass.render.call(this);
36164         this.autoSize.defer(100, this);
36165     },
36166
36167     autoSize : function(){
36168         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36169         if(this.view){
36170             this.view.fitColumns();
36171         }
36172     },
36173
36174     onColumnResize : function(){
36175         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36176         this.autoSize();
36177     },
36178     /**
36179      * Sets the data for the Grid
36180      * accepts a Key => Value object of all the elements avaiable.
36181      * @param {Object} data  to appear in grid.
36182      */
36183     setSource : function(source){
36184         this.store.setSource(source);
36185         //this.autoSize();
36186     },
36187     /**
36188      * Gets all the data from the grid.
36189      * @return {Object} data  data stored in grid
36190      */
36191     getSource : function(){
36192         return this.store.getSource();
36193     }
36194 });/*
36195  * Based on:
36196  * Ext JS Library 1.1.1
36197  * Copyright(c) 2006-2007, Ext JS, LLC.
36198  *
36199  * Originally Released Under LGPL - original licence link has changed is not relivant.
36200  *
36201  * Fork - LGPL
36202  * <script type="text/javascript">
36203  */
36204  
36205 /**
36206  * @class Roo.LoadMask
36207  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36208  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36209  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36210  * element's UpdateManager load indicator and will be destroyed after the initial load.
36211  * @constructor
36212  * Create a new LoadMask
36213  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36214  * @param {Object} config The config object
36215  */
36216 Roo.LoadMask = function(el, config){
36217     this.el = Roo.get(el);
36218     Roo.apply(this, config);
36219     if(this.store){
36220         this.store.on('beforeload', this.onBeforeLoad, this);
36221         this.store.on('load', this.onLoad, this);
36222         this.store.on('loadexception', this.onLoad, this);
36223         this.removeMask = false;
36224     }else{
36225         var um = this.el.getUpdateManager();
36226         um.showLoadIndicator = false; // disable the default indicator
36227         um.on('beforeupdate', this.onBeforeLoad, this);
36228         um.on('update', this.onLoad, this);
36229         um.on('failure', this.onLoad, this);
36230         this.removeMask = true;
36231     }
36232 };
36233
36234 Roo.LoadMask.prototype = {
36235     /**
36236      * @cfg {Boolean} removeMask
36237      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36238      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36239      */
36240     /**
36241      * @cfg {String} msg
36242      * The text to display in a centered loading message box (defaults to 'Loading...')
36243      */
36244     msg : 'Loading...',
36245     /**
36246      * @cfg {String} msgCls
36247      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36248      */
36249     msgCls : 'x-mask-loading',
36250
36251     /**
36252      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36253      * @type Boolean
36254      */
36255     disabled: false,
36256
36257     /**
36258      * Disables the mask to prevent it from being displayed
36259      */
36260     disable : function(){
36261        this.disabled = true;
36262     },
36263
36264     /**
36265      * Enables the mask so that it can be displayed
36266      */
36267     enable : function(){
36268         this.disabled = false;
36269     },
36270
36271     // private
36272     onLoad : function(){
36273         this.el.unmask(this.removeMask);
36274     },
36275
36276     // private
36277     onBeforeLoad : function(){
36278         if(!this.disabled){
36279             this.el.mask(this.msg, this.msgCls);
36280         }
36281     },
36282
36283     // private
36284     destroy : function(){
36285         if(this.store){
36286             this.store.un('beforeload', this.onBeforeLoad, this);
36287             this.store.un('load', this.onLoad, this);
36288             this.store.un('loadexception', this.onLoad, this);
36289         }else{
36290             var um = this.el.getUpdateManager();
36291             um.un('beforeupdate', this.onBeforeLoad, this);
36292             um.un('update', this.onLoad, this);
36293             um.un('failure', this.onLoad, this);
36294         }
36295     }
36296 };/*
36297  * Based on:
36298  * Ext JS Library 1.1.1
36299  * Copyright(c) 2006-2007, Ext JS, LLC.
36300  *
36301  * Originally Released Under LGPL - original licence link has changed is not relivant.
36302  *
36303  * Fork - LGPL
36304  * <script type="text/javascript">
36305  */
36306 Roo.XTemplate = function(){
36307     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36308     var s = this.html;
36309
36310     s = ['<tpl>', s, '</tpl>'].join('');
36311
36312     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36313
36314     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36315     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36316     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36317     var m, id = 0;
36318     var tpls = [];
36319
36320     while(m = s.match(re)){
36321        var m2 = m[0].match(nameRe);
36322        var m3 = m[0].match(ifRe);
36323        var m4 = m[0].match(execRe);
36324        var exp = null, fn = null, exec = null;
36325        var name = m2 && m2[1] ? m2[1] : '';
36326        if(m3){
36327            exp = m3 && m3[1] ? m3[1] : null;
36328            if(exp){
36329                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36330            }
36331        }
36332        if(m4){
36333            exp = m4 && m4[1] ? m4[1] : null;
36334            if(exp){
36335                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36336            }
36337        }
36338        if(name){
36339            switch(name){
36340                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36341                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36342                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36343            }
36344        }
36345        tpls.push({
36346             id: id,
36347             target: name,
36348             exec: exec,
36349             test: fn,
36350             body: m[1]||''
36351         });
36352        s = s.replace(m[0], '{xtpl'+ id + '}');
36353        ++id;
36354     }
36355     for(var i = tpls.length-1; i >= 0; --i){
36356         this.compileTpl(tpls[i]);
36357     }
36358     this.master = tpls[tpls.length-1];
36359     this.tpls = tpls;
36360 };
36361 Roo.extend(Roo.XTemplate, Roo.Template, {
36362
36363     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36364
36365     applySubTemplate : function(id, values, parent){
36366         var t = this.tpls[id];
36367         if(t.test && !t.test.call(this, values, parent)){
36368             return '';
36369         }
36370         if(t.exec && t.exec.call(this, values, parent)){
36371             return '';
36372         }
36373         var vs = t.target ? t.target.call(this, values, parent) : values;
36374         parent = t.target ? values : parent;
36375         if(t.target && vs instanceof Array){
36376             var buf = [];
36377             for(var i = 0, len = vs.length; i < len; i++){
36378                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36379             }
36380             return buf.join('');
36381         }
36382         return t.compiled.call(this, vs, parent);
36383     },
36384
36385     compileTpl : function(tpl){
36386         var fm = Roo.util.Format;
36387         var useF = this.disableFormats !== true;
36388         var sep = Roo.isGecko ? "+" : ",";
36389         var fn = function(m, name, format, args){
36390             if(name.substr(0, 4) == 'xtpl'){
36391                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36392             }
36393             var v;
36394             if(name.indexOf('.') != -1){
36395                 v = name;
36396             }else{
36397                 v = "values['" + name + "']";
36398             }
36399             if(format && useF){
36400                 args = args ? ',' + args : "";
36401                 if(format.substr(0, 5) != "this."){
36402                     format = "fm." + format + '(';
36403                 }else{
36404                     format = 'this.call("'+ format.substr(5) + '", ';
36405                     args = ", values";
36406                 }
36407             }else{
36408                 args= ''; format = "("+v+" === undefined ? '' : ";
36409             }
36410             return "'"+ sep + format + v + args + ")"+sep+"'";
36411         };
36412         var body;
36413         // branched to use + in gecko and [].join() in others
36414         if(Roo.isGecko){
36415             body = "tpl.compiled = function(values, parent){ return '" +
36416                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36417                     "';};";
36418         }else{
36419             body = ["tpl.compiled = function(values, parent){ return ['"];
36420             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36421             body.push("'].join('');};");
36422             body = body.join('');
36423         }
36424         /** eval:var:zzzzzzz */
36425         eval(body);
36426         return this;
36427     },
36428
36429     applyTemplate : function(values){
36430         return this.master.compiled.call(this, values, {});
36431         var s = this.subs;
36432     },
36433
36434     apply : function(){
36435         return this.applyTemplate.apply(this, arguments);
36436     },
36437
36438     compile : function(){return this;}
36439 });
36440
36441 Roo.XTemplate.from = function(el){
36442     el = Roo.getDom(el);
36443     return new Roo.XTemplate(el.value || el.innerHTML);
36444 };/*
36445  * Original code for Roojs - LGPL
36446  * <script type="text/javascript">
36447  */
36448  
36449 /**
36450  * @class Roo.XComponent
36451  * A delayed Element creator...
36452  * 
36453  * Mypart.xyx = new Roo.XComponent({
36454
36455     parent : 'Mypart.xyz', // empty == document.element.!!
36456     order : '001',
36457     name : 'xxxx'
36458     region : 'xxxx'
36459     disabled : function() {} 
36460      
36461     tree : function() { // return an tree of xtype declared components
36462         var MODULE = this;
36463         return 
36464         {
36465             xtype : 'NestedLayoutPanel',
36466             // technicall
36467         }
36468      ]
36469  *})
36470  * @extends Roo.util.Observable
36471  * @constructor
36472  * @param cfg {Object} configuration of component
36473  * 
36474  */
36475 Roo.XComponent = function(cfg) {
36476     Roo.apply(this, cfg);
36477     this.addEvents({ 
36478         /**
36479              * @event built
36480              * Fires when this the componnt is built
36481              * @param {Roo.XComponent} c the component
36482              */
36483         'built' : true,
36484         /**
36485              * @event buildcomplete
36486              * Fires on the top level element when all elements have been built
36487              * @param {Roo.XComponent} c the top level component.
36488          */
36489         'buildcomplete' : true
36490         
36491     });
36492     
36493     Roo.XComponent.register(this);
36494     this.modules = false;
36495     this.el = false; // where the layout goes..
36496     
36497     
36498 }
36499 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36500     /**
36501      * @property el
36502      * The created element (with Roo.factory())
36503      * @type {Roo.Layout}
36504      */
36505     el  : false,
36506     
36507     /**
36508      * @property el
36509      * for BC  - use el in new code
36510      * @type {Roo.Layout}
36511      */
36512     panel : false,
36513     
36514     /**
36515      * @property layout
36516      * for BC  - use el in new code
36517      * @type {Roo.Layout}
36518      */
36519     layout : false,
36520     
36521      /**
36522      * @cfg {Function|boolean} disabled
36523      * If this module is disabled by some rule, return true from the funtion
36524      */
36525     disabled : false,
36526     
36527     /**
36528      * @cfg {String} parent 
36529      * Name of parent element which it get xtype added to..
36530      */
36531     parent: false,
36532     
36533     /**
36534      * @cfg {String} order
36535      * Used to set the order in which elements are created (usefull for multiple tabs)
36536      */
36537     
36538     order : false,
36539     /**
36540      * @cfg {String} name
36541      * String to display while loading.
36542      */
36543     name : false,
36544     /**
36545      * @cfg {Array} items
36546      * A single item array - the first element is the root of the tree..
36547      * It's done this way to stay compatible with the Xtype system...
36548      */
36549     items : false
36550      
36551      
36552     
36553 });
36554
36555 Roo.apply(Roo.XComponent, {
36556     
36557     /**
36558      * @property  buildCompleted
36559      * True when the builder has completed building the interface.
36560      * @type Boolean
36561      */
36562     buildCompleted : false,
36563      
36564     /**
36565      * @property  topModule
36566      * the upper most module - uses document.element as it's constructor.
36567      * @type Object
36568      */
36569      
36570     topModule  : false,
36571       
36572     /**
36573      * @property  modules
36574      * array of modules to be created by registration system.
36575      * @type Roo.XComponent
36576      */
36577     
36578     modules : [],
36579       
36580     
36581     /**
36582      * Register components to be built later.
36583      *
36584      * This solves the following issues
36585      * - Building is not done on page load, but after an authentication process has occured.
36586      * - Interface elements are registered on page load
36587      * - Parent Interface elements may not be loaded before child, so this handles that..
36588      * 
36589      *
36590      * example:
36591      * 
36592      * MyApp.register({
36593           order : '000001',
36594           module : 'Pman.Tab.projectMgr',
36595           region : 'center',
36596           parent : 'Pman.layout',
36597           disabled : false,  // or use a function..
36598         })
36599      
36600      * * @param {Object} details about module
36601      */
36602     register : function(obj) {
36603         this.modules.push(obj);
36604          
36605     },
36606     /**
36607      * convert a string to an object..
36608      * 
36609      */
36610     
36611     toObject : function(str)
36612     {
36613         if (!str || typeof(str) == 'object') {
36614             return str;
36615         }
36616         var ar = str.split('.');
36617         var rt, o;
36618         rt = ar.shift();
36619             /** eval:var:o */
36620         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36621         if (o === false) {
36622             throw "Module not found : " + str;
36623         }
36624         Roo.each(ar, function(e) {
36625             if (typeof(o[e]) == 'undefined') {
36626                 throw "Module not found : " + str;
36627             }
36628             o = o[e];
36629         });
36630         return o;
36631         
36632     },
36633     
36634     
36635     /**
36636      * move modules into their correct place in the tree..
36637      * 
36638      */
36639     preBuild : function ()
36640     {
36641         
36642         Roo.each(this.modules , function (obj)
36643         {
36644             obj.parent = this.toObject(obj.parent);
36645             
36646             if (!obj.parent) {
36647                 this.topModule = obj;
36648                 return;
36649             }
36650             
36651             if (!obj.parent.modules) {
36652                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36653                     function(o) { return o.order + '' }
36654                 );
36655             }
36656             
36657             obj.parent.modules.add(obj);
36658         }, this);
36659     },
36660     
36661      /**
36662      * make a list of modules to build.
36663      * @return {Array} list of modules. 
36664      */ 
36665     
36666     buildOrder : function()
36667     {
36668         var _this = this;
36669         var cmp = function(a,b) {   
36670             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36671         };
36672         
36673         if (!this.topModule || !this.topModule.modules) {
36674             throw "No top level modules to build";
36675         }
36676        
36677         // make a flat list in order of modules to build.
36678         var mods = [ this.topModule ];
36679         
36680         
36681         // add modules to their parents..
36682         var addMod = function(m) {
36683            // Roo.debug && Roo.log(m.modKey);
36684             
36685             mods.push(m);
36686             if (m.modules) {
36687                 m.modules.keySort('ASC',  cmp );
36688                 m.modules.each(addMod);
36689             }
36690             // not sure if this is used any more..
36691             if (m.finalize) {
36692                 m.finalize.name = m.name + " (clean up) ";
36693                 mods.push(m.finalize);
36694             }
36695             
36696         }
36697         this.topModule.modules.keySort('ASC',  cmp );
36698         this.topModule.modules.each(addMod);
36699         return mods;
36700     },
36701     
36702      /**
36703      * Build the registered modules.
36704      * @param {Object} parent element.
36705      * @param {Function} optional method to call after module has been added.
36706      * 
36707      */ 
36708    
36709     build : function() 
36710     {
36711         
36712         this.preBuild();
36713         var mods = this.buildOrder();
36714       
36715         //this.allmods = mods;
36716         //Roo.debug && Roo.log(mods);
36717         //return;
36718         if (!mods.length) { // should not happen
36719             throw "NO modules!!!";
36720         }
36721         
36722         
36723         
36724         // flash it up as modal - so we store the mask!?
36725         Roo.MessageBox.show({ title: 'loading' });
36726         Roo.MessageBox.show({
36727            title: "Please wait...",
36728            msg: "Building Interface...",
36729            width:450,
36730            progress:true,
36731            closable:false,
36732            modal: false
36733           
36734         });
36735         var total = mods.length;
36736         
36737         var _this = this;
36738         var progressRun = function() {
36739             if (!mods.length) {
36740                 Roo.debug && Roo.log('hide?');
36741                 Roo.MessageBox.hide();
36742                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36743                 return;    
36744             }
36745             
36746             var m = mods.shift();
36747             Roo.debug && Roo.log(m);
36748             if (typeof(m) == 'function') { // not sure if this is supported any more..
36749                 m.call(this);
36750                 return progressRun.defer(10, _this);
36751             } 
36752             
36753             Roo.MessageBox.updateProgress(
36754                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36755                     " of " + total + 
36756                     (m.name ? (' - ' + m.name) : '')
36757                     );
36758             
36759          
36760             
36761             var disabled = (typeof(m.disabled) == 'function') ?
36762                 m.disabled.call(m.module.disabled) : m.disabled;    
36763             
36764             
36765             if (disabled) {
36766                 return progressRun(); // we do not update the display!
36767             }
36768             
36769             if (!m.parent) {
36770                 // it's a top level one..
36771                 var layoutbase = new Ext.BorderLayout(document.body, {
36772                
36773                     center: {
36774                          titlebar: false,
36775                          autoScroll:false,
36776                          closeOnTab: true,
36777                          tabPosition: 'top',
36778                          //resizeTabs: true,
36779                          alwaysShowTabs: true,
36780                          minTabWidth: 140
36781                     }
36782                 });
36783                 var tree = m.tree();
36784                 tree.region = 'center';
36785                 m.el = layoutbase.addxtype(tree);
36786                 m.panel = m.el;
36787                 m.layout = m.panel.layout;    
36788                 return progressRun.defer(10, _this);
36789             }
36790             
36791             var tree = m.tree();
36792             tree.region = tree.region || m.region;
36793             m.el = m.parent.el.addxtype(tree);
36794             m.fireEvent('built', m);
36795             m.panel = m.el;
36796             m.layout = m.panel.layout;    
36797             progressRun.defer(10, _this); 
36798             
36799         }
36800         progressRun.defer(1, _this);
36801      
36802         
36803         
36804     }
36805      
36806    
36807     
36808     
36809 });
36810  //<script type="text/javascript">
36811
36812
36813 /**
36814  * @class Roo.Login
36815  * @extends Roo.LayoutDialog
36816  * A generic Login Dialog..... - only one needed in theory!?!?
36817  *
36818  * Fires XComponent builder on success...
36819  * 
36820  * Sends 
36821  *    username,password, lang = for login actions.
36822  *    check = 1 for periodic checking that sesion is valid.
36823  *    passwordRequest = email request password
36824  *    logout = 1 = to logout
36825  * 
36826  * Affects: (this id="????" elements)
36827  *   loading  (removed) (used to indicate application is loading)
36828  *   loading-mask (hides) (used to hide application when it's building loading)
36829  *   
36830  * 
36831  * Usage: 
36832  *    
36833  * 
36834  * Myapp.login = Roo.Login({
36835      url: xxxx,
36836    
36837      realm : 'Myapp', 
36838      
36839      
36840      method : 'POST',
36841      
36842      
36843      * 
36844  })
36845  * 
36846  * 
36847  * 
36848  **/
36849  
36850 Roo.Login = function(cfg)
36851 {
36852     this.addEvents({
36853         'refreshed' : true
36854     });
36855     
36856     Roo.apply(this,cfg);
36857     
36858     Roo.onReady(function() {
36859         this.onLoad();
36860     }, this);
36861     // call parent..
36862     
36863    
36864     Roo.Login.superclass.constructor.call(this, this);
36865     //this.addxtype(this.items[0]);
36866     
36867     
36868 }
36869
36870
36871 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36872     
36873     /**
36874      * @cfg {String} method
36875      * Method used to query for login details.
36876      */
36877     
36878     method : 'POST',
36879     /**
36880      * @cfg {String} url
36881      * URL to query login data. - eg. baseURL + '/Login.php'
36882      */
36883     url : '',
36884     
36885     /**
36886      * @property user
36887      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36888      * @type {Object} 
36889      */
36890     user : false,
36891     /**
36892      * @property checkFails
36893      * Number of times we have attempted to get authentication check, and failed.
36894      * @type {Number} 
36895      */
36896     checkFails : 0,
36897       /**
36898      * @property intervalID
36899      * The window interval that does the constant login checking.
36900      * @type {Number} 
36901      */
36902     intervalID : 0,
36903     
36904     
36905     onLoad : function() // called on page load...
36906     {
36907         // load 
36908          
36909         if (Roo.get('loading')) { // clear any loading indicator..
36910             Roo.get('loading').remove();
36911         }
36912         
36913         //this.switchLang('en'); // set the language to english..
36914        
36915         this.check({
36916             success:  function(response, opts)  {  // check successfull...
36917             
36918                 var res = this.processResponse(response);
36919                 this.checkFails =0;
36920                 if (!res.success) { // error!
36921                     this.checkFails = 5;
36922                     //console.log('call failure');
36923                     return this.failure(response,opts);
36924                 }
36925                 
36926                 if (!res.data.id) { // id=0 == login failure.
36927                     return this.show();
36928                 }
36929                 
36930                               
36931                         //console.log(success);
36932                 this.fillAuth(res.data);   
36933                 this.checkFails =0;
36934                 Roo.XComponent.build();
36935             },
36936             failure : this.show
36937         });
36938         
36939     }, 
36940     
36941     
36942     check: function(cfg) // called every so often to refresh cookie etc..
36943     {
36944         if (cfg.again) { // could be undefined..
36945             this.checkFails++;
36946         } else {
36947             this.checkFails = 0;
36948         }
36949         var _this = this;
36950         if (this.sending) {
36951             if ( this.checkFails > 4) {
36952                 Roo.MessageBox.alert("Error",  
36953                     "Error getting authentication status. - try reloading, or wait a while", function() {
36954                         _this.sending = false;
36955                     }); 
36956                 return;
36957             }
36958             cfg.again = true;
36959             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36960             return;
36961         }
36962         this.sending = true;
36963         
36964         Roo.Ajax.request({  
36965             url: this.url,
36966             params: {
36967                 getAuthUser: true
36968             },  
36969             method: this.method,
36970             success:  cfg.success || this.success,
36971             failure : cfg.failure || this.failure,
36972             scope : this,
36973             callCfg : cfg
36974               
36975         });  
36976     }, 
36977     
36978     
36979     logout: function()
36980     {
36981         window.onbeforeunload = function() { }; // false does not work for IE..
36982         this.user = false;
36983         var _this = this;
36984         
36985         Roo.Ajax.request({  
36986             url: this.url,
36987             params: {
36988                 logout: 1
36989             },  
36990             method: 'GET',
36991             failure : function() {
36992                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36993                     document.location = document.location.toString() + '?ts=' + Math.random();
36994                 });
36995                 
36996             },
36997             success : function() {
36998                 _this.user = false;
36999                 this.checkFails =0;
37000                 // fixme..
37001                 document.location = document.location.toString() + '?ts=' + Math.random();
37002             }
37003               
37004               
37005         }); 
37006     },
37007     
37008     processResponse : function (response)
37009     {
37010         var res = '';
37011         try {
37012             res = Roo.decode(response.responseText);
37013             // oops...
37014             if (typeof(res) != 'object') {
37015                 res = { success : false, errorMsg : res, errors : true };
37016             }
37017             if (typeof(res.success) == 'undefined') {
37018                 res.success = false;
37019             }
37020             
37021         } catch(e) {
37022             res = { success : false,  errorMsg : response.responseText, errors : true };
37023         }
37024         return res;
37025     },
37026     
37027     success : function(response, opts)  // check successfull...
37028     {  
37029         this.sending = false;
37030         var res = this.processResponse(response);
37031         if (!res.success) {
37032             return this.failure(response, opts);
37033         }
37034         if (!res.data || !res.data.id) {
37035             return this.failure(response,opts);
37036         }
37037         //console.log(res);
37038         this.fillAuth(res.data);
37039         
37040         this.checkFails =0;
37041         
37042     },
37043     
37044     
37045     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37046     {
37047         this.authUser = -1;
37048         this.sending = false;
37049         var res = this.processResponse(response);
37050         //console.log(res);
37051         if ( this.checkFails > 2) {
37052         
37053             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37054                 "Error getting authentication status. - try reloading"); 
37055             return;
37056         }
37057         opts.callCfg.again = true;
37058         this.check.defer(1000, this, [ opts.callCfg ]);
37059         return;  
37060     },
37061     
37062     
37063     
37064     fillAuth: function(au) {
37065         this.startAuthCheck();
37066         this.authUserId = au.id;
37067         this.authUser = au;
37068         this.lastChecked = new Date();
37069         this.fireEvent('refreshed', au);
37070         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37071         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37072         au.lang = au.lang || 'en';
37073         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37074         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37075         this.switchLang(au.lang );
37076         
37077      
37078         // open system... - -on setyp..
37079         if (this.authUserId  < 0) {
37080             Roo.MessageBox.alert("Warning", 
37081                 "This is an open system - please set up a admin user with a password.");  
37082         }
37083          
37084         //Pman.onload(); // which should do nothing if it's a re-auth result...
37085         
37086              
37087     },
37088     
37089     startAuthCheck : function() // starter for timeout checking..
37090     {
37091         if (this.intervalID) { // timer already in place...
37092             return false;
37093         }
37094         var _this = this;
37095         this.intervalID =  window.setInterval(function() {
37096               _this.check(false);
37097             }, 120000); // every 120 secs = 2mins..
37098         
37099         
37100     },
37101          
37102     
37103     switchLang : function (lang) 
37104     {
37105         _T = typeof(_T) == 'undefined' ? false : _T;
37106           if (!_T || !lang.length) {
37107             return;
37108         }
37109         
37110         if (!_T && lang != 'en') {
37111             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37112             return;
37113         }
37114         
37115         if (typeof(_T.en) == 'undefined') {
37116             _T.en = {};
37117             Roo.apply(_T.en, _T);
37118         }
37119         
37120         if (typeof(_T[lang]) == 'undefined') {
37121             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37122             return;
37123         }
37124         
37125         
37126         Roo.apply(_T, _T[lang]);
37127         // just need to set the text values for everything...
37128         var _this = this;
37129         /* this will not work ...
37130         if (this.form) { 
37131             
37132                
37133             function formLabel(name, val) {
37134                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37135             }
37136             
37137             formLabel('password', "Password"+':');
37138             formLabel('username', "Email Address"+':');
37139             formLabel('lang', "Language"+':');
37140             this.dialog.setTitle("Login");
37141             this.dialog.buttons[0].setText("Forgot Password");
37142             this.dialog.buttons[1].setText("Login");
37143         }
37144         */
37145         
37146         
37147     },
37148     
37149     
37150     title: "Login",
37151     modal: true,
37152     width:  350,
37153     //height: 230,
37154     height: 180,
37155     shadow: true,
37156     minWidth:200,
37157     minHeight:180,
37158     //proxyDrag: true,
37159     closable: false,
37160     draggable: false,
37161     collapsible: false,
37162     resizable: false,
37163     center: {  // needed??
37164         autoScroll:false,
37165         titlebar: false,
37166        // tabPosition: 'top',
37167         hideTabs: true,
37168         closeOnTab: true,
37169         alwaysShowTabs: false
37170     } ,
37171     listeners : {
37172         
37173         show  : function(dlg)
37174         {
37175             //console.log(this);
37176             this.form = this.layout.getRegion('center').activePanel.form;
37177             this.form.dialog = dlg;
37178             this.buttons[0].form = this.form;
37179             this.buttons[0].dialog = dlg;
37180             this.buttons[1].form = this.form;
37181             this.buttons[1].dialog = dlg;
37182            
37183            //this.resizeToLogo.defer(1000,this);
37184             // this is all related to resizing for logos..
37185             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37186            //// if (!sz) {
37187              //   this.resizeToLogo.defer(1000,this);
37188              //   return;
37189            // }
37190             //var w = Ext.lib.Dom.getViewWidth() - 100;
37191             //var h = Ext.lib.Dom.getViewHeight() - 100;
37192             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37193             //this.center();
37194             if (this.disabled) {
37195                 this.hide();
37196                 return;
37197             }
37198             
37199             if (this.user.id < 0) { // used for inital setup situations.
37200                 return;
37201             }
37202             
37203             if (this.intervalID) {
37204                 // remove the timer
37205                 window.clearInterval(this.intervalID);
37206                 this.intervalID = false;
37207             }
37208             
37209             
37210             if (Roo.get('loading')) {
37211                 Roo.get('loading').remove();
37212             }
37213             if (Roo.get('loading-mask')) {
37214                 Roo.get('loading-mask').hide();
37215             }
37216             
37217             //incomming._node = tnode;
37218             this.form.reset();
37219             //this.dialog.modal = !modal;
37220             //this.dialog.show();
37221             this.el.unmask(); 
37222             
37223             
37224             this.form.setValues({
37225                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37226                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37227             });
37228             
37229             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37230             if (this.form.findField('username').getValue().length > 0 ){
37231                 this.form.findField('password').focus();
37232             } else {
37233                this.form.findField('username').focus();
37234             }
37235     
37236         }
37237     },
37238     items : [
37239          {
37240        
37241             xtype : 'ContentPanel',
37242             xns : Roo,
37243             region: 'center',
37244             fitToFrame : true,
37245             
37246             items : [
37247     
37248                 {
37249                
37250                     xtype : 'Form',
37251                     xns : Roo.form,
37252                     labelWidth: 100,
37253                     style : 'margin: 10px;',
37254                     
37255                     listeners : {
37256                         actionfailed : function(f, act) {
37257                             // form can return { errors: .... }
37258                                 
37259                             //act.result.errors // invalid form element list...
37260                             //act.result.errorMsg// invalid form element list...
37261                             
37262                             this.dialog.el.unmask();
37263                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37264                                         "Login failed - communication error - try again.");
37265                                       
37266                         },
37267                         actioncomplete: function(re, act) {
37268                              
37269                             Roo.state.Manager.set(
37270                                 this.dialog.realm + '.username',  
37271                                     this.findField('username').getValue()
37272                             );
37273                             Roo.state.Manager.set(
37274                                 this.dialog.realm + '.lang',  
37275                                 this.findField('lang').getValue() 
37276                             );
37277                             
37278                             this.dialog.fillAuth(act.result.data);
37279                               
37280                             this.dialog.hide();
37281                             
37282                             if (Roo.get('loading-mask')) {
37283                                 Roo.get('loading-mask').show();
37284                             }
37285                             Roo.XComponent.build();
37286                             
37287                              
37288                             
37289                         }
37290                     },
37291                     items : [
37292                         {
37293                             xtype : 'TextField',
37294                             xns : Roo.form,
37295                             fieldLabel: "Email Address",
37296                             name: 'username',
37297                             width:200,
37298                             autoCreate : {tag: "input", type: "text", size: "20"}
37299                         },
37300                         {
37301                             xtype : 'TextField',
37302                             xns : Roo.form,
37303                             fieldLabel: "Password",
37304                             inputType: 'password',
37305                             name: 'password',
37306                             width:200,
37307                             autoCreate : {tag: "input", type: "text", size: "20"},
37308                             listeners : {
37309                                 specialkey : function(e,ev) {
37310                                     if (ev.keyCode == 13) {
37311                                         this.form.dialog.el.mask("Logging in");
37312                                         this.form.doAction('submit', {
37313                                             url: this.form.dialog.url,
37314                                             method: this.form.dialog.method
37315                                         });
37316                                     }
37317                                 }
37318                             }  
37319                         },
37320                         {
37321                             xtype : 'ComboBox',
37322                             xns : Roo.form,
37323                             fieldLabel: "Language",
37324                             name : 'langdisp',
37325                             store: {
37326                                 xtype : 'SimpleStore',
37327                                 fields: ['lang', 'ldisp'],
37328                                 data : [
37329                                     [ 'en', 'English' ],
37330                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37331                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37332                                 ]
37333                             },
37334                             
37335                             valueField : 'lang',
37336                             hiddenName:  'lang',
37337                             width: 200,
37338                             displayField:'ldisp',
37339                             typeAhead: false,
37340                             editable: false,
37341                             mode: 'local',
37342                             triggerAction: 'all',
37343                             emptyText:'Select a Language...',
37344                             selectOnFocus:true,
37345                             listeners : {
37346                                 select :  function(cb, rec, ix) {
37347                                     this.form.switchLang(rec.data.lang);
37348                                 }
37349                             }
37350                         
37351                         }
37352                     ]
37353                 }
37354                   
37355                 
37356             ]
37357         }
37358     ],
37359     buttons : [
37360         {
37361             xtype : 'Button',
37362             xns : 'Roo',
37363             text : "Forgot Password",
37364             listeners : {
37365                 click : function() {
37366                     //console.log(this);
37367                     var n = this.form.findField('username').getValue();
37368                     if (!n.length) {
37369                         Roo.MessageBox.alert("Error", "Fill in your email address");
37370                         return;
37371                     }
37372                     Roo.Ajax.request({
37373                         url: this.dialog.url,
37374                         params: {
37375                             passwordRequest: n
37376                         },
37377                         method: this.dialog.method,
37378                         success:  function(response, opts)  {  // check successfull...
37379                         
37380                             var res = this.dialog.processResponse(response);
37381                             if (!res.success) { // error!
37382                                Roo.MessageBox.alert("Error" ,
37383                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37384                                return;
37385                             }
37386                             Roo.MessageBox.alert("Notice" ,
37387                                 "Please check you email for the Password Reset message");
37388                         },
37389                         failure : function() {
37390                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37391                         }
37392                         
37393                     });
37394                 }
37395             }
37396         },
37397         {
37398             xtype : 'Button',
37399             xns : 'Roo',
37400             text : "Login",
37401             listeners : {
37402                 
37403                 click : function () {
37404                         
37405                     this.dialog.el.mask("Logging in");
37406                     this.form.doAction('submit', {
37407                             url: this.dialog.url,
37408                             method: this.dialog.method
37409                     });
37410                 }
37411             }
37412         }
37413     ]
37414   
37415   
37416 })
37417  
37418
37419
37420