Rebuild documentation and compressed files.
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157     /**
9158      * @event beforeclick
9159      * Fires before a click is processed. Returns false to cancel the default action.
9160      * @param {Roo.View} this
9161      * @param {Number} index The index of the target node
9162      * @param {HTMLElement} node The target node
9163      * @param {Roo.EventObject} e The raw event object
9164      */
9165         "beforeclick" : true,
9166     /**
9167      * @event click
9168      * Fires when a template node is clicked.
9169      * @param {Roo.View} this
9170      * @param {Number} index The index of the target node
9171      * @param {HTMLElement} node The target node
9172      * @param {Roo.EventObject} e The raw event object
9173      */
9174         "click" : true,
9175     /**
9176      * @event dblclick
9177      * Fires when a template node is double clicked.
9178      * @param {Roo.View} this
9179      * @param {Number} index The index of the target node
9180      * @param {HTMLElement} node The target node
9181      * @param {Roo.EventObject} e The raw event object
9182      */
9183         "dblclick" : true,
9184     /**
9185      * @event contextmenu
9186      * Fires when a template node is right clicked.
9187      * @param {Roo.View} this
9188      * @param {Number} index The index of the target node
9189      * @param {HTMLElement} node The target node
9190      * @param {Roo.EventObject} e The raw event object
9191      */
9192         "contextmenu" : true,
9193     /**
9194      * @event selectionchange
9195      * Fires when the selected nodes change.
9196      * @param {Roo.View} this
9197      * @param {Array} selections Array of the selected nodes
9198      */
9199         "selectionchange" : true,
9200
9201     /**
9202      * @event beforeselect
9203      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204      * @param {Roo.View} this
9205      * @param {HTMLElement} node The node to be selected
9206      * @param {Array} selections Array of currently selected nodes
9207      */
9208         "beforeselect" : true
9209     });
9210
9211     this.el.on({
9212         "click": this.onClick,
9213         "dblclick": this.onDblClick,
9214         "contextmenu": this.onContextMenu,
9215         scope:this
9216     });
9217
9218     this.selections = [];
9219     this.nodes = [];
9220     this.cmp = new Roo.CompositeElementLite([]);
9221     if(this.store){
9222         this.store = Roo.factory(this.store, Roo.data);
9223         this.setStore(this.store, true);
9224     }
9225     Roo.View.superclass.constructor.call(this);
9226 };
9227
9228 Roo.extend(Roo.View, Roo.util.Observable, {
9229     
9230      /**
9231      * @cfg {Roo.data.Store} store Data store to load data from.
9232      */
9233     store : false,
9234     
9235     /**
9236      * @cfg {String|Roo.Element} el The container element.
9237      */
9238     el : '',
9239     
9240     /**
9241      * @cfg {String|Roo.Template} tpl The template used by this View 
9242      */
9243     tpl : false,
9244     
9245     /**
9246      * @cfg {String} selectedClass The css class to add to selected nodes
9247      */
9248     selectedClass : "x-view-selected",
9249      /**
9250      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9251      */
9252     emptyText : "",
9253     /**
9254      * @cfg {Boolean} multiSelect Allow multiple selection
9255      */
9256     
9257     multiSelect : false,
9258     /**
9259      * @cfg {Boolean} singleSelect Allow single selection
9260      */
9261     singleSelect:  false,
9262     
9263     /**
9264      * Returns the element this view is bound to.
9265      * @return {Roo.Element}
9266      */
9267     getEl : function(){
9268         return this.el;
9269     },
9270
9271     /**
9272      * Refreshes the view.
9273      */
9274     refresh : function(){
9275         var t = this.tpl;
9276         this.clearSelections();
9277         this.el.update("");
9278         var html = [];
9279         var records = this.store.getRange();
9280         if(records.length < 1){
9281             this.el.update(this.emptyText);
9282             return;
9283         }
9284         for(var i = 0, len = records.length; i < len; i++){
9285             var data = this.prepareData(records[i].data, i, records[i]);
9286             html[html.length] = t.apply(data);
9287         }
9288         this.el.update(html.join(""));
9289         this.nodes = this.el.dom.childNodes;
9290         this.updateIndexes(0);
9291     },
9292
9293     /**
9294      * Function to override to reformat the data that is sent to
9295      * the template for each node.
9296      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9297      * a JSON object for an UpdateManager bound view).
9298      */
9299     prepareData : function(data){
9300         return data;
9301     },
9302
9303     onUpdate : function(ds, record){
9304         this.clearSelections();
9305         var index = this.store.indexOf(record);
9306         var n = this.nodes[index];
9307         this.tpl.insertBefore(n, this.prepareData(record.data));
9308         n.parentNode.removeChild(n);
9309         this.updateIndexes(index, index);
9310     },
9311
9312     onAdd : function(ds, records, index){
9313         this.clearSelections();
9314         if(this.nodes.length == 0){
9315             this.refresh();
9316             return;
9317         }
9318         var n = this.nodes[index];
9319         for(var i = 0, len = records.length; i < len; i++){
9320             var d = this.prepareData(records[i].data);
9321             if(n){
9322                 this.tpl.insertBefore(n, d);
9323             }else{
9324                 this.tpl.append(this.el, d);
9325             }
9326         }
9327         this.updateIndexes(index);
9328     },
9329
9330     onRemove : function(ds, record, index){
9331         this.clearSelections();
9332         this.el.dom.removeChild(this.nodes[index]);
9333         this.updateIndexes(index);
9334     },
9335
9336     /**
9337      * Refresh an individual node.
9338      * @param {Number} index
9339      */
9340     refreshNode : function(index){
9341         this.onUpdate(this.store, this.store.getAt(index));
9342     },
9343
9344     updateIndexes : function(startIndex, endIndex){
9345         var ns = this.nodes;
9346         startIndex = startIndex || 0;
9347         endIndex = endIndex || ns.length - 1;
9348         for(var i = startIndex; i <= endIndex; i++){
9349             ns[i].nodeIndex = i;
9350         }
9351     },
9352
9353     /**
9354      * Changes the data store this view uses and refresh the view.
9355      * @param {Store} store
9356      */
9357     setStore : function(store, initial){
9358         if(!initial && this.store){
9359             this.store.un("datachanged", this.refresh);
9360             this.store.un("add", this.onAdd);
9361             this.store.un("remove", this.onRemove);
9362             this.store.un("update", this.onUpdate);
9363             this.store.un("clear", this.refresh);
9364         }
9365         if(store){
9366           
9367             store.on("datachanged", this.refresh, this);
9368             store.on("add", this.onAdd, this);
9369             store.on("remove", this.onRemove, this);
9370             store.on("update", this.onUpdate, this);
9371             store.on("clear", this.refresh, this);
9372         }
9373         
9374         if(store){
9375             this.refresh();
9376         }
9377     },
9378
9379     /**
9380      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9381      * @param {HTMLElement} node
9382      * @return {HTMLElement} The template node
9383      */
9384     findItemFromChild : function(node){
9385         var el = this.el.dom;
9386         if(!node || node.parentNode == el){
9387                     return node;
9388             }
9389             var p = node.parentNode;
9390             while(p && p != el){
9391             if(p.parentNode == el){
9392                 return p;
9393             }
9394             p = p.parentNode;
9395         }
9396             return null;
9397     },
9398
9399     /** @ignore */
9400     onClick : function(e){
9401         var item = this.findItemFromChild(e.getTarget());
9402         if(item){
9403             var index = this.indexOf(item);
9404             if(this.onItemClick(item, index, e) !== false){
9405                 this.fireEvent("click", this, index, item, e);
9406             }
9407         }else{
9408             this.clearSelections();
9409         }
9410     },
9411
9412     /** @ignore */
9413     onContextMenu : function(e){
9414         var item = this.findItemFromChild(e.getTarget());
9415         if(item){
9416             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9417         }
9418     },
9419
9420     /** @ignore */
9421     onDblClick : function(e){
9422         var item = this.findItemFromChild(e.getTarget());
9423         if(item){
9424             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9425         }
9426     },
9427
9428     onItemClick : function(item, index, e){
9429         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9430             return false;
9431         }
9432         if(this.multiSelect || this.singleSelect){
9433             if(this.multiSelect && e.shiftKey && this.lastSelection){
9434                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9435             }else{
9436                 this.select(item, this.multiSelect && e.ctrlKey);
9437                 this.lastSelection = item;
9438             }
9439             e.preventDefault();
9440         }
9441         return true;
9442     },
9443
9444     /**
9445      * Get the number of selected nodes.
9446      * @return {Number}
9447      */
9448     getSelectionCount : function(){
9449         return this.selections.length;
9450     },
9451
9452     /**
9453      * Get the currently selected nodes.
9454      * @return {Array} An array of HTMLElements
9455      */
9456     getSelectedNodes : function(){
9457         return this.selections;
9458     },
9459
9460     /**
9461      * Get the indexes of the selected nodes.
9462      * @return {Array}
9463      */
9464     getSelectedIndexes : function(){
9465         var indexes = [], s = this.selections;
9466         for(var i = 0, len = s.length; i < len; i++){
9467             indexes.push(s[i].nodeIndex);
9468         }
9469         return indexes;
9470     },
9471
9472     /**
9473      * Clear all selections
9474      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9475      */
9476     clearSelections : function(suppressEvent){
9477         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9478             this.cmp.elements = this.selections;
9479             this.cmp.removeClass(this.selectedClass);
9480             this.selections = [];
9481             if(!suppressEvent){
9482                 this.fireEvent("selectionchange", this, this.selections);
9483             }
9484         }
9485     },
9486
9487     /**
9488      * Returns true if the passed node is selected
9489      * @param {HTMLElement/Number} node The node or node index
9490      * @return {Boolean}
9491      */
9492     isSelected : function(node){
9493         var s = this.selections;
9494         if(s.length < 1){
9495             return false;
9496         }
9497         node = this.getNode(node);
9498         return s.indexOf(node) !== -1;
9499     },
9500
9501     /**
9502      * Selects nodes.
9503      * @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
9504      * @param {Boolean} keepExisting (optional) true to keep existing selections
9505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9506      */
9507     select : function(nodeInfo, keepExisting, suppressEvent){
9508         if(nodeInfo instanceof Array){
9509             if(!keepExisting){
9510                 this.clearSelections(true);
9511             }
9512             for(var i = 0, len = nodeInfo.length; i < len; i++){
9513                 this.select(nodeInfo[i], true, true);
9514             }
9515         } else{
9516             var node = this.getNode(nodeInfo);
9517             if(node && !this.isSelected(node)){
9518                 if(!keepExisting){
9519                     this.clearSelections(true);
9520                 }
9521                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9522                     Roo.fly(node).addClass(this.selectedClass);
9523                     this.selections.push(node);
9524                     if(!suppressEvent){
9525                         this.fireEvent("selectionchange", this, this.selections);
9526                     }
9527                 }
9528             }
9529         }
9530     },
9531
9532     /**
9533      * Gets a template node.
9534      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9535      * @return {HTMLElement} The node or null if it wasn't found
9536      */
9537     getNode : function(nodeInfo){
9538         if(typeof nodeInfo == "string"){
9539             return document.getElementById(nodeInfo);
9540         }else if(typeof nodeInfo == "number"){
9541             return this.nodes[nodeInfo];
9542         }
9543         return nodeInfo;
9544     },
9545
9546     /**
9547      * Gets a range template nodes.
9548      * @param {Number} startIndex
9549      * @param {Number} endIndex
9550      * @return {Array} An array of nodes
9551      */
9552     getNodes : function(start, end){
9553         var ns = this.nodes;
9554         start = start || 0;
9555         end = typeof end == "undefined" ? ns.length - 1 : end;
9556         var nodes = [];
9557         if(start <= end){
9558             for(var i = start; i <= end; i++){
9559                 nodes.push(ns[i]);
9560             }
9561         } else{
9562             for(var i = start; i >= end; i--){
9563                 nodes.push(ns[i]);
9564             }
9565         }
9566         return nodes;
9567     },
9568
9569     /**
9570      * Finds the index of the passed node
9571      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9572      * @return {Number} The index of the node or -1
9573      */
9574     indexOf : function(node){
9575         node = this.getNode(node);
9576         if(typeof node.nodeIndex == "number"){
9577             return node.nodeIndex;
9578         }
9579         var ns = this.nodes;
9580         for(var i = 0, len = ns.length; i < len; i++){
9581             if(ns[i] == node){
9582                 return i;
9583             }
9584         }
9585         return -1;
9586     }
9587 });
9588 /*
9589  * Based on:
9590  * Ext JS Library 1.1.1
9591  * Copyright(c) 2006-2007, Ext JS, LLC.
9592  *
9593  * Originally Released Under LGPL - original licence link has changed is not relivant.
9594  *
9595  * Fork - LGPL
9596  * <script type="text/javascript">
9597  */
9598
9599 /**
9600  * @class Roo.JsonView
9601  * @extends Roo.View
9602  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9603 <pre><code>
9604 var view = new Roo.JsonView({
9605     container: "my-element",
9606     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9607     multiSelect: true, 
9608     jsonRoot: "data" 
9609 });
9610
9611 // listen for node click?
9612 view.on("click", function(vw, index, node, e){
9613     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9614 });
9615
9616 // direct load of JSON data
9617 view.load("foobar.php");
9618
9619 // Example from my blog list
9620 var tpl = new Roo.Template(
9621     '&lt;div class="entry"&gt;' +
9622     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9623     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9624     "&lt;/div&gt;&lt;hr /&gt;"
9625 );
9626
9627 var moreView = new Roo.JsonView({
9628     container :  "entry-list", 
9629     template : tpl,
9630     jsonRoot: "posts"
9631 });
9632 moreView.on("beforerender", this.sortEntries, this);
9633 moreView.load({
9634     url: "/blog/get-posts.php",
9635     params: "allposts=true",
9636     text: "Loading Blog Entries..."
9637 });
9638 </code></pre>
9639
9640 * Note: old code is supported with arguments : (container, template, config)
9641
9642
9643  * @constructor
9644  * Create a new JsonView
9645  * 
9646  * @param {Object} config The config object
9647  * 
9648  */
9649 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9650     
9651     
9652     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9653
9654     var um = this.el.getUpdateManager();
9655     um.setRenderer(this);
9656     um.on("update", this.onLoad, this);
9657     um.on("failure", this.onLoadException, this);
9658
9659     /**
9660      * @event beforerender
9661      * Fires before rendering of the downloaded JSON data.
9662      * @param {Roo.JsonView} this
9663      * @param {Object} data The JSON data loaded
9664      */
9665     /**
9666      * @event load
9667      * Fires when data is loaded.
9668      * @param {Roo.JsonView} this
9669      * @param {Object} data The JSON data loaded
9670      * @param {Object} response The raw Connect response object
9671      */
9672     /**
9673      * @event loadexception
9674      * Fires when loading fails.
9675      * @param {Roo.JsonView} this
9676      * @param {Object} response The raw Connect response object
9677      */
9678     this.addEvents({
9679         'beforerender' : true,
9680         'load' : true,
9681         'loadexception' : true
9682     });
9683 };
9684 Roo.extend(Roo.JsonView, Roo.View, {
9685     /**
9686      * @type {String} The root property in the loaded JSON object that contains the data
9687      */
9688     jsonRoot : "",
9689
9690     /**
9691      * Refreshes the view.
9692      */
9693     refresh : function(){
9694         this.clearSelections();
9695         this.el.update("");
9696         var html = [];
9697         var o = this.jsonData;
9698         if(o && o.length > 0){
9699             for(var i = 0, len = o.length; i < len; i++){
9700                 var data = this.prepareData(o[i], i, o);
9701                 html[html.length] = this.tpl.apply(data);
9702             }
9703         }else{
9704             html.push(this.emptyText);
9705         }
9706         this.el.update(html.join(""));
9707         this.nodes = this.el.dom.childNodes;
9708         this.updateIndexes(0);
9709     },
9710
9711     /**
9712      * 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.
9713      * @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:
9714      <pre><code>
9715      view.load({
9716          url: "your-url.php",
9717          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9718          callback: yourFunction,
9719          scope: yourObject, //(optional scope)
9720          discardUrl: false,
9721          nocache: false,
9722          text: "Loading...",
9723          timeout: 30,
9724          scripts: false
9725      });
9726      </code></pre>
9727      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9728      * 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.
9729      * @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}
9730      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9731      * @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.
9732      */
9733     load : function(){
9734         var um = this.el.getUpdateManager();
9735         um.update.apply(um, arguments);
9736     },
9737
9738     render : function(el, response){
9739         this.clearSelections();
9740         this.el.update("");
9741         var o;
9742         try{
9743             o = Roo.util.JSON.decode(response.responseText);
9744             if(this.jsonRoot){
9745                 
9746                 o = o[this.jsonRoot];
9747             }
9748         } catch(e){
9749         }
9750         /**
9751          * The current JSON data or null
9752          */
9753         this.jsonData = o;
9754         this.beforeRender();
9755         this.refresh();
9756     },
9757
9758 /**
9759  * Get the number of records in the current JSON dataset
9760  * @return {Number}
9761  */
9762     getCount : function(){
9763         return this.jsonData ? this.jsonData.length : 0;
9764     },
9765
9766 /**
9767  * Returns the JSON object for the specified node(s)
9768  * @param {HTMLElement/Array} node The node or an array of nodes
9769  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9770  * you get the JSON object for the node
9771  */
9772     getNodeData : function(node){
9773         if(node instanceof Array){
9774             var data = [];
9775             for(var i = 0, len = node.length; i < len; i++){
9776                 data.push(this.getNodeData(node[i]));
9777             }
9778             return data;
9779         }
9780         return this.jsonData[this.indexOf(node)] || null;
9781     },
9782
9783     beforeRender : function(){
9784         this.snapshot = this.jsonData;
9785         if(this.sortInfo){
9786             this.sort.apply(this, this.sortInfo);
9787         }
9788         this.fireEvent("beforerender", this, this.jsonData);
9789     },
9790
9791     onLoad : function(el, o){
9792         this.fireEvent("load", this, this.jsonData, o);
9793     },
9794
9795     onLoadException : function(el, o){
9796         this.fireEvent("loadexception", this, o);
9797     },
9798
9799 /**
9800  * Filter the data by a specific property.
9801  * @param {String} property A property on your JSON objects
9802  * @param {String/RegExp} value Either string that the property values
9803  * should start with, or a RegExp to test against the property
9804  */
9805     filter : function(property, value){
9806         if(this.jsonData){
9807             var data = [];
9808             var ss = this.snapshot;
9809             if(typeof value == "string"){
9810                 var vlen = value.length;
9811                 if(vlen == 0){
9812                     this.clearFilter();
9813                     return;
9814                 }
9815                 value = value.toLowerCase();
9816                 for(var i = 0, len = ss.length; i < len; i++){
9817                     var o = ss[i];
9818                     if(o[property].substr(0, vlen).toLowerCase() == value){
9819                         data.push(o);
9820                     }
9821                 }
9822             } else if(value.exec){ // regex?
9823                 for(var i = 0, len = ss.length; i < len; i++){
9824                     var o = ss[i];
9825                     if(value.test(o[property])){
9826                         data.push(o);
9827                     }
9828                 }
9829             } else{
9830                 return;
9831             }
9832             this.jsonData = data;
9833             this.refresh();
9834         }
9835     },
9836
9837 /**
9838  * Filter by a function. The passed function will be called with each
9839  * object in the current dataset. If the function returns true the value is kept,
9840  * otherwise it is filtered.
9841  * @param {Function} fn
9842  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9843  */
9844     filterBy : function(fn, scope){
9845         if(this.jsonData){
9846             var data = [];
9847             var ss = this.snapshot;
9848             for(var i = 0, len = ss.length; i < len; i++){
9849                 var o = ss[i];
9850                 if(fn.call(scope || this, o)){
9851                     data.push(o);
9852                 }
9853             }
9854             this.jsonData = data;
9855             this.refresh();
9856         }
9857     },
9858
9859 /**
9860  * Clears the current filter.
9861  */
9862     clearFilter : function(){
9863         if(this.snapshot && this.jsonData != this.snapshot){
9864             this.jsonData = this.snapshot;
9865             this.refresh();
9866         }
9867     },
9868
9869
9870 /**
9871  * Sorts the data for this view and refreshes it.
9872  * @param {String} property A property on your JSON objects to sort on
9873  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9874  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9875  */
9876     sort : function(property, dir, sortType){
9877         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9878         if(this.jsonData){
9879             var p = property;
9880             var dsc = dir && dir.toLowerCase() == "desc";
9881             var f = function(o1, o2){
9882                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9883                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9884                 ;
9885                 if(v1 < v2){
9886                     return dsc ? +1 : -1;
9887                 } else if(v1 > v2){
9888                     return dsc ? -1 : +1;
9889                 } else{
9890                     return 0;
9891                 }
9892             };
9893             this.jsonData.sort(f);
9894             this.refresh();
9895             if(this.jsonData != this.snapshot){
9896                 this.snapshot.sort(f);
9897             }
9898         }
9899     }
9900 });/*
9901  * Based on:
9902  * Ext JS Library 1.1.1
9903  * Copyright(c) 2006-2007, Ext JS, LLC.
9904  *
9905  * Originally Released Under LGPL - original licence link has changed is not relivant.
9906  *
9907  * Fork - LGPL
9908  * <script type="text/javascript">
9909  */
9910  
9911
9912 /**
9913  * @class Roo.ColorPalette
9914  * @extends Roo.Component
9915  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9916  * Here's an example of typical usage:
9917  * <pre><code>
9918 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9919 cp.render('my-div');
9920
9921 cp.on('select', function(palette, selColor){
9922     // do something with selColor
9923 });
9924 </code></pre>
9925  * @constructor
9926  * Create a new ColorPalette
9927  * @param {Object} config The config object
9928  */
9929 Roo.ColorPalette = function(config){
9930     Roo.ColorPalette.superclass.constructor.call(this, config);
9931     this.addEvents({
9932         /**
9933              * @event select
9934              * Fires when a color is selected
9935              * @param {ColorPalette} this
9936              * @param {String} color The 6-digit color hex code (without the # symbol)
9937              */
9938         select: true
9939     });
9940
9941     if(this.handler){
9942         this.on("select", this.handler, this.scope, true);
9943     }
9944 };
9945 Roo.extend(Roo.ColorPalette, Roo.Component, {
9946     /**
9947      * @cfg {String} itemCls
9948      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9949      */
9950     itemCls : "x-color-palette",
9951     /**
9952      * @cfg {String} value
9953      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9954      * the hex codes are case-sensitive.
9955      */
9956     value : null,
9957     clickEvent:'click',
9958     // private
9959     ctype: "Roo.ColorPalette",
9960
9961     /**
9962      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9963      */
9964     allowReselect : false,
9965
9966     /**
9967      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9968      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9969      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9970      * of colors with the width setting until the box is symmetrical.</p>
9971      * <p>You can override individual colors if needed:</p>
9972      * <pre><code>
9973 var cp = new Roo.ColorPalette();
9974 cp.colors[0] = "FF0000";  // change the first box to red
9975 </code></pre>
9976
9977 Or you can provide a custom array of your own for complete control:
9978 <pre><code>
9979 var cp = new Roo.ColorPalette();
9980 cp.colors = ["000000", "993300", "333300"];
9981 </code></pre>
9982      * @type Array
9983      */
9984     colors : [
9985         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9986         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9987         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9988         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9989         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9990     ],
9991
9992     // private
9993     onRender : function(container, position){
9994         var t = new Roo.MasterTemplate(
9995             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9996         );
9997         var c = this.colors;
9998         for(var i = 0, len = c.length; i < len; i++){
9999             t.add([c[i]]);
10000         }
10001         var el = document.createElement("div");
10002         el.className = this.itemCls;
10003         t.overwrite(el);
10004         container.dom.insertBefore(el, position);
10005         this.el = Roo.get(el);
10006         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10007         if(this.clickEvent != 'click'){
10008             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10009         }
10010     },
10011
10012     // private
10013     afterRender : function(){
10014         Roo.ColorPalette.superclass.afterRender.call(this);
10015         if(this.value){
10016             var s = this.value;
10017             this.value = null;
10018             this.select(s);
10019         }
10020     },
10021
10022     // private
10023     handleClick : function(e, t){
10024         e.preventDefault();
10025         if(!this.disabled){
10026             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10027             this.select(c.toUpperCase());
10028         }
10029     },
10030
10031     /**
10032      * Selects the specified color in the palette (fires the select event)
10033      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10034      */
10035     select : function(color){
10036         color = color.replace("#", "");
10037         if(color != this.value || this.allowReselect){
10038             var el = this.el;
10039             if(this.value){
10040                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10041             }
10042             el.child("a.color-"+color).addClass("x-color-palette-sel");
10043             this.value = color;
10044             this.fireEvent("select", this, color);
10045         }
10046     }
10047 });/*
10048  * Based on:
10049  * Ext JS Library 1.1.1
10050  * Copyright(c) 2006-2007, Ext JS, LLC.
10051  *
10052  * Originally Released Under LGPL - original licence link has changed is not relivant.
10053  *
10054  * Fork - LGPL
10055  * <script type="text/javascript">
10056  */
10057  
10058 /**
10059  * @class Roo.DatePicker
10060  * @extends Roo.Component
10061  * Simple date picker class.
10062  * @constructor
10063  * Create a new DatePicker
10064  * @param {Object} config The config object
10065  */
10066 Roo.DatePicker = function(config){
10067     Roo.DatePicker.superclass.constructor.call(this, config);
10068
10069     this.value = config && config.value ?
10070                  config.value.clearTime() : new Date().clearTime();
10071
10072     this.addEvents({
10073         /**
10074              * @event select
10075              * Fires when a date is selected
10076              * @param {DatePicker} this
10077              * @param {Date} date The selected date
10078              */
10079         select: true
10080     });
10081
10082     if(this.handler){
10083         this.on("select", this.handler,  this.scope || this);
10084     }
10085     // build the disabledDatesRE
10086     if(!this.disabledDatesRE && this.disabledDates){
10087         var dd = this.disabledDates;
10088         var re = "(?:";
10089         for(var i = 0; i < dd.length; i++){
10090             re += dd[i];
10091             if(i != dd.length-1) re += "|";
10092         }
10093         this.disabledDatesRE = new RegExp(re + ")");
10094     }
10095 };
10096
10097 Roo.extend(Roo.DatePicker, Roo.Component, {
10098     /**
10099      * @cfg {String} todayText
10100      * The text to display on the button that selects the current date (defaults to "Today")
10101      */
10102     todayText : "Today",
10103     /**
10104      * @cfg {String} okText
10105      * The text to display on the ok button
10106      */
10107     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10108     /**
10109      * @cfg {String} cancelText
10110      * The text to display on the cancel button
10111      */
10112     cancelText : "Cancel",
10113     /**
10114      * @cfg {String} todayTip
10115      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10116      */
10117     todayTip : "{0} (Spacebar)",
10118     /**
10119      * @cfg {Date} minDate
10120      * Minimum allowable date (JavaScript date object, defaults to null)
10121      */
10122     minDate : null,
10123     /**
10124      * @cfg {Date} maxDate
10125      * Maximum allowable date (JavaScript date object, defaults to null)
10126      */
10127     maxDate : null,
10128     /**
10129      * @cfg {String} minText
10130      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10131      */
10132     minText : "This date is before the minimum date",
10133     /**
10134      * @cfg {String} maxText
10135      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10136      */
10137     maxText : "This date is after the maximum date",
10138     /**
10139      * @cfg {String} format
10140      * The default date format string which can be overriden for localization support.  The format must be
10141      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10142      */
10143     format : "m/d/y",
10144     /**
10145      * @cfg {Array} disabledDays
10146      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10147      */
10148     disabledDays : null,
10149     /**
10150      * @cfg {String} disabledDaysText
10151      * The tooltip to display when the date falls on a disabled day (defaults to "")
10152      */
10153     disabledDaysText : "",
10154     /**
10155      * @cfg {RegExp} disabledDatesRE
10156      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10157      */
10158     disabledDatesRE : null,
10159     /**
10160      * @cfg {String} disabledDatesText
10161      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10162      */
10163     disabledDatesText : "",
10164     /**
10165      * @cfg {Boolean} constrainToViewport
10166      * True to constrain the date picker to the viewport (defaults to true)
10167      */
10168     constrainToViewport : true,
10169     /**
10170      * @cfg {Array} monthNames
10171      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10172      */
10173     monthNames : Date.monthNames,
10174     /**
10175      * @cfg {Array} dayNames
10176      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10177      */
10178     dayNames : Date.dayNames,
10179     /**
10180      * @cfg {String} nextText
10181      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10182      */
10183     nextText: 'Next Month (Control+Right)',
10184     /**
10185      * @cfg {String} prevText
10186      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10187      */
10188     prevText: 'Previous Month (Control+Left)',
10189     /**
10190      * @cfg {String} monthYearText
10191      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10192      */
10193     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10194     /**
10195      * @cfg {Number} startDay
10196      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10197      */
10198     startDay : 0,
10199     /**
10200      * @cfg {Bool} showClear
10201      * Show a clear button (usefull for date form elements that can be blank.)
10202      */
10203     
10204     showClear: false,
10205     
10206     /**
10207      * Sets the value of the date field
10208      * @param {Date} value The date to set
10209      */
10210     setValue : function(value){
10211         var old = this.value;
10212         this.value = value.clearTime(true);
10213         if(this.el){
10214             this.update(this.value);
10215         }
10216     },
10217
10218     /**
10219      * Gets the current selected value of the date field
10220      * @return {Date} The selected date
10221      */
10222     getValue : function(){
10223         return this.value;
10224     },
10225
10226     // private
10227     focus : function(){
10228         if(this.el){
10229             this.update(this.activeDate);
10230         }
10231     },
10232
10233     // private
10234     onRender : function(container, position){
10235         var m = [
10236              '<table cellspacing="0">',
10237                 '<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>',
10238                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10239         var dn = this.dayNames;
10240         for(var i = 0; i < 7; i++){
10241             var d = this.startDay+i;
10242             if(d > 6){
10243                 d = d-7;
10244             }
10245             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10246         }
10247         m[m.length] = "</tr></thead><tbody><tr>";
10248         for(var i = 0; i < 42; i++) {
10249             if(i % 7 == 0 && i != 0){
10250                 m[m.length] = "</tr><tr>";
10251             }
10252             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10253         }
10254         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10255             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10256
10257         var el = document.createElement("div");
10258         el.className = "x-date-picker";
10259         el.innerHTML = m.join("");
10260
10261         container.dom.insertBefore(el, position);
10262
10263         this.el = Roo.get(el);
10264         this.eventEl = Roo.get(el.firstChild);
10265
10266         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10267             handler: this.showPrevMonth,
10268             scope: this,
10269             preventDefault:true,
10270             stopDefault:true
10271         });
10272
10273         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10274             handler: this.showNextMonth,
10275             scope: this,
10276             preventDefault:true,
10277             stopDefault:true
10278         });
10279
10280         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10281
10282         this.monthPicker = this.el.down('div.x-date-mp');
10283         this.monthPicker.enableDisplayMode('block');
10284         
10285         var kn = new Roo.KeyNav(this.eventEl, {
10286             "left" : function(e){
10287                 e.ctrlKey ?
10288                     this.showPrevMonth() :
10289                     this.update(this.activeDate.add("d", -1));
10290             },
10291
10292             "right" : function(e){
10293                 e.ctrlKey ?
10294                     this.showNextMonth() :
10295                     this.update(this.activeDate.add("d", 1));
10296             },
10297
10298             "up" : function(e){
10299                 e.ctrlKey ?
10300                     this.showNextYear() :
10301                     this.update(this.activeDate.add("d", -7));
10302             },
10303
10304             "down" : function(e){
10305                 e.ctrlKey ?
10306                     this.showPrevYear() :
10307                     this.update(this.activeDate.add("d", 7));
10308             },
10309
10310             "pageUp" : function(e){
10311                 this.showNextMonth();
10312             },
10313
10314             "pageDown" : function(e){
10315                 this.showPrevMonth();
10316             },
10317
10318             "enter" : function(e){
10319                 e.stopPropagation();
10320                 return true;
10321             },
10322
10323             scope : this
10324         });
10325
10326         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10327
10328         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10329
10330         this.el.unselectable();
10331         
10332         this.cells = this.el.select("table.x-date-inner tbody td");
10333         this.textNodes = this.el.query("table.x-date-inner tbody span");
10334
10335         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10336             text: "&#160;",
10337             tooltip: this.monthYearText
10338         });
10339
10340         this.mbtn.on('click', this.showMonthPicker, this);
10341         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10342
10343
10344         var today = (new Date()).dateFormat(this.format);
10345         
10346         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10347         if (this.showClear) {
10348             baseTb.add( new Roo.Toolbar.Fill());
10349         }
10350         baseTb.add({
10351             text: String.format(this.todayText, today),
10352             tooltip: String.format(this.todayTip, today),
10353             handler: this.selectToday,
10354             scope: this
10355         });
10356         
10357         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10358             
10359         //});
10360         if (this.showClear) {
10361             
10362             baseTb.add( new Roo.Toolbar.Fill());
10363             baseTb.add({
10364                 text: '&#160;',
10365                 cls: 'x-btn-icon x-btn-clear',
10366                 handler: function() {
10367                     //this.value = '';
10368                     this.fireEvent("select", this, '');
10369                 },
10370                 scope: this
10371             });
10372         }
10373         
10374         
10375         if(Roo.isIE){
10376             this.el.repaint();
10377         }
10378         this.update(this.value);
10379     },
10380
10381     createMonthPicker : function(){
10382         if(!this.monthPicker.dom.firstChild){
10383             var buf = ['<table border="0" cellspacing="0">'];
10384             for(var i = 0; i < 6; i++){
10385                 buf.push(
10386                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10387                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10388                     i == 0 ?
10389                     '<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>' :
10390                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10391                 );
10392             }
10393             buf.push(
10394                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10395                     this.okText,
10396                     '</button><button type="button" class="x-date-mp-cancel">',
10397                     this.cancelText,
10398                     '</button></td></tr>',
10399                 '</table>'
10400             );
10401             this.monthPicker.update(buf.join(''));
10402             this.monthPicker.on('click', this.onMonthClick, this);
10403             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10404
10405             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10406             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10407
10408             this.mpMonths.each(function(m, a, i){
10409                 i += 1;
10410                 if((i%2) == 0){
10411                     m.dom.xmonth = 5 + Math.round(i * .5);
10412                 }else{
10413                     m.dom.xmonth = Math.round((i-1) * .5);
10414                 }
10415             });
10416         }
10417     },
10418
10419     showMonthPicker : function(){
10420         this.createMonthPicker();
10421         var size = this.el.getSize();
10422         this.monthPicker.setSize(size);
10423         this.monthPicker.child('table').setSize(size);
10424
10425         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10426         this.updateMPMonth(this.mpSelMonth);
10427         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10428         this.updateMPYear(this.mpSelYear);
10429
10430         this.monthPicker.slideIn('t', {duration:.2});
10431     },
10432
10433     updateMPYear : function(y){
10434         this.mpyear = y;
10435         var ys = this.mpYears.elements;
10436         for(var i = 1; i <= 10; i++){
10437             var td = ys[i-1], y2;
10438             if((i%2) == 0){
10439                 y2 = y + Math.round(i * .5);
10440                 td.firstChild.innerHTML = y2;
10441                 td.xyear = y2;
10442             }else{
10443                 y2 = y - (5-Math.round(i * .5));
10444                 td.firstChild.innerHTML = y2;
10445                 td.xyear = y2;
10446             }
10447             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10448         }
10449     },
10450
10451     updateMPMonth : function(sm){
10452         this.mpMonths.each(function(m, a, i){
10453             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10454         });
10455     },
10456
10457     selectMPMonth: function(m){
10458         
10459     },
10460
10461     onMonthClick : function(e, t){
10462         e.stopEvent();
10463         var el = new Roo.Element(t), pn;
10464         if(el.is('button.x-date-mp-cancel')){
10465             this.hideMonthPicker();
10466         }
10467         else if(el.is('button.x-date-mp-ok')){
10468             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10469             this.hideMonthPicker();
10470         }
10471         else if(pn = el.up('td.x-date-mp-month', 2)){
10472             this.mpMonths.removeClass('x-date-mp-sel');
10473             pn.addClass('x-date-mp-sel');
10474             this.mpSelMonth = pn.dom.xmonth;
10475         }
10476         else if(pn = el.up('td.x-date-mp-year', 2)){
10477             this.mpYears.removeClass('x-date-mp-sel');
10478             pn.addClass('x-date-mp-sel');
10479             this.mpSelYear = pn.dom.xyear;
10480         }
10481         else if(el.is('a.x-date-mp-prev')){
10482             this.updateMPYear(this.mpyear-10);
10483         }
10484         else if(el.is('a.x-date-mp-next')){
10485             this.updateMPYear(this.mpyear+10);
10486         }
10487     },
10488
10489     onMonthDblClick : function(e, t){
10490         e.stopEvent();
10491         var el = new Roo.Element(t), pn;
10492         if(pn = el.up('td.x-date-mp-month', 2)){
10493             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10494             this.hideMonthPicker();
10495         }
10496         else if(pn = el.up('td.x-date-mp-year', 2)){
10497             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10498             this.hideMonthPicker();
10499         }
10500     },
10501
10502     hideMonthPicker : function(disableAnim){
10503         if(this.monthPicker){
10504             if(disableAnim === true){
10505                 this.monthPicker.hide();
10506             }else{
10507                 this.monthPicker.slideOut('t', {duration:.2});
10508             }
10509         }
10510     },
10511
10512     // private
10513     showPrevMonth : function(e){
10514         this.update(this.activeDate.add("mo", -1));
10515     },
10516
10517     // private
10518     showNextMonth : function(e){
10519         this.update(this.activeDate.add("mo", 1));
10520     },
10521
10522     // private
10523     showPrevYear : function(){
10524         this.update(this.activeDate.add("y", -1));
10525     },
10526
10527     // private
10528     showNextYear : function(){
10529         this.update(this.activeDate.add("y", 1));
10530     },
10531
10532     // private
10533     handleMouseWheel : function(e){
10534         var delta = e.getWheelDelta();
10535         if(delta > 0){
10536             this.showPrevMonth();
10537             e.stopEvent();
10538         } else if(delta < 0){
10539             this.showNextMonth();
10540             e.stopEvent();
10541         }
10542     },
10543
10544     // private
10545     handleDateClick : function(e, t){
10546         e.stopEvent();
10547         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10548             this.setValue(new Date(t.dateValue));
10549             this.fireEvent("select", this, this.value);
10550         }
10551     },
10552
10553     // private
10554     selectToday : function(){
10555         this.setValue(new Date().clearTime());
10556         this.fireEvent("select", this, this.value);
10557     },
10558
10559     // private
10560     update : function(date){
10561         var vd = this.activeDate;
10562         this.activeDate = date;
10563         if(vd && this.el){
10564             var t = date.getTime();
10565             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10566                 this.cells.removeClass("x-date-selected");
10567                 this.cells.each(function(c){
10568                    if(c.dom.firstChild.dateValue == t){
10569                        c.addClass("x-date-selected");
10570                        setTimeout(function(){
10571                             try{c.dom.firstChild.focus();}catch(e){}
10572                        }, 50);
10573                        return false;
10574                    }
10575                 });
10576                 return;
10577             }
10578         }
10579         var days = date.getDaysInMonth();
10580         var firstOfMonth = date.getFirstDateOfMonth();
10581         var startingPos = firstOfMonth.getDay()-this.startDay;
10582
10583         if(startingPos <= this.startDay){
10584             startingPos += 7;
10585         }
10586
10587         var pm = date.add("mo", -1);
10588         var prevStart = pm.getDaysInMonth()-startingPos;
10589
10590         var cells = this.cells.elements;
10591         var textEls = this.textNodes;
10592         days += startingPos;
10593
10594         // convert everything to numbers so it's fast
10595         var day = 86400000;
10596         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10597         var today = new Date().clearTime().getTime();
10598         var sel = date.clearTime().getTime();
10599         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10600         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10601         var ddMatch = this.disabledDatesRE;
10602         var ddText = this.disabledDatesText;
10603         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10604         var ddaysText = this.disabledDaysText;
10605         var format = this.format;
10606
10607         var setCellClass = function(cal, cell){
10608             cell.title = "";
10609             var t = d.getTime();
10610             cell.firstChild.dateValue = t;
10611             if(t == today){
10612                 cell.className += " x-date-today";
10613                 cell.title = cal.todayText;
10614             }
10615             if(t == sel){
10616                 cell.className += " x-date-selected";
10617                 setTimeout(function(){
10618                     try{cell.firstChild.focus();}catch(e){}
10619                 }, 50);
10620             }
10621             // disabling
10622             if(t < min) {
10623                 cell.className = " x-date-disabled";
10624                 cell.title = cal.minText;
10625                 return;
10626             }
10627             if(t > max) {
10628                 cell.className = " x-date-disabled";
10629                 cell.title = cal.maxText;
10630                 return;
10631             }
10632             if(ddays){
10633                 if(ddays.indexOf(d.getDay()) != -1){
10634                     cell.title = ddaysText;
10635                     cell.className = " x-date-disabled";
10636                 }
10637             }
10638             if(ddMatch && format){
10639                 var fvalue = d.dateFormat(format);
10640                 if(ddMatch.test(fvalue)){
10641                     cell.title = ddText.replace("%0", fvalue);
10642                     cell.className = " x-date-disabled";
10643                 }
10644             }
10645         };
10646
10647         var i = 0;
10648         for(; i < startingPos; i++) {
10649             textEls[i].innerHTML = (++prevStart);
10650             d.setDate(d.getDate()+1);
10651             cells[i].className = "x-date-prevday";
10652             setCellClass(this, cells[i]);
10653         }
10654         for(; i < days; i++){
10655             intDay = i - startingPos + 1;
10656             textEls[i].innerHTML = (intDay);
10657             d.setDate(d.getDate()+1);
10658             cells[i].className = "x-date-active";
10659             setCellClass(this, cells[i]);
10660         }
10661         var extraDays = 0;
10662         for(; i < 42; i++) {
10663              textEls[i].innerHTML = (++extraDays);
10664              d.setDate(d.getDate()+1);
10665              cells[i].className = "x-date-nextday";
10666              setCellClass(this, cells[i]);
10667         }
10668
10669         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10670
10671         if(!this.internalRender){
10672             var main = this.el.dom.firstChild;
10673             var w = main.offsetWidth;
10674             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10675             Roo.fly(main).setWidth(w);
10676             this.internalRender = true;
10677             // opera does not respect the auto grow header center column
10678             // then, after it gets a width opera refuses to recalculate
10679             // without a second pass
10680             if(Roo.isOpera && !this.secondPass){
10681                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10682                 this.secondPass = true;
10683                 this.update.defer(10, this, [date]);
10684             }
10685         }
10686     }
10687 });/*
10688  * Based on:
10689  * Ext JS Library 1.1.1
10690  * Copyright(c) 2006-2007, Ext JS, LLC.
10691  *
10692  * Originally Released Under LGPL - original licence link has changed is not relivant.
10693  *
10694  * Fork - LGPL
10695  * <script type="text/javascript">
10696  */
10697 /**
10698  * @class Roo.TabPanel
10699  * @extends Roo.util.Observable
10700  * A lightweight tab container.
10701  * <br><br>
10702  * Usage:
10703  * <pre><code>
10704 // basic tabs 1, built from existing content
10705 var tabs = new Roo.TabPanel("tabs1");
10706 tabs.addTab("script", "View Script");
10707 tabs.addTab("markup", "View Markup");
10708 tabs.activate("script");
10709
10710 // more advanced tabs, built from javascript
10711 var jtabs = new Roo.TabPanel("jtabs");
10712 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10713
10714 // set up the UpdateManager
10715 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10716 var updater = tab2.getUpdateManager();
10717 updater.setDefaultUrl("ajax1.htm");
10718 tab2.on('activate', updater.refresh, updater, true);
10719
10720 // Use setUrl for Ajax loading
10721 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10722 tab3.setUrl("ajax2.htm", null, true);
10723
10724 // Disabled tab
10725 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10726 tab4.disable();
10727
10728 jtabs.activate("jtabs-1");
10729  * </code></pre>
10730  * @constructor
10731  * Create a new TabPanel.
10732  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10733  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10734  */
10735 Roo.TabPanel = function(container, config){
10736     /**
10737     * The container element for this TabPanel.
10738     * @type Roo.Element
10739     */
10740     this.el = Roo.get(container, true);
10741     if(config){
10742         if(typeof config == "boolean"){
10743             this.tabPosition = config ? "bottom" : "top";
10744         }else{
10745             Roo.apply(this, config);
10746         }
10747     }
10748     if(this.tabPosition == "bottom"){
10749         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10750         this.el.addClass("x-tabs-bottom");
10751     }
10752     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10753     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10754     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10755     if(Roo.isIE){
10756         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10757     }
10758     if(this.tabPosition != "bottom"){
10759     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10760      * @type Roo.Element
10761      */
10762       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10763       this.el.addClass("x-tabs-top");
10764     }
10765     this.items = [];
10766
10767     this.bodyEl.setStyle("position", "relative");
10768
10769     this.active = null;
10770     this.activateDelegate = this.activate.createDelegate(this);
10771
10772     this.addEvents({
10773         /**
10774          * @event tabchange
10775          * Fires when the active tab changes
10776          * @param {Roo.TabPanel} this
10777          * @param {Roo.TabPanelItem} activePanel The new active tab
10778          */
10779         "tabchange": true,
10780         /**
10781          * @event beforetabchange
10782          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10783          * @param {Roo.TabPanel} this
10784          * @param {Object} e Set cancel to true on this object to cancel the tab change
10785          * @param {Roo.TabPanelItem} tab The tab being changed to
10786          */
10787         "beforetabchange" : true
10788     });
10789
10790     Roo.EventManager.onWindowResize(this.onResize, this);
10791     this.cpad = this.el.getPadding("lr");
10792     this.hiddenCount = 0;
10793
10794     Roo.TabPanel.superclass.constructor.call(this);
10795 };
10796
10797 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10798         /*
10799          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10800          */
10801     tabPosition : "top",
10802         /*
10803          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10804          */
10805     currentTabWidth : 0,
10806         /*
10807          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10808          */
10809     minTabWidth : 40,
10810         /*
10811          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10812          */
10813     maxTabWidth : 250,
10814         /*
10815          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10816          */
10817     preferredTabWidth : 175,
10818         /*
10819          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10820          */
10821     resizeTabs : false,
10822         /*
10823          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10824          */
10825     monitorResize : true,
10826
10827     /**
10828      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10829      * @param {String} id The id of the div to use <b>or create</b>
10830      * @param {String} text The text for the tab
10831      * @param {String} content (optional) Content to put in the TabPanelItem body
10832      * @param {Boolean} closable (optional) True to create a close icon on the tab
10833      * @return {Roo.TabPanelItem} The created TabPanelItem
10834      */
10835     addTab : function(id, text, content, closable){
10836         var item = new Roo.TabPanelItem(this, id, text, closable);
10837         this.addTabItem(item);
10838         if(content){
10839             item.setContent(content);
10840         }
10841         return item;
10842     },
10843
10844     /**
10845      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10846      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10847      * @return {Roo.TabPanelItem}
10848      */
10849     getTab : function(id){
10850         return this.items[id];
10851     },
10852
10853     /**
10854      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10855      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10856      */
10857     hideTab : function(id){
10858         var t = this.items[id];
10859         if(!t.isHidden()){
10860            t.setHidden(true);
10861            this.hiddenCount++;
10862            this.autoSizeTabs();
10863         }
10864     },
10865
10866     /**
10867      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10868      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10869      */
10870     unhideTab : function(id){
10871         var t = this.items[id];
10872         if(t.isHidden()){
10873            t.setHidden(false);
10874            this.hiddenCount--;
10875            this.autoSizeTabs();
10876         }
10877     },
10878
10879     /**
10880      * Adds an existing {@link Roo.TabPanelItem}.
10881      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10882      */
10883     addTabItem : function(item){
10884         this.items[item.id] = item;
10885         this.items.push(item);
10886         if(this.resizeTabs){
10887            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10888            this.autoSizeTabs();
10889         }else{
10890             item.autoSize();
10891         }
10892     },
10893
10894     /**
10895      * Removes a {@link Roo.TabPanelItem}.
10896      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10897      */
10898     removeTab : function(id){
10899         var items = this.items;
10900         var tab = items[id];
10901         if(!tab) { return; }
10902         var index = items.indexOf(tab);
10903         if(this.active == tab && items.length > 1){
10904             var newTab = this.getNextAvailable(index);
10905             if(newTab) {
10906                 newTab.activate();
10907             }
10908         }
10909         this.stripEl.dom.removeChild(tab.pnode.dom);
10910         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10911             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10912         }
10913         items.splice(index, 1);
10914         delete this.items[tab.id];
10915         tab.fireEvent("close", tab);
10916         tab.purgeListeners();
10917         this.autoSizeTabs();
10918     },
10919
10920     getNextAvailable : function(start){
10921         var items = this.items;
10922         var index = start;
10923         // look for a next tab that will slide over to
10924         // replace the one being removed
10925         while(index < items.length){
10926             var item = items[++index];
10927             if(item && !item.isHidden()){
10928                 return item;
10929             }
10930         }
10931         // if one isn't found select the previous tab (on the left)
10932         index = start;
10933         while(index >= 0){
10934             var item = items[--index];
10935             if(item && !item.isHidden()){
10936                 return item;
10937             }
10938         }
10939         return null;
10940     },
10941
10942     /**
10943      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10944      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10945      */
10946     disableTab : function(id){
10947         var tab = this.items[id];
10948         if(tab && this.active != tab){
10949             tab.disable();
10950         }
10951     },
10952
10953     /**
10954      * Enables a {@link Roo.TabPanelItem} that is disabled.
10955      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10956      */
10957     enableTab : function(id){
10958         var tab = this.items[id];
10959         tab.enable();
10960     },
10961
10962     /**
10963      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10964      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10965      * @return {Roo.TabPanelItem} The TabPanelItem.
10966      */
10967     activate : function(id){
10968         var tab = this.items[id];
10969         if(!tab){
10970             return null;
10971         }
10972         if(tab == this.active || tab.disabled){
10973             return tab;
10974         }
10975         var e = {};
10976         this.fireEvent("beforetabchange", this, e, tab);
10977         if(e.cancel !== true && !tab.disabled){
10978             if(this.active){
10979                 this.active.hide();
10980             }
10981             this.active = this.items[id];
10982             this.active.show();
10983             this.fireEvent("tabchange", this, this.active);
10984         }
10985         return tab;
10986     },
10987
10988     /**
10989      * Gets the active {@link Roo.TabPanelItem}.
10990      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10991      */
10992     getActiveTab : function(){
10993         return this.active;
10994     },
10995
10996     /**
10997      * Updates the tab body element to fit the height of the container element
10998      * for overflow scrolling
10999      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11000      */
11001     syncHeight : function(targetHeight){
11002         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11003         var bm = this.bodyEl.getMargins();
11004         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11005         this.bodyEl.setHeight(newHeight);
11006         return newHeight;
11007     },
11008
11009     onResize : function(){
11010         if(this.monitorResize){
11011             this.autoSizeTabs();
11012         }
11013     },
11014
11015     /**
11016      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11017      */
11018     beginUpdate : function(){
11019         this.updating = true;
11020     },
11021
11022     /**
11023      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11024      */
11025     endUpdate : function(){
11026         this.updating = false;
11027         this.autoSizeTabs();
11028     },
11029
11030     /**
11031      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11032      */
11033     autoSizeTabs : function(){
11034         var count = this.items.length;
11035         var vcount = count - this.hiddenCount;
11036         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11037         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11038         var availWidth = Math.floor(w / vcount);
11039         var b = this.stripBody;
11040         if(b.getWidth() > w){
11041             var tabs = this.items;
11042             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11043             if(availWidth < this.minTabWidth){
11044                 /*if(!this.sleft){    // incomplete scrolling code
11045                     this.createScrollButtons();
11046                 }
11047                 this.showScroll();
11048                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11049             }
11050         }else{
11051             if(this.currentTabWidth < this.preferredTabWidth){
11052                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11053             }
11054         }
11055     },
11056
11057     /**
11058      * Returns the number of tabs in this TabPanel.
11059      * @return {Number}
11060      */
11061      getCount : function(){
11062          return this.items.length;
11063      },
11064
11065     /**
11066      * Resizes all the tabs to the passed width
11067      * @param {Number} The new width
11068      */
11069     setTabWidth : function(width){
11070         this.currentTabWidth = width;
11071         for(var i = 0, len = this.items.length; i < len; i++) {
11072                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11073         }
11074     },
11075
11076     /**
11077      * Destroys this TabPanel
11078      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11079      */
11080     destroy : function(removeEl){
11081         Roo.EventManager.removeResizeListener(this.onResize, this);
11082         for(var i = 0, len = this.items.length; i < len; i++){
11083             this.items[i].purgeListeners();
11084         }
11085         if(removeEl === true){
11086             this.el.update("");
11087             this.el.remove();
11088         }
11089     }
11090 });
11091
11092 /**
11093  * @class Roo.TabPanelItem
11094  * @extends Roo.util.Observable
11095  * Represents an individual item (tab plus body) in a TabPanel.
11096  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11097  * @param {String} id The id of this TabPanelItem
11098  * @param {String} text The text for the tab of this TabPanelItem
11099  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11100  */
11101 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11102     /**
11103      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11104      * @type Roo.TabPanel
11105      */
11106     this.tabPanel = tabPanel;
11107     /**
11108      * The id for this TabPanelItem
11109      * @type String
11110      */
11111     this.id = id;
11112     /** @private */
11113     this.disabled = false;
11114     /** @private */
11115     this.text = text;
11116     /** @private */
11117     this.loaded = false;
11118     this.closable = closable;
11119
11120     /**
11121      * The body element for this TabPanelItem.
11122      * @type Roo.Element
11123      */
11124     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11125     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11126     this.bodyEl.setStyle("display", "block");
11127     this.bodyEl.setStyle("zoom", "1");
11128     this.hideAction();
11129
11130     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11131     /** @private */
11132     this.el = Roo.get(els.el, true);
11133     this.inner = Roo.get(els.inner, true);
11134     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11135     this.pnode = Roo.get(els.el.parentNode, true);
11136     this.el.on("mousedown", this.onTabMouseDown, this);
11137     this.el.on("click", this.onTabClick, this);
11138     /** @private */
11139     if(closable){
11140         var c = Roo.get(els.close, true);
11141         c.dom.title = this.closeText;
11142         c.addClassOnOver("close-over");
11143         c.on("click", this.closeClick, this);
11144      }
11145
11146     this.addEvents({
11147          /**
11148          * @event activate
11149          * Fires when this tab becomes the active tab.
11150          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11151          * @param {Roo.TabPanelItem} this
11152          */
11153         "activate": true,
11154         /**
11155          * @event beforeclose
11156          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11157          * @param {Roo.TabPanelItem} this
11158          * @param {Object} e Set cancel to true on this object to cancel the close.
11159          */
11160         "beforeclose": true,
11161         /**
11162          * @event close
11163          * Fires when this tab is closed.
11164          * @param {Roo.TabPanelItem} this
11165          */
11166          "close": true,
11167         /**
11168          * @event deactivate
11169          * Fires when this tab is no longer the active tab.
11170          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11171          * @param {Roo.TabPanelItem} this
11172          */
11173          "deactivate" : true
11174     });
11175     this.hidden = false;
11176
11177     Roo.TabPanelItem.superclass.constructor.call(this);
11178 };
11179
11180 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11181     purgeListeners : function(){
11182        Roo.util.Observable.prototype.purgeListeners.call(this);
11183        this.el.removeAllListeners();
11184     },
11185     /**
11186      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11187      */
11188     show : function(){
11189         this.pnode.addClass("on");
11190         this.showAction();
11191         if(Roo.isOpera){
11192             this.tabPanel.stripWrap.repaint();
11193         }
11194         this.fireEvent("activate", this.tabPanel, this);
11195     },
11196
11197     /**
11198      * Returns true if this tab is the active tab.
11199      * @return {Boolean}
11200      */
11201     isActive : function(){
11202         return this.tabPanel.getActiveTab() == this;
11203     },
11204
11205     /**
11206      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11207      */
11208     hide : function(){
11209         this.pnode.removeClass("on");
11210         this.hideAction();
11211         this.fireEvent("deactivate", this.tabPanel, this);
11212     },
11213
11214     hideAction : function(){
11215         this.bodyEl.hide();
11216         this.bodyEl.setStyle("position", "absolute");
11217         this.bodyEl.setLeft("-20000px");
11218         this.bodyEl.setTop("-20000px");
11219     },
11220
11221     showAction : function(){
11222         this.bodyEl.setStyle("position", "relative");
11223         this.bodyEl.setTop("");
11224         this.bodyEl.setLeft("");
11225         this.bodyEl.show();
11226     },
11227
11228     /**
11229      * Set the tooltip for the tab.
11230      * @param {String} tooltip The tab's tooltip
11231      */
11232     setTooltip : function(text){
11233         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11234             this.textEl.dom.qtip = text;
11235             this.textEl.dom.removeAttribute('title');
11236         }else{
11237             this.textEl.dom.title = text;
11238         }
11239     },
11240
11241     onTabClick : function(e){
11242         e.preventDefault();
11243         this.tabPanel.activate(this.id);
11244     },
11245
11246     onTabMouseDown : function(e){
11247         e.preventDefault();
11248         this.tabPanel.activate(this.id);
11249     },
11250
11251     getWidth : function(){
11252         return this.inner.getWidth();
11253     },
11254
11255     setWidth : function(width){
11256         var iwidth = width - this.pnode.getPadding("lr");
11257         this.inner.setWidth(iwidth);
11258         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11259         this.pnode.setWidth(width);
11260     },
11261
11262     /**
11263      * Show or hide the tab
11264      * @param {Boolean} hidden True to hide or false to show.
11265      */
11266     setHidden : function(hidden){
11267         this.hidden = hidden;
11268         this.pnode.setStyle("display", hidden ? "none" : "");
11269     },
11270
11271     /**
11272      * Returns true if this tab is "hidden"
11273      * @return {Boolean}
11274      */
11275     isHidden : function(){
11276         return this.hidden;
11277     },
11278
11279     /**
11280      * Returns the text for this tab
11281      * @return {String}
11282      */
11283     getText : function(){
11284         return this.text;
11285     },
11286
11287     autoSize : function(){
11288         //this.el.beginMeasure();
11289         this.textEl.setWidth(1);
11290         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11291         //this.el.endMeasure();
11292     },
11293
11294     /**
11295      * Sets the text for the tab (Note: this also sets the tooltip text)
11296      * @param {String} text The tab's text and tooltip
11297      */
11298     setText : function(text){
11299         this.text = text;
11300         this.textEl.update(text);
11301         this.setTooltip(text);
11302         if(!this.tabPanel.resizeTabs){
11303             this.autoSize();
11304         }
11305     },
11306     /**
11307      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11308      */
11309     activate : function(){
11310         this.tabPanel.activate(this.id);
11311     },
11312
11313     /**
11314      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11315      */
11316     disable : function(){
11317         if(this.tabPanel.active != this){
11318             this.disabled = true;
11319             this.pnode.addClass("disabled");
11320         }
11321     },
11322
11323     /**
11324      * Enables this TabPanelItem if it was previously disabled.
11325      */
11326     enable : function(){
11327         this.disabled = false;
11328         this.pnode.removeClass("disabled");
11329     },
11330
11331     /**
11332      * Sets the content for this TabPanelItem.
11333      * @param {String} content The content
11334      * @param {Boolean} loadScripts true to look for and load scripts
11335      */
11336     setContent : function(content, loadScripts){
11337         this.bodyEl.update(content, loadScripts);
11338     },
11339
11340     /**
11341      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11342      * @return {Roo.UpdateManager} The UpdateManager
11343      */
11344     getUpdateManager : function(){
11345         return this.bodyEl.getUpdateManager();
11346     },
11347
11348     /**
11349      * Set a URL to be used to load the content for this TabPanelItem.
11350      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11351      * @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)
11352      * @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)
11353      * @return {Roo.UpdateManager} The UpdateManager
11354      */
11355     setUrl : function(url, params, loadOnce){
11356         if(this.refreshDelegate){
11357             this.un('activate', this.refreshDelegate);
11358         }
11359         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11360         this.on("activate", this.refreshDelegate);
11361         return this.bodyEl.getUpdateManager();
11362     },
11363
11364     /** @private */
11365     _handleRefresh : function(url, params, loadOnce){
11366         if(!loadOnce || !this.loaded){
11367             var updater = this.bodyEl.getUpdateManager();
11368             updater.update(url, params, this._setLoaded.createDelegate(this));
11369         }
11370     },
11371
11372     /**
11373      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11374      *   Will fail silently if the setUrl method has not been called.
11375      *   This does not activate the panel, just updates its content.
11376      */
11377     refresh : function(){
11378         if(this.refreshDelegate){
11379            this.loaded = false;
11380            this.refreshDelegate();
11381         }
11382     },
11383
11384     /** @private */
11385     _setLoaded : function(){
11386         this.loaded = true;
11387     },
11388
11389     /** @private */
11390     closeClick : function(e){
11391         var o = {};
11392         e.stopEvent();
11393         this.fireEvent("beforeclose", this, o);
11394         if(o.cancel !== true){
11395             this.tabPanel.removeTab(this.id);
11396         }
11397     },
11398     /**
11399      * The text displayed in the tooltip for the close icon.
11400      * @type String
11401      */
11402     closeText : "Close this tab"
11403 });
11404
11405 /** @private */
11406 Roo.TabPanel.prototype.createStrip = function(container){
11407     var strip = document.createElement("div");
11408     strip.className = "x-tabs-wrap";
11409     container.appendChild(strip);
11410     return strip;
11411 };
11412 /** @private */
11413 Roo.TabPanel.prototype.createStripList = function(strip){
11414     // div wrapper for retard IE
11415     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>';
11416     return strip.firstChild.firstChild.firstChild.firstChild;
11417 };
11418 /** @private */
11419 Roo.TabPanel.prototype.createBody = function(container){
11420     var body = document.createElement("div");
11421     Roo.id(body, "tab-body");
11422     Roo.fly(body).addClass("x-tabs-body");
11423     container.appendChild(body);
11424     return body;
11425 };
11426 /** @private */
11427 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11428     var body = Roo.getDom(id);
11429     if(!body){
11430         body = document.createElement("div");
11431         body.id = id;
11432     }
11433     Roo.fly(body).addClass("x-tabs-item-body");
11434     bodyEl.insertBefore(body, bodyEl.firstChild);
11435     return body;
11436 };
11437 /** @private */
11438 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11439     var td = document.createElement("td");
11440     stripEl.appendChild(td);
11441     if(closable){
11442         td.className = "x-tabs-closable";
11443         if(!this.closeTpl){
11444             this.closeTpl = new Roo.Template(
11445                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11446                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11447                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11448             );
11449         }
11450         var el = this.closeTpl.overwrite(td, {"text": text});
11451         var close = el.getElementsByTagName("div")[0];
11452         var inner = el.getElementsByTagName("em")[0];
11453         return {"el": el, "close": close, "inner": inner};
11454     } else {
11455         if(!this.tabTpl){
11456             this.tabTpl = new Roo.Template(
11457                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11458                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11459             );
11460         }
11461         var el = this.tabTpl.overwrite(td, {"text": text});
11462         var inner = el.getElementsByTagName("em")[0];
11463         return {"el": el, "inner": inner};
11464     }
11465 };/*
11466  * Based on:
11467  * Ext JS Library 1.1.1
11468  * Copyright(c) 2006-2007, Ext JS, LLC.
11469  *
11470  * Originally Released Under LGPL - original licence link has changed is not relivant.
11471  *
11472  * Fork - LGPL
11473  * <script type="text/javascript">
11474  */
11475
11476 /**
11477  * @class Roo.Button
11478  * @extends Roo.util.Observable
11479  * Simple Button class
11480  * @cfg {String} text The button text
11481  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11482  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11483  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11484  * @cfg {Object} scope The scope of the handler
11485  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11486  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11487  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11488  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11489  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11490  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11491    applies if enableToggle = true)
11492  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11493  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11494   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11495  * @constructor
11496  * Create a new button
11497  * @param {Object} config The config object
11498  */
11499 Roo.Button = function(renderTo, config)
11500 {
11501     if (!config) {
11502         config = renderTo;
11503         renderTo = config.renderTo || false;
11504     }
11505     
11506     Roo.apply(this, config);
11507     this.addEvents({
11508         /**
11509              * @event click
11510              * Fires when this button is clicked
11511              * @param {Button} this
11512              * @param {EventObject} e The click event
11513              */
11514             "click" : true,
11515         /**
11516              * @event toggle
11517              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11518              * @param {Button} this
11519              * @param {Boolean} pressed
11520              */
11521             "toggle" : true,
11522         /**
11523              * @event mouseover
11524              * Fires when the mouse hovers over the button
11525              * @param {Button} this
11526              * @param {Event} e The event object
11527              */
11528         'mouseover' : true,
11529         /**
11530              * @event mouseout
11531              * Fires when the mouse exits the button
11532              * @param {Button} this
11533              * @param {Event} e The event object
11534              */
11535         'mouseout': true,
11536          /**
11537              * @event render
11538              * Fires when the button is rendered
11539              * @param {Button} this
11540              */
11541         'render': true
11542     });
11543     if(this.menu){
11544         this.menu = Roo.menu.MenuMgr.get(this.menu);
11545     }
11546     // register listeners first!!  - so render can be captured..
11547     Roo.util.Observable.call(this);
11548     if(renderTo){
11549         this.render(renderTo);
11550     }
11551     
11552   
11553 };
11554
11555 Roo.extend(Roo.Button, Roo.util.Observable, {
11556     /**
11557      * 
11558      */
11559     
11560     /**
11561      * Read-only. True if this button is hidden
11562      * @type Boolean
11563      */
11564     hidden : false,
11565     /**
11566      * Read-only. True if this button is disabled
11567      * @type Boolean
11568      */
11569     disabled : false,
11570     /**
11571      * Read-only. True if this button is pressed (only if enableToggle = true)
11572      * @type Boolean
11573      */
11574     pressed : false,
11575
11576     /**
11577      * @cfg {Number} tabIndex 
11578      * The DOM tabIndex for this button (defaults to undefined)
11579      */
11580     tabIndex : undefined,
11581
11582     /**
11583      * @cfg {Boolean} enableToggle
11584      * True to enable pressed/not pressed toggling (defaults to false)
11585      */
11586     enableToggle: false,
11587     /**
11588      * @cfg {Mixed} menu
11589      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11590      */
11591     menu : undefined,
11592     /**
11593      * @cfg {String} menuAlign
11594      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11595      */
11596     menuAlign : "tl-bl?",
11597
11598     /**
11599      * @cfg {String} iconCls
11600      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11601      */
11602     iconCls : undefined,
11603     /**
11604      * @cfg {String} type
11605      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11606      */
11607     type : 'button',
11608
11609     // private
11610     menuClassTarget: 'tr',
11611
11612     /**
11613      * @cfg {String} clickEvent
11614      * The type of event to map to the button's event handler (defaults to 'click')
11615      */
11616     clickEvent : 'click',
11617
11618     /**
11619      * @cfg {Boolean} handleMouseEvents
11620      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11621      */
11622     handleMouseEvents : true,
11623
11624     /**
11625      * @cfg {String} tooltipType
11626      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11627      */
11628     tooltipType : 'qtip',
11629
11630     /**
11631      * @cfg {String} cls
11632      * A CSS class to apply to the button's main element.
11633      */
11634     
11635     /**
11636      * @cfg {Roo.Template} template (Optional)
11637      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11638      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11639      * require code modifications if required elements (e.g. a button) aren't present.
11640      */
11641
11642     // private
11643     render : function(renderTo){
11644         var btn;
11645         if(this.hideParent){
11646             this.parentEl = Roo.get(renderTo);
11647         }
11648         if(!this.dhconfig){
11649             if(!this.template){
11650                 if(!Roo.Button.buttonTemplate){
11651                     // hideous table template
11652                     Roo.Button.buttonTemplate = new Roo.Template(
11653                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11654                         '<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>',
11655                         "</tr></tbody></table>");
11656                 }
11657                 this.template = Roo.Button.buttonTemplate;
11658             }
11659             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11660             var btnEl = btn.child("button:first");
11661             btnEl.on('focus', this.onFocus, this);
11662             btnEl.on('blur', this.onBlur, this);
11663             if(this.cls){
11664                 btn.addClass(this.cls);
11665             }
11666             if(this.icon){
11667                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11668             }
11669             if(this.iconCls){
11670                 btnEl.addClass(this.iconCls);
11671                 if(!this.cls){
11672                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11673                 }
11674             }
11675             if(this.tabIndex !== undefined){
11676                 btnEl.dom.tabIndex = this.tabIndex;
11677             }
11678             if(this.tooltip){
11679                 if(typeof this.tooltip == 'object'){
11680                     Roo.QuickTips.tips(Roo.apply({
11681                           target: btnEl.id
11682                     }, this.tooltip));
11683                 } else {
11684                     btnEl.dom[this.tooltipType] = this.tooltip;
11685                 }
11686             }
11687         }else{
11688             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11689         }
11690         this.el = btn;
11691         if(this.id){
11692             this.el.dom.id = this.el.id = this.id;
11693         }
11694         if(this.menu){
11695             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11696             this.menu.on("show", this.onMenuShow, this);
11697             this.menu.on("hide", this.onMenuHide, this);
11698         }
11699         btn.addClass("x-btn");
11700         if(Roo.isIE && !Roo.isIE7){
11701             this.autoWidth.defer(1, this);
11702         }else{
11703             this.autoWidth();
11704         }
11705         if(this.handleMouseEvents){
11706             btn.on("mouseover", this.onMouseOver, this);
11707             btn.on("mouseout", this.onMouseOut, this);
11708             btn.on("mousedown", this.onMouseDown, this);
11709         }
11710         btn.on(this.clickEvent, this.onClick, this);
11711         //btn.on("mouseup", this.onMouseUp, this);
11712         if(this.hidden){
11713             this.hide();
11714         }
11715         if(this.disabled){
11716             this.disable();
11717         }
11718         Roo.ButtonToggleMgr.register(this);
11719         if(this.pressed){
11720             this.el.addClass("x-btn-pressed");
11721         }
11722         if(this.repeat){
11723             var repeater = new Roo.util.ClickRepeater(btn,
11724                 typeof this.repeat == "object" ? this.repeat : {}
11725             );
11726             repeater.on("click", this.onClick,  this);
11727         }
11728         
11729         this.fireEvent('render', this);
11730         
11731     },
11732     /**
11733      * Returns the button's underlying element
11734      * @return {Roo.Element} The element
11735      */
11736     getEl : function(){
11737         return this.el;  
11738     },
11739     
11740     /**
11741      * Destroys this Button and removes any listeners.
11742      */
11743     destroy : function(){
11744         Roo.ButtonToggleMgr.unregister(this);
11745         this.el.removeAllListeners();
11746         this.purgeListeners();
11747         this.el.remove();
11748     },
11749
11750     // private
11751     autoWidth : function(){
11752         if(this.el){
11753             this.el.setWidth("auto");
11754             if(Roo.isIE7 && Roo.isStrict){
11755                 var ib = this.el.child('button');
11756                 if(ib && ib.getWidth() > 20){
11757                     ib.clip();
11758                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11759                 }
11760             }
11761             if(this.minWidth){
11762                 if(this.hidden){
11763                     this.el.beginMeasure();
11764                 }
11765                 if(this.el.getWidth() < this.minWidth){
11766                     this.el.setWidth(this.minWidth);
11767                 }
11768                 if(this.hidden){
11769                     this.el.endMeasure();
11770                 }
11771             }
11772         }
11773     },
11774
11775     /**
11776      * Assigns this button's click handler
11777      * @param {Function} handler The function to call when the button is clicked
11778      * @param {Object} scope (optional) Scope for the function passed in
11779      */
11780     setHandler : function(handler, scope){
11781         this.handler = handler;
11782         this.scope = scope;  
11783     },
11784     
11785     /**
11786      * Sets this button's text
11787      * @param {String} text The button text
11788      */
11789     setText : function(text){
11790         this.text = text;
11791         if(this.el){
11792             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11793         }
11794         this.autoWidth();
11795     },
11796     
11797     /**
11798      * Gets the text for this button
11799      * @return {String} The button text
11800      */
11801     getText : function(){
11802         return this.text;  
11803     },
11804     
11805     /**
11806      * Show this button
11807      */
11808     show: function(){
11809         this.hidden = false;
11810         if(this.el){
11811             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11812         }
11813     },
11814     
11815     /**
11816      * Hide this button
11817      */
11818     hide: function(){
11819         this.hidden = true;
11820         if(this.el){
11821             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11822         }
11823     },
11824     
11825     /**
11826      * Convenience function for boolean show/hide
11827      * @param {Boolean} visible True to show, false to hide
11828      */
11829     setVisible: function(visible){
11830         if(visible) {
11831             this.show();
11832         }else{
11833             this.hide();
11834         }
11835     },
11836     
11837     /**
11838      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11839      * @param {Boolean} state (optional) Force a particular state
11840      */
11841     toggle : function(state){
11842         state = state === undefined ? !this.pressed : state;
11843         if(state != this.pressed){
11844             if(state){
11845                 this.el.addClass("x-btn-pressed");
11846                 this.pressed = true;
11847                 this.fireEvent("toggle", this, true);
11848             }else{
11849                 this.el.removeClass("x-btn-pressed");
11850                 this.pressed = false;
11851                 this.fireEvent("toggle", this, false);
11852             }
11853             if(this.toggleHandler){
11854                 this.toggleHandler.call(this.scope || this, this, state);
11855             }
11856         }
11857     },
11858     
11859     /**
11860      * Focus the button
11861      */
11862     focus : function(){
11863         this.el.child('button:first').focus();
11864     },
11865     
11866     /**
11867      * Disable this button
11868      */
11869     disable : function(){
11870         if(this.el){
11871             this.el.addClass("x-btn-disabled");
11872         }
11873         this.disabled = true;
11874     },
11875     
11876     /**
11877      * Enable this button
11878      */
11879     enable : function(){
11880         if(this.el){
11881             this.el.removeClass("x-btn-disabled");
11882         }
11883         this.disabled = false;
11884     },
11885
11886     /**
11887      * Convenience function for boolean enable/disable
11888      * @param {Boolean} enabled True to enable, false to disable
11889      */
11890     setDisabled : function(v){
11891         this[v !== true ? "enable" : "disable"]();
11892     },
11893
11894     // private
11895     onClick : function(e){
11896         if(e){
11897             e.preventDefault();
11898         }
11899         if(e.button != 0){
11900             return;
11901         }
11902         if(!this.disabled){
11903             if(this.enableToggle){
11904                 this.toggle();
11905             }
11906             if(this.menu && !this.menu.isVisible()){
11907                 this.menu.show(this.el, this.menuAlign);
11908             }
11909             this.fireEvent("click", this, e);
11910             if(this.handler){
11911                 this.el.removeClass("x-btn-over");
11912                 this.handler.call(this.scope || this, this, e);
11913             }
11914         }
11915     },
11916     // private
11917     onMouseOver : function(e){
11918         if(!this.disabled){
11919             this.el.addClass("x-btn-over");
11920             this.fireEvent('mouseover', this, e);
11921         }
11922     },
11923     // private
11924     onMouseOut : function(e){
11925         if(!e.within(this.el,  true)){
11926             this.el.removeClass("x-btn-over");
11927             this.fireEvent('mouseout', this, e);
11928         }
11929     },
11930     // private
11931     onFocus : function(e){
11932         if(!this.disabled){
11933             this.el.addClass("x-btn-focus");
11934         }
11935     },
11936     // private
11937     onBlur : function(e){
11938         this.el.removeClass("x-btn-focus");
11939     },
11940     // private
11941     onMouseDown : function(e){
11942         if(!this.disabled && e.button == 0){
11943             this.el.addClass("x-btn-click");
11944             Roo.get(document).on('mouseup', this.onMouseUp, this);
11945         }
11946     },
11947     // private
11948     onMouseUp : function(e){
11949         if(e.button == 0){
11950             this.el.removeClass("x-btn-click");
11951             Roo.get(document).un('mouseup', this.onMouseUp, this);
11952         }
11953     },
11954     // private
11955     onMenuShow : function(e){
11956         this.el.addClass("x-btn-menu-active");
11957     },
11958     // private
11959     onMenuHide : function(e){
11960         this.el.removeClass("x-btn-menu-active");
11961     }   
11962 });
11963
11964 // Private utility class used by Button
11965 Roo.ButtonToggleMgr = function(){
11966    var groups = {};
11967    
11968    function toggleGroup(btn, state){
11969        if(state){
11970            var g = groups[btn.toggleGroup];
11971            for(var i = 0, l = g.length; i < l; i++){
11972                if(g[i] != btn){
11973                    g[i].toggle(false);
11974                }
11975            }
11976        }
11977    }
11978    
11979    return {
11980        register : function(btn){
11981            if(!btn.toggleGroup){
11982                return;
11983            }
11984            var g = groups[btn.toggleGroup];
11985            if(!g){
11986                g = groups[btn.toggleGroup] = [];
11987            }
11988            g.push(btn);
11989            btn.on("toggle", toggleGroup);
11990        },
11991        
11992        unregister : function(btn){
11993            if(!btn.toggleGroup){
11994                return;
11995            }
11996            var g = groups[btn.toggleGroup];
11997            if(g){
11998                g.remove(btn);
11999                btn.un("toggle", toggleGroup);
12000            }
12001        }
12002    };
12003 }();/*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013  
12014 /**
12015  * @class Roo.SplitButton
12016  * @extends Roo.Button
12017  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12018  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12019  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12020  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12021  * @cfg {String} arrowTooltip The title attribute of the arrow
12022  * @constructor
12023  * Create a new menu button
12024  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12025  * @param {Object} config The config object
12026  */
12027 Roo.SplitButton = function(renderTo, config){
12028     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12029     /**
12030      * @event arrowclick
12031      * Fires when this button's arrow is clicked
12032      * @param {SplitButton} this
12033      * @param {EventObject} e The click event
12034      */
12035     this.addEvents({"arrowclick":true});
12036 };
12037
12038 Roo.extend(Roo.SplitButton, Roo.Button, {
12039     render : function(renderTo){
12040         // this is one sweet looking template!
12041         var tpl = new Roo.Template(
12042             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12043             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12044             '<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>',
12045             "</tbody></table></td><td>",
12046             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12047             '<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>',
12048             "</tbody></table></td></tr></table>"
12049         );
12050         var btn = tpl.append(renderTo, [this.text, this.type], true);
12051         var btnEl = btn.child("button");
12052         if(this.cls){
12053             btn.addClass(this.cls);
12054         }
12055         if(this.icon){
12056             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12057         }
12058         if(this.iconCls){
12059             btnEl.addClass(this.iconCls);
12060             if(!this.cls){
12061                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12062             }
12063         }
12064         this.el = btn;
12065         if(this.handleMouseEvents){
12066             btn.on("mouseover", this.onMouseOver, this);
12067             btn.on("mouseout", this.onMouseOut, this);
12068             btn.on("mousedown", this.onMouseDown, this);
12069             btn.on("mouseup", this.onMouseUp, this);
12070         }
12071         btn.on(this.clickEvent, this.onClick, this);
12072         if(this.tooltip){
12073             if(typeof this.tooltip == 'object'){
12074                 Roo.QuickTips.tips(Roo.apply({
12075                       target: btnEl.id
12076                 }, this.tooltip));
12077             } else {
12078                 btnEl.dom[this.tooltipType] = this.tooltip;
12079             }
12080         }
12081         if(this.arrowTooltip){
12082             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12083         }
12084         if(this.hidden){
12085             this.hide();
12086         }
12087         if(this.disabled){
12088             this.disable();
12089         }
12090         if(this.pressed){
12091             this.el.addClass("x-btn-pressed");
12092         }
12093         if(Roo.isIE && !Roo.isIE7){
12094             this.autoWidth.defer(1, this);
12095         }else{
12096             this.autoWidth();
12097         }
12098         if(this.menu){
12099             this.menu.on("show", this.onMenuShow, this);
12100             this.menu.on("hide", this.onMenuHide, this);
12101         }
12102         this.fireEvent('render', this);
12103     },
12104
12105     // private
12106     autoWidth : function(){
12107         if(this.el){
12108             var tbl = this.el.child("table:first");
12109             var tbl2 = this.el.child("table:last");
12110             this.el.setWidth("auto");
12111             tbl.setWidth("auto");
12112             if(Roo.isIE7 && Roo.isStrict){
12113                 var ib = this.el.child('button:first');
12114                 if(ib && ib.getWidth() > 20){
12115                     ib.clip();
12116                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12117                 }
12118             }
12119             if(this.minWidth){
12120                 if(this.hidden){
12121                     this.el.beginMeasure();
12122                 }
12123                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12124                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12125                 }
12126                 if(this.hidden){
12127                     this.el.endMeasure();
12128                 }
12129             }
12130             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12131         } 
12132     },
12133     /**
12134      * Sets this button's click handler
12135      * @param {Function} handler The function to call when the button is clicked
12136      * @param {Object} scope (optional) Scope for the function passed above
12137      */
12138     setHandler : function(handler, scope){
12139         this.handler = handler;
12140         this.scope = scope;  
12141     },
12142     
12143     /**
12144      * Sets this button's arrow click handler
12145      * @param {Function} handler The function to call when the arrow is clicked
12146      * @param {Object} scope (optional) Scope for the function passed above
12147      */
12148     setArrowHandler : function(handler, scope){
12149         this.arrowHandler = handler;
12150         this.scope = scope;  
12151     },
12152     
12153     /**
12154      * Focus the button
12155      */
12156     focus : function(){
12157         if(this.el){
12158             this.el.child("button:first").focus();
12159         }
12160     },
12161
12162     // private
12163     onClick : function(e){
12164         e.preventDefault();
12165         if(!this.disabled){
12166             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12167                 if(this.menu && !this.menu.isVisible()){
12168                     this.menu.show(this.el, this.menuAlign);
12169                 }
12170                 this.fireEvent("arrowclick", this, e);
12171                 if(this.arrowHandler){
12172                     this.arrowHandler.call(this.scope || this, this, e);
12173                 }
12174             }else{
12175                 this.fireEvent("click", this, e);
12176                 if(this.handler){
12177                     this.handler.call(this.scope || this, this, e);
12178                 }
12179             }
12180         }
12181     },
12182     // private
12183     onMouseDown : function(e){
12184         if(!this.disabled){
12185             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12186         }
12187     },
12188     // private
12189     onMouseUp : function(e){
12190         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12191     }   
12192 });
12193
12194
12195 // backwards compat
12196 Roo.MenuButton = Roo.SplitButton;/*
12197  * Based on:
12198  * Ext JS Library 1.1.1
12199  * Copyright(c) 2006-2007, Ext JS, LLC.
12200  *
12201  * Originally Released Under LGPL - original licence link has changed is not relivant.
12202  *
12203  * Fork - LGPL
12204  * <script type="text/javascript">
12205  */
12206
12207 /**
12208  * @class Roo.Toolbar
12209  * Basic Toolbar class.
12210  * @constructor
12211  * Creates a new Toolbar
12212  * @param {Object} config The config object
12213  */ 
12214 Roo.Toolbar = function(container, buttons, config)
12215 {
12216     /// old consturctor format still supported..
12217     if(container instanceof Array){ // omit the container for later rendering
12218         buttons = container;
12219         config = buttons;
12220         container = null;
12221     }
12222     if (typeof(container) == 'object' && container.xtype) {
12223         config = container;
12224         container = config.container;
12225         buttons = config.buttons; // not really - use items!!
12226     }
12227     var xitems = [];
12228     if (config && config.items) {
12229         xitems = config.items;
12230         delete config.items;
12231     }
12232     Roo.apply(this, config);
12233     this.buttons = buttons;
12234     
12235     if(container){
12236         this.render(container);
12237     }
12238     Roo.each(xitems, function(b) {
12239         this.add(b);
12240     }, this);
12241     
12242 };
12243
12244 Roo.Toolbar.prototype = {
12245     /**
12246      * @cfg {Roo.data.Store} items
12247      * array of button configs or elements to add
12248      */
12249     
12250     /**
12251      * @cfg {String/HTMLElement/Element} container
12252      * The id or element that will contain the toolbar
12253      */
12254     // private
12255     render : function(ct){
12256         this.el = Roo.get(ct);
12257         if(this.cls){
12258             this.el.addClass(this.cls);
12259         }
12260         // using a table allows for vertical alignment
12261         // 100% width is needed by Safari...
12262         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12263         this.tr = this.el.child("tr", true);
12264         var autoId = 0;
12265         this.items = new Roo.util.MixedCollection(false, function(o){
12266             return o.id || ("item" + (++autoId));
12267         });
12268         if(this.buttons){
12269             this.add.apply(this, this.buttons);
12270             delete this.buttons;
12271         }
12272     },
12273
12274     /**
12275      * Adds element(s) to the toolbar -- this function takes a variable number of 
12276      * arguments of mixed type and adds them to the toolbar.
12277      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12278      * <ul>
12279      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12280      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12281      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12282      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12283      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12284      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12285      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12286      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12287      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12288      * </ul>
12289      * @param {Mixed} arg2
12290      * @param {Mixed} etc.
12291      */
12292     add : function(){
12293         var a = arguments, l = a.length;
12294         for(var i = 0; i < l; i++){
12295             this._add(a[i]);
12296         }
12297     },
12298     // private..
12299     _add : function(el) {
12300         
12301         if (el.xtype) {
12302             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12303         }
12304         
12305         if (el.applyTo){ // some kind of form field
12306             return this.addField(el);
12307         } 
12308         if (el.render){ // some kind of Toolbar.Item
12309             return this.addItem(el);
12310         }
12311         if (typeof el == "string"){ // string
12312             if(el == "separator" || el == "-"){
12313                 return this.addSeparator();
12314             }
12315             if (el == " "){
12316                 return this.addSpacer();
12317             }
12318             if(el == "->"){
12319                 return this.addFill();
12320             }
12321             return this.addText(el);
12322             
12323         }
12324         if(el.tagName){ // element
12325             return this.addElement(el);
12326         }
12327         if(typeof el == "object"){ // must be button config?
12328             return this.addButton(el);
12329         }
12330         // and now what?!?!
12331         return false;
12332         
12333     },
12334     
12335     /**
12336      * Add an Xtype element
12337      * @param {Object} xtype Xtype Object
12338      * @return {Object} created Object
12339      */
12340     addxtype : function(e){
12341         return this.add(e);  
12342     },
12343     
12344     /**
12345      * Returns the Element for this toolbar.
12346      * @return {Roo.Element}
12347      */
12348     getEl : function(){
12349         return this.el;  
12350     },
12351     
12352     /**
12353      * Adds a separator
12354      * @return {Roo.Toolbar.Item} The separator item
12355      */
12356     addSeparator : function(){
12357         return this.addItem(new Roo.Toolbar.Separator());
12358     },
12359
12360     /**
12361      * Adds a spacer element
12362      * @return {Roo.Toolbar.Spacer} The spacer item
12363      */
12364     addSpacer : function(){
12365         return this.addItem(new Roo.Toolbar.Spacer());
12366     },
12367
12368     /**
12369      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12370      * @return {Roo.Toolbar.Fill} The fill item
12371      */
12372     addFill : function(){
12373         return this.addItem(new Roo.Toolbar.Fill());
12374     },
12375
12376     /**
12377      * Adds any standard HTML element to the toolbar
12378      * @param {String/HTMLElement/Element} el The element or id of the element to add
12379      * @return {Roo.Toolbar.Item} The element's item
12380      */
12381     addElement : function(el){
12382         return this.addItem(new Roo.Toolbar.Item(el));
12383     },
12384     /**
12385      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12386      * @type Roo.util.MixedCollection  
12387      */
12388     items : false,
12389      
12390     /**
12391      * Adds any Toolbar.Item or subclass
12392      * @param {Roo.Toolbar.Item} item
12393      * @return {Roo.Toolbar.Item} The item
12394      */
12395     addItem : function(item){
12396         var td = this.nextBlock();
12397         item.render(td);
12398         this.items.add(item);
12399         return item;
12400     },
12401     
12402     /**
12403      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12404      * @param {Object/Array} config A button config or array of configs
12405      * @return {Roo.Toolbar.Button/Array}
12406      */
12407     addButton : function(config){
12408         if(config instanceof Array){
12409             var buttons = [];
12410             for(var i = 0, len = config.length; i < len; i++) {
12411                 buttons.push(this.addButton(config[i]));
12412             }
12413             return buttons;
12414         }
12415         var b = config;
12416         if(!(config instanceof Roo.Toolbar.Button)){
12417             b = config.split ?
12418                 new Roo.Toolbar.SplitButton(config) :
12419                 new Roo.Toolbar.Button(config);
12420         }
12421         var td = this.nextBlock();
12422         b.render(td);
12423         this.items.add(b);
12424         return b;
12425     },
12426     
12427     /**
12428      * Adds text to the toolbar
12429      * @param {String} text The text to add
12430      * @return {Roo.Toolbar.Item} The element's item
12431      */
12432     addText : function(text){
12433         return this.addItem(new Roo.Toolbar.TextItem(text));
12434     },
12435     
12436     /**
12437      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12438      * @param {Number} index The index where the item is to be inserted
12439      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12440      * @return {Roo.Toolbar.Button/Item}
12441      */
12442     insertButton : function(index, item){
12443         if(item instanceof Array){
12444             var buttons = [];
12445             for(var i = 0, len = item.length; i < len; i++) {
12446                buttons.push(this.insertButton(index + i, item[i]));
12447             }
12448             return buttons;
12449         }
12450         if (!(item instanceof Roo.Toolbar.Button)){
12451            item = new Roo.Toolbar.Button(item);
12452         }
12453         var td = document.createElement("td");
12454         this.tr.insertBefore(td, this.tr.childNodes[index]);
12455         item.render(td);
12456         this.items.insert(index, item);
12457         return item;
12458     },
12459     
12460     /**
12461      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12462      * @param {Object} config
12463      * @return {Roo.Toolbar.Item} The element's item
12464      */
12465     addDom : function(config, returnEl){
12466         var td = this.nextBlock();
12467         Roo.DomHelper.overwrite(td, config);
12468         var ti = new Roo.Toolbar.Item(td.firstChild);
12469         ti.render(td);
12470         this.items.add(ti);
12471         return ti;
12472     },
12473
12474     /**
12475      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12476      * @type Roo.util.MixedCollection  
12477      */
12478     fields : false,
12479     
12480     /**
12481      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12482      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12483      * @param {Roo.form.Field} field
12484      * @return {Roo.ToolbarItem}
12485      */
12486      
12487       
12488     addField : function(field) {
12489         if (!this.fields) {
12490             var autoId = 0;
12491             this.fields = new Roo.util.MixedCollection(false, function(o){
12492                 return o.id || ("item" + (++autoId));
12493             });
12494
12495         }
12496         
12497         var td = this.nextBlock();
12498         field.render(td);
12499         var ti = new Roo.Toolbar.Item(td.firstChild);
12500         ti.render(td);
12501         this.items.add(ti);
12502         this.fields.add(field);
12503         return ti;
12504     },
12505     /**
12506      * Hide the toolbar
12507      * @method hide
12508      */
12509      
12510       
12511     hide : function()
12512     {
12513         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12514         this.el.child('div').hide();
12515     },
12516     /**
12517      * Show the toolbar
12518      * @method show
12519      */
12520     show : function()
12521     {
12522         this.el.child('div').show();
12523     },
12524       
12525     // private
12526     nextBlock : function(){
12527         var td = document.createElement("td");
12528         this.tr.appendChild(td);
12529         return td;
12530     },
12531
12532     // private
12533     destroy : function(){
12534         if(this.items){ // rendered?
12535             Roo.destroy.apply(Roo, this.items.items);
12536         }
12537         if(this.fields){ // rendered?
12538             Roo.destroy.apply(Roo, this.fields.items);
12539         }
12540         Roo.Element.uncache(this.el, this.tr);
12541     }
12542 };
12543
12544 /**
12545  * @class Roo.Toolbar.Item
12546  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12547  * @constructor
12548  * Creates a new Item
12549  * @param {HTMLElement} el 
12550  */
12551 Roo.Toolbar.Item = function(el){
12552     this.el = Roo.getDom(el);
12553     this.id = Roo.id(this.el);
12554     this.hidden = false;
12555 };
12556
12557 Roo.Toolbar.Item.prototype = {
12558     
12559     /**
12560      * Get this item's HTML Element
12561      * @return {HTMLElement}
12562      */
12563     getEl : function(){
12564        return this.el;  
12565     },
12566
12567     // private
12568     render : function(td){
12569         this.td = td;
12570         td.appendChild(this.el);
12571     },
12572     
12573     /**
12574      * Removes and destroys this item.
12575      */
12576     destroy : function(){
12577         this.td.parentNode.removeChild(this.td);
12578     },
12579     
12580     /**
12581      * Shows this item.
12582      */
12583     show: function(){
12584         this.hidden = false;
12585         this.td.style.display = "";
12586     },
12587     
12588     /**
12589      * Hides this item.
12590      */
12591     hide: function(){
12592         this.hidden = true;
12593         this.td.style.display = "none";
12594     },
12595     
12596     /**
12597      * Convenience function for boolean show/hide.
12598      * @param {Boolean} visible true to show/false to hide
12599      */
12600     setVisible: function(visible){
12601         if(visible) {
12602             this.show();
12603         }else{
12604             this.hide();
12605         }
12606     },
12607     
12608     /**
12609      * Try to focus this item.
12610      */
12611     focus : function(){
12612         Roo.fly(this.el).focus();
12613     },
12614     
12615     /**
12616      * Disables this item.
12617      */
12618     disable : function(){
12619         Roo.fly(this.td).addClass("x-item-disabled");
12620         this.disabled = true;
12621         this.el.disabled = true;
12622     },
12623     
12624     /**
12625      * Enables this item.
12626      */
12627     enable : function(){
12628         Roo.fly(this.td).removeClass("x-item-disabled");
12629         this.disabled = false;
12630         this.el.disabled = false;
12631     }
12632 };
12633
12634
12635 /**
12636  * @class Roo.Toolbar.Separator
12637  * @extends Roo.Toolbar.Item
12638  * A simple toolbar separator class
12639  * @constructor
12640  * Creates a new Separator
12641  */
12642 Roo.Toolbar.Separator = function(){
12643     var s = document.createElement("span");
12644     s.className = "ytb-sep";
12645     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12646 };
12647 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12648     enable:Roo.emptyFn,
12649     disable:Roo.emptyFn,
12650     focus:Roo.emptyFn
12651 });
12652
12653 /**
12654  * @class Roo.Toolbar.Spacer
12655  * @extends Roo.Toolbar.Item
12656  * A simple element that adds extra horizontal space to a toolbar.
12657  * @constructor
12658  * Creates a new Spacer
12659  */
12660 Roo.Toolbar.Spacer = function(){
12661     var s = document.createElement("div");
12662     s.className = "ytb-spacer";
12663     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12664 };
12665 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12666     enable:Roo.emptyFn,
12667     disable:Roo.emptyFn,
12668     focus:Roo.emptyFn
12669 });
12670
12671 /**
12672  * @class Roo.Toolbar.Fill
12673  * @extends Roo.Toolbar.Spacer
12674  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12675  * @constructor
12676  * Creates a new Spacer
12677  */
12678 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12679     // private
12680     render : function(td){
12681         td.style.width = '100%';
12682         Roo.Toolbar.Fill.superclass.render.call(this, td);
12683     }
12684 });
12685
12686 /**
12687  * @class Roo.Toolbar.TextItem
12688  * @extends Roo.Toolbar.Item
12689  * A simple class that renders text directly into a toolbar.
12690  * @constructor
12691  * Creates a new TextItem
12692  * @param {String} text
12693  */
12694 Roo.Toolbar.TextItem = function(text){
12695     if (typeof(text) == 'object') {
12696         text = text.text;
12697     }
12698     var s = document.createElement("span");
12699     s.className = "ytb-text";
12700     s.innerHTML = text;
12701     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12702 };
12703 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12704     enable:Roo.emptyFn,
12705     disable:Roo.emptyFn,
12706     focus:Roo.emptyFn
12707 });
12708
12709 /**
12710  * @class Roo.Toolbar.Button
12711  * @extends Roo.Button
12712  * A button that renders into a toolbar.
12713  * @constructor
12714  * Creates a new Button
12715  * @param {Object} config A standard {@link Roo.Button} config object
12716  */
12717 Roo.Toolbar.Button = function(config){
12718     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12719 };
12720 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12721     render : function(td){
12722         this.td = td;
12723         Roo.Toolbar.Button.superclass.render.call(this, td);
12724     },
12725     
12726     /**
12727      * Removes and destroys this button
12728      */
12729     destroy : function(){
12730         Roo.Toolbar.Button.superclass.destroy.call(this);
12731         this.td.parentNode.removeChild(this.td);
12732     },
12733     
12734     /**
12735      * Shows this button
12736      */
12737     show: function(){
12738         this.hidden = false;
12739         this.td.style.display = "";
12740     },
12741     
12742     /**
12743      * Hides this button
12744      */
12745     hide: function(){
12746         this.hidden = true;
12747         this.td.style.display = "none";
12748     },
12749
12750     /**
12751      * Disables this item
12752      */
12753     disable : function(){
12754         Roo.fly(this.td).addClass("x-item-disabled");
12755         this.disabled = true;
12756     },
12757
12758     /**
12759      * Enables this item
12760      */
12761     enable : function(){
12762         Roo.fly(this.td).removeClass("x-item-disabled");
12763         this.disabled = false;
12764     }
12765 });
12766 // backwards compat
12767 Roo.ToolbarButton = Roo.Toolbar.Button;
12768
12769 /**
12770  * @class Roo.Toolbar.SplitButton
12771  * @extends Roo.SplitButton
12772  * A menu button that renders into a toolbar.
12773  * @constructor
12774  * Creates a new SplitButton
12775  * @param {Object} config A standard {@link Roo.SplitButton} config object
12776  */
12777 Roo.Toolbar.SplitButton = function(config){
12778     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12779 };
12780 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12781     render : function(td){
12782         this.td = td;
12783         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12784     },
12785     
12786     /**
12787      * Removes and destroys this button
12788      */
12789     destroy : function(){
12790         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12791         this.td.parentNode.removeChild(this.td);
12792     },
12793     
12794     /**
12795      * Shows this button
12796      */
12797     show: function(){
12798         this.hidden = false;
12799         this.td.style.display = "";
12800     },
12801     
12802     /**
12803      * Hides this button
12804      */
12805     hide: function(){
12806         this.hidden = true;
12807         this.td.style.display = "none";
12808     }
12809 });
12810
12811 // backwards compat
12812 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12813  * Based on:
12814  * Ext JS Library 1.1.1
12815  * Copyright(c) 2006-2007, Ext JS, LLC.
12816  *
12817  * Originally Released Under LGPL - original licence link has changed is not relivant.
12818  *
12819  * Fork - LGPL
12820  * <script type="text/javascript">
12821  */
12822  
12823 /**
12824  * @class Roo.PagingToolbar
12825  * @extends Roo.Toolbar
12826  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12827  * @constructor
12828  * Create a new PagingToolbar
12829  * @param {Object} config The config object
12830  */
12831 Roo.PagingToolbar = function(el, ds, config)
12832 {
12833     // old args format still supported... - xtype is prefered..
12834     if (typeof(el) == 'object' && el.xtype) {
12835         // created from xtype...
12836         config = el;
12837         ds = el.dataSource;
12838         el = config.container;
12839     }
12840     var items = [];
12841     if (config.items) {
12842         items = config.items;
12843         config.items = [];
12844     }
12845     
12846     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12847     this.ds = ds;
12848     this.cursor = 0;
12849     this.renderButtons(this.el);
12850     this.bind(ds);
12851     
12852     // supprot items array.
12853    
12854     Roo.each(items, function(e) {
12855         this.add(Roo.factory(e));
12856     },this);
12857     
12858 };
12859
12860 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12861     /**
12862      * @cfg {Roo.data.Store} dataSource
12863      * The underlying data store providing the paged data
12864      */
12865     /**
12866      * @cfg {String/HTMLElement/Element} container
12867      * container The id or element that will contain the toolbar
12868      */
12869     /**
12870      * @cfg {Boolean} displayInfo
12871      * True to display the displayMsg (defaults to false)
12872      */
12873     /**
12874      * @cfg {Number} pageSize
12875      * The number of records to display per page (defaults to 20)
12876      */
12877     pageSize: 20,
12878     /**
12879      * @cfg {String} displayMsg
12880      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12881      */
12882     displayMsg : 'Displaying {0} - {1} of {2}',
12883     /**
12884      * @cfg {String} emptyMsg
12885      * The message to display when no records are found (defaults to "No data to display")
12886      */
12887     emptyMsg : 'No data to display',
12888     /**
12889      * Customizable piece of the default paging text (defaults to "Page")
12890      * @type String
12891      */
12892     beforePageText : "Page",
12893     /**
12894      * Customizable piece of the default paging text (defaults to "of %0")
12895      * @type String
12896      */
12897     afterPageText : "of {0}",
12898     /**
12899      * Customizable piece of the default paging text (defaults to "First Page")
12900      * @type String
12901      */
12902     firstText : "First Page",
12903     /**
12904      * Customizable piece of the default paging text (defaults to "Previous Page")
12905      * @type String
12906      */
12907     prevText : "Previous Page",
12908     /**
12909      * Customizable piece of the default paging text (defaults to "Next Page")
12910      * @type String
12911      */
12912     nextText : "Next Page",
12913     /**
12914      * Customizable piece of the default paging text (defaults to "Last Page")
12915      * @type String
12916      */
12917     lastText : "Last Page",
12918     /**
12919      * Customizable piece of the default paging text (defaults to "Refresh")
12920      * @type String
12921      */
12922     refreshText : "Refresh",
12923
12924     // private
12925     renderButtons : function(el){
12926         Roo.PagingToolbar.superclass.render.call(this, el);
12927         this.first = this.addButton({
12928             tooltip: this.firstText,
12929             cls: "x-btn-icon x-grid-page-first",
12930             disabled: true,
12931             handler: this.onClick.createDelegate(this, ["first"])
12932         });
12933         this.prev = this.addButton({
12934             tooltip: this.prevText,
12935             cls: "x-btn-icon x-grid-page-prev",
12936             disabled: true,
12937             handler: this.onClick.createDelegate(this, ["prev"])
12938         });
12939         //this.addSeparator();
12940         this.add(this.beforePageText);
12941         this.field = Roo.get(this.addDom({
12942            tag: "input",
12943            type: "text",
12944            size: "3",
12945            value: "1",
12946            cls: "x-grid-page-number"
12947         }).el);
12948         this.field.on("keydown", this.onPagingKeydown, this);
12949         this.field.on("focus", function(){this.dom.select();});
12950         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12951         this.field.setHeight(18);
12952         //this.addSeparator();
12953         this.next = this.addButton({
12954             tooltip: this.nextText,
12955             cls: "x-btn-icon x-grid-page-next",
12956             disabled: true,
12957             handler: this.onClick.createDelegate(this, ["next"])
12958         });
12959         this.last = this.addButton({
12960             tooltip: this.lastText,
12961             cls: "x-btn-icon x-grid-page-last",
12962             disabled: true,
12963             handler: this.onClick.createDelegate(this, ["last"])
12964         });
12965         //this.addSeparator();
12966         this.loading = this.addButton({
12967             tooltip: this.refreshText,
12968             cls: "x-btn-icon x-grid-loading",
12969             handler: this.onClick.createDelegate(this, ["refresh"])
12970         });
12971
12972         if(this.displayInfo){
12973             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12974         }
12975     },
12976
12977     // private
12978     updateInfo : function(){
12979         if(this.displayEl){
12980             var count = this.ds.getCount();
12981             var msg = count == 0 ?
12982                 this.emptyMsg :
12983                 String.format(
12984                     this.displayMsg,
12985                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12986                 );
12987             this.displayEl.update(msg);
12988         }
12989     },
12990
12991     // private
12992     onLoad : function(ds, r, o){
12993        this.cursor = o.params ? o.params.start : 0;
12994        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12995
12996        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12997        this.field.dom.value = ap;
12998        this.first.setDisabled(ap == 1);
12999        this.prev.setDisabled(ap == 1);
13000        this.next.setDisabled(ap == ps);
13001        this.last.setDisabled(ap == ps);
13002        this.loading.enable();
13003        this.updateInfo();
13004     },
13005
13006     // private
13007     getPageData : function(){
13008         var total = this.ds.getTotalCount();
13009         return {
13010             total : total,
13011             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13012             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13013         };
13014     },
13015
13016     // private
13017     onLoadError : function(){
13018         this.loading.enable();
13019     },
13020
13021     // private
13022     onPagingKeydown : function(e){
13023         var k = e.getKey();
13024         var d = this.getPageData();
13025         if(k == e.RETURN){
13026             var v = this.field.dom.value, pageNum;
13027             if(!v || isNaN(pageNum = parseInt(v, 10))){
13028                 this.field.dom.value = d.activePage;
13029                 return;
13030             }
13031             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13032             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13033             e.stopEvent();
13034         }
13035         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))
13036         {
13037           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13038           this.field.dom.value = pageNum;
13039           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13040           e.stopEvent();
13041         }
13042         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13043         {
13044           var v = this.field.dom.value, pageNum; 
13045           var increment = (e.shiftKey) ? 10 : 1;
13046           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13047             increment *= -1;
13048           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13049             this.field.dom.value = d.activePage;
13050             return;
13051           }
13052           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13053           {
13054             this.field.dom.value = parseInt(v, 10) + increment;
13055             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13056             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13057           }
13058           e.stopEvent();
13059         }
13060     },
13061
13062     // private
13063     beforeLoad : function(){
13064         if(this.loading){
13065             this.loading.disable();
13066         }
13067     },
13068
13069     // private
13070     onClick : function(which){
13071         var ds = this.ds;
13072         switch(which){
13073             case "first":
13074                 ds.load({params:{start: 0, limit: this.pageSize}});
13075             break;
13076             case "prev":
13077                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13078             break;
13079             case "next":
13080                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13081             break;
13082             case "last":
13083                 var total = ds.getTotalCount();
13084                 var extra = total % this.pageSize;
13085                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13086                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13087             break;
13088             case "refresh":
13089                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13090             break;
13091         }
13092     },
13093
13094     /**
13095      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13096      * @param {Roo.data.Store} store The data store to unbind
13097      */
13098     unbind : function(ds){
13099         ds.un("beforeload", this.beforeLoad, this);
13100         ds.un("load", this.onLoad, this);
13101         ds.un("loadexception", this.onLoadError, this);
13102         ds.un("remove", this.updateInfo, this);
13103         ds.un("add", this.updateInfo, this);
13104         this.ds = undefined;
13105     },
13106
13107     /**
13108      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13109      * @param {Roo.data.Store} store The data store to bind
13110      */
13111     bind : function(ds){
13112         ds.on("beforeload", this.beforeLoad, this);
13113         ds.on("load", this.onLoad, this);
13114         ds.on("loadexception", this.onLoadError, this);
13115         ds.on("remove", this.updateInfo, this);
13116         ds.on("add", this.updateInfo, this);
13117         this.ds = ds;
13118     }
13119 });/*
13120  * Based on:
13121  * Ext JS Library 1.1.1
13122  * Copyright(c) 2006-2007, Ext JS, LLC.
13123  *
13124  * Originally Released Under LGPL - original licence link has changed is not relivant.
13125  *
13126  * Fork - LGPL
13127  * <script type="text/javascript">
13128  */
13129
13130 /**
13131  * @class Roo.Resizable
13132  * @extends Roo.util.Observable
13133  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13134  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13135  * 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
13136  * the element will be wrapped for you automatically.</p>
13137  * <p>Here is the list of valid resize handles:</p>
13138  * <pre>
13139 Value   Description
13140 ------  -------------------
13141  'n'     north
13142  's'     south
13143  'e'     east
13144  'w'     west
13145  'nw'    northwest
13146  'sw'    southwest
13147  'se'    southeast
13148  'ne'    northeast
13149  'hd'    horizontal drag
13150  'all'   all
13151 </pre>
13152  * <p>Here's an example showing the creation of a typical Resizable:</p>
13153  * <pre><code>
13154 var resizer = new Roo.Resizable("element-id", {
13155     handles: 'all',
13156     minWidth: 200,
13157     minHeight: 100,
13158     maxWidth: 500,
13159     maxHeight: 400,
13160     pinned: true
13161 });
13162 resizer.on("resize", myHandler);
13163 </code></pre>
13164  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13165  * resizer.east.setDisplayed(false);</p>
13166  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13167  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13168  * resize operation's new size (defaults to [0, 0])
13169  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13170  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13171  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13172  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13173  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13174  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13175  * @cfg {Number} width The width of the element in pixels (defaults to null)
13176  * @cfg {Number} height The height of the element in pixels (defaults to null)
13177  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13178  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13179  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13180  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13181  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13182  * in favor of the handles config option (defaults to false)
13183  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13184  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13185  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13186  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13187  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13188  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13189  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13190  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13191  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13192  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13193  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13194  * @constructor
13195  * Create a new resizable component
13196  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13197  * @param {Object} config configuration options
13198   */
13199 Roo.Resizable = function(el, config)
13200 {
13201     this.el = Roo.get(el);
13202
13203     if(config && config.wrap){
13204         config.resizeChild = this.el;
13205         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13206         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13207         this.el.setStyle("overflow", "hidden");
13208         this.el.setPositioning(config.resizeChild.getPositioning());
13209         config.resizeChild.clearPositioning();
13210         if(!config.width || !config.height){
13211             var csize = config.resizeChild.getSize();
13212             this.el.setSize(csize.width, csize.height);
13213         }
13214         if(config.pinned && !config.adjustments){
13215             config.adjustments = "auto";
13216         }
13217     }
13218
13219     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13220     this.proxy.unselectable();
13221     this.proxy.enableDisplayMode('block');
13222
13223     Roo.apply(this, config);
13224
13225     if(this.pinned){
13226         this.disableTrackOver = true;
13227         this.el.addClass("x-resizable-pinned");
13228     }
13229     // if the element isn't positioned, make it relative
13230     var position = this.el.getStyle("position");
13231     if(position != "absolute" && position != "fixed"){
13232         this.el.setStyle("position", "relative");
13233     }
13234     if(!this.handles){ // no handles passed, must be legacy style
13235         this.handles = 's,e,se';
13236         if(this.multiDirectional){
13237             this.handles += ',n,w';
13238         }
13239     }
13240     if(this.handles == "all"){
13241         this.handles = "n s e w ne nw se sw";
13242     }
13243     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13244     var ps = Roo.Resizable.positions;
13245     for(var i = 0, len = hs.length; i < len; i++){
13246         if(hs[i] && ps[hs[i]]){
13247             var pos = ps[hs[i]];
13248             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13249         }
13250     }
13251     // legacy
13252     this.corner = this.southeast;
13253     
13254     // updateBox = the box can move..
13255     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13256         this.updateBox = true;
13257     }
13258
13259     this.activeHandle = null;
13260
13261     if(this.resizeChild){
13262         if(typeof this.resizeChild == "boolean"){
13263             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13264         }else{
13265             this.resizeChild = Roo.get(this.resizeChild, true);
13266         }
13267     }
13268     
13269     if(this.adjustments == "auto"){
13270         var rc = this.resizeChild;
13271         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13272         if(rc && (hw || hn)){
13273             rc.position("relative");
13274             rc.setLeft(hw ? hw.el.getWidth() : 0);
13275             rc.setTop(hn ? hn.el.getHeight() : 0);
13276         }
13277         this.adjustments = [
13278             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13279             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13280         ];
13281     }
13282
13283     if(this.draggable){
13284         this.dd = this.dynamic ?
13285             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13286         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13287     }
13288
13289     // public events
13290     this.addEvents({
13291         /**
13292          * @event beforeresize
13293          * Fired before resize is allowed. Set enabled to false to cancel resize.
13294          * @param {Roo.Resizable} this
13295          * @param {Roo.EventObject} e The mousedown event
13296          */
13297         "beforeresize" : true,
13298         /**
13299          * @event resize
13300          * Fired after a resize.
13301          * @param {Roo.Resizable} this
13302          * @param {Number} width The new width
13303          * @param {Number} height The new height
13304          * @param {Roo.EventObject} e The mouseup event
13305          */
13306         "resize" : true
13307     });
13308
13309     if(this.width !== null && this.height !== null){
13310         this.resizeTo(this.width, this.height);
13311     }else{
13312         this.updateChildSize();
13313     }
13314     if(Roo.isIE){
13315         this.el.dom.style.zoom = 1;
13316     }
13317     Roo.Resizable.superclass.constructor.call(this);
13318 };
13319
13320 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13321         resizeChild : false,
13322         adjustments : [0, 0],
13323         minWidth : 5,
13324         minHeight : 5,
13325         maxWidth : 10000,
13326         maxHeight : 10000,
13327         enabled : true,
13328         animate : false,
13329         duration : .35,
13330         dynamic : false,
13331         handles : false,
13332         multiDirectional : false,
13333         disableTrackOver : false,
13334         easing : 'easeOutStrong',
13335         widthIncrement : 0,
13336         heightIncrement : 0,
13337         pinned : false,
13338         width : null,
13339         height : null,
13340         preserveRatio : false,
13341         transparent: false,
13342         minX: 0,
13343         minY: 0,
13344         draggable: false,
13345
13346         /**
13347          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13348          */
13349         constrainTo: undefined,
13350         /**
13351          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13352          */
13353         resizeRegion: undefined,
13354
13355
13356     /**
13357      * Perform a manual resize
13358      * @param {Number} width
13359      * @param {Number} height
13360      */
13361     resizeTo : function(width, height){
13362         this.el.setSize(width, height);
13363         this.updateChildSize();
13364         this.fireEvent("resize", this, width, height, null);
13365     },
13366
13367     // private
13368     startSizing : function(e, handle){
13369         this.fireEvent("beforeresize", this, e);
13370         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13371
13372             if(!this.overlay){
13373                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13374                 this.overlay.unselectable();
13375                 this.overlay.enableDisplayMode("block");
13376                 this.overlay.on("mousemove", this.onMouseMove, this);
13377                 this.overlay.on("mouseup", this.onMouseUp, this);
13378             }
13379             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13380
13381             this.resizing = true;
13382             this.startBox = this.el.getBox();
13383             this.startPoint = e.getXY();
13384             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13385                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13386
13387             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13388             this.overlay.show();
13389
13390             if(this.constrainTo) {
13391                 var ct = Roo.get(this.constrainTo);
13392                 this.resizeRegion = ct.getRegion().adjust(
13393                     ct.getFrameWidth('t'),
13394                     ct.getFrameWidth('l'),
13395                     -ct.getFrameWidth('b'),
13396                     -ct.getFrameWidth('r')
13397                 );
13398             }
13399
13400             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13401             this.proxy.show();
13402             this.proxy.setBox(this.startBox);
13403             if(!this.dynamic){
13404                 this.proxy.setStyle('visibility', 'visible');
13405             }
13406         }
13407     },
13408
13409     // private
13410     onMouseDown : function(handle, e){
13411         if(this.enabled){
13412             e.stopEvent();
13413             this.activeHandle = handle;
13414             this.startSizing(e, handle);
13415         }
13416     },
13417
13418     // private
13419     onMouseUp : function(e){
13420         var size = this.resizeElement();
13421         this.resizing = false;
13422         this.handleOut();
13423         this.overlay.hide();
13424         this.proxy.hide();
13425         this.fireEvent("resize", this, size.width, size.height, e);
13426     },
13427
13428     // private
13429     updateChildSize : function(){
13430         if(this.resizeChild){
13431             var el = this.el;
13432             var child = this.resizeChild;
13433             var adj = this.adjustments;
13434             if(el.dom.offsetWidth){
13435                 var b = el.getSize(true);
13436                 child.setSize(b.width+adj[0], b.height+adj[1]);
13437             }
13438             // Second call here for IE
13439             // The first call enables instant resizing and
13440             // the second call corrects scroll bars if they
13441             // exist
13442             if(Roo.isIE){
13443                 setTimeout(function(){
13444                     if(el.dom.offsetWidth){
13445                         var b = el.getSize(true);
13446                         child.setSize(b.width+adj[0], b.height+adj[1]);
13447                     }
13448                 }, 10);
13449             }
13450         }
13451     },
13452
13453     // private
13454     snap : function(value, inc, min){
13455         if(!inc || !value) return value;
13456         var newValue = value;
13457         var m = value % inc;
13458         if(m > 0){
13459             if(m > (inc/2)){
13460                 newValue = value + (inc-m);
13461             }else{
13462                 newValue = value - m;
13463             }
13464         }
13465         return Math.max(min, newValue);
13466     },
13467
13468     // private
13469     resizeElement : function(){
13470         var box = this.proxy.getBox();
13471         if(this.updateBox){
13472             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13473         }else{
13474             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13475         }
13476         this.updateChildSize();
13477         if(!this.dynamic){
13478             this.proxy.hide();
13479         }
13480         return box;
13481     },
13482
13483     // private
13484     constrain : function(v, diff, m, mx){
13485         if(v - diff < m){
13486             diff = v - m;
13487         }else if(v - diff > mx){
13488             diff = mx - v;
13489         }
13490         return diff;
13491     },
13492
13493     // private
13494     onMouseMove : function(e){
13495         if(this.enabled){
13496             try{// try catch so if something goes wrong the user doesn't get hung
13497
13498             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13499                 return;
13500             }
13501
13502             //var curXY = this.startPoint;
13503             var curSize = this.curSize || this.startBox;
13504             var x = this.startBox.x, y = this.startBox.y;
13505             var ox = x, oy = y;
13506             var w = curSize.width, h = curSize.height;
13507             var ow = w, oh = h;
13508             var mw = this.minWidth, mh = this.minHeight;
13509             var mxw = this.maxWidth, mxh = this.maxHeight;
13510             var wi = this.widthIncrement;
13511             var hi = this.heightIncrement;
13512
13513             var eventXY = e.getXY();
13514             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13515             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13516
13517             var pos = this.activeHandle.position;
13518
13519             switch(pos){
13520                 case "east":
13521                     w += diffX;
13522                     w = Math.min(Math.max(mw, w), mxw);
13523                     break;
13524              
13525                 case "south":
13526                     h += diffY;
13527                     h = Math.min(Math.max(mh, h), mxh);
13528                     break;
13529                 case "southeast":
13530                     w += diffX;
13531                     h += diffY;
13532                     w = Math.min(Math.max(mw, w), mxw);
13533                     h = Math.min(Math.max(mh, h), mxh);
13534                     break;
13535                 case "north":
13536                     diffY = this.constrain(h, diffY, mh, mxh);
13537                     y += diffY;
13538                     h -= diffY;
13539                     break;
13540                 case "hdrag":
13541                     
13542                     if (wi) {
13543                         var adiffX = Math.abs(diffX);
13544                         var sub = (adiffX % wi); // how much 
13545                         if (sub > (wi/2)) { // far enough to snap
13546                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13547                         } else {
13548                             // remove difference.. 
13549                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13550                         }
13551                     }
13552                     x += diffX;
13553                     x = Math.max(this.minX, x);
13554                     break;
13555                 case "west":
13556                     diffX = this.constrain(w, diffX, mw, mxw);
13557                     x += diffX;
13558                     w -= diffX;
13559                     break;
13560                 case "northeast":
13561                     w += diffX;
13562                     w = Math.min(Math.max(mw, w), mxw);
13563                     diffY = this.constrain(h, diffY, mh, mxh);
13564                     y += diffY;
13565                     h -= diffY;
13566                     break;
13567                 case "northwest":
13568                     diffX = this.constrain(w, diffX, mw, mxw);
13569                     diffY = this.constrain(h, diffY, mh, mxh);
13570                     y += diffY;
13571                     h -= diffY;
13572                     x += diffX;
13573                     w -= diffX;
13574                     break;
13575                case "southwest":
13576                     diffX = this.constrain(w, diffX, mw, mxw);
13577                     h += diffY;
13578                     h = Math.min(Math.max(mh, h), mxh);
13579                     x += diffX;
13580                     w -= diffX;
13581                     break;
13582             }
13583
13584             var sw = this.snap(w, wi, mw);
13585             var sh = this.snap(h, hi, mh);
13586             if(sw != w || sh != h){
13587                 switch(pos){
13588                     case "northeast":
13589                         y -= sh - h;
13590                     break;
13591                     case "north":
13592                         y -= sh - h;
13593                         break;
13594                     case "southwest":
13595                         x -= sw - w;
13596                     break;
13597                     case "west":
13598                         x -= sw - w;
13599                         break;
13600                     case "northwest":
13601                         x -= sw - w;
13602                         y -= sh - h;
13603                     break;
13604                 }
13605                 w = sw;
13606                 h = sh;
13607             }
13608
13609             if(this.preserveRatio){
13610                 switch(pos){
13611                     case "southeast":
13612                     case "east":
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         w = ow * (h/oh);
13616                        break;
13617                     case "south":
13618                         w = ow * (h/oh);
13619                         w = Math.min(Math.max(mw, w), mxw);
13620                         h = oh * (w/ow);
13621                         break;
13622                     case "northeast":
13623                         w = ow * (h/oh);
13624                         w = Math.min(Math.max(mw, w), mxw);
13625                         h = oh * (w/ow);
13626                     break;
13627                     case "north":
13628                         var tw = w;
13629                         w = ow * (h/oh);
13630                         w = Math.min(Math.max(mw, w), mxw);
13631                         h = oh * (w/ow);
13632                         x += (tw - w) / 2;
13633                         break;
13634                     case "southwest":
13635                         h = oh * (w/ow);
13636                         h = Math.min(Math.max(mh, h), mxh);
13637                         var tw = w;
13638                         w = ow * (h/oh);
13639                         x += tw - w;
13640                         break;
13641                     case "west":
13642                         var th = h;
13643                         h = oh * (w/ow);
13644                         h = Math.min(Math.max(mh, h), mxh);
13645                         y += (th - h) / 2;
13646                         var tw = w;
13647                         w = ow * (h/oh);
13648                         x += tw - w;
13649                        break;
13650                     case "northwest":
13651                         var tw = w;
13652                         var th = h;
13653                         h = oh * (w/ow);
13654                         h = Math.min(Math.max(mh, h), mxh);
13655                         w = ow * (h/oh);
13656                         y += th - h;
13657                         x += tw - w;
13658                        break;
13659
13660                 }
13661             }
13662             if (pos == 'hdrag') {
13663                 w = ow;
13664             }
13665             this.proxy.setBounds(x, y, w, h);
13666             if(this.dynamic){
13667                 this.resizeElement();
13668             }
13669             }catch(e){}
13670         }
13671     },
13672
13673     // private
13674     handleOver : function(){
13675         if(this.enabled){
13676             this.el.addClass("x-resizable-over");
13677         }
13678     },
13679
13680     // private
13681     handleOut : function(){
13682         if(!this.resizing){
13683             this.el.removeClass("x-resizable-over");
13684         }
13685     },
13686
13687     /**
13688      * Returns the element this component is bound to.
13689      * @return {Roo.Element}
13690      */
13691     getEl : function(){
13692         return this.el;
13693     },
13694
13695     /**
13696      * Returns the resizeChild element (or null).
13697      * @return {Roo.Element}
13698      */
13699     getResizeChild : function(){
13700         return this.resizeChild;
13701     },
13702
13703     /**
13704      * Destroys this resizable. If the element was wrapped and
13705      * removeEl is not true then the element remains.
13706      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13707      */
13708     destroy : function(removeEl){
13709         this.proxy.remove();
13710         if(this.overlay){
13711             this.overlay.removeAllListeners();
13712             this.overlay.remove();
13713         }
13714         var ps = Roo.Resizable.positions;
13715         for(var k in ps){
13716             if(typeof ps[k] != "function" && this[ps[k]]){
13717                 var h = this[ps[k]];
13718                 h.el.removeAllListeners();
13719                 h.el.remove();
13720             }
13721         }
13722         if(removeEl){
13723             this.el.update("");
13724             this.el.remove();
13725         }
13726     }
13727 });
13728
13729 // private
13730 // hash to map config positions to true positions
13731 Roo.Resizable.positions = {
13732     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13733     hd: "hdrag"
13734 };
13735
13736 // private
13737 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13738     if(!this.tpl){
13739         // only initialize the template if resizable is used
13740         var tpl = Roo.DomHelper.createTemplate(
13741             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13742         );
13743         tpl.compile();
13744         Roo.Resizable.Handle.prototype.tpl = tpl;
13745     }
13746     this.position = pos;
13747     this.rz = rz;
13748     // show north drag fro topdra
13749     var handlepos = pos == 'hdrag' ? 'north' : pos;
13750     
13751     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13752     if (pos == 'hdrag') {
13753         this.el.setStyle('cursor', 'pointer');
13754     }
13755     this.el.unselectable();
13756     if(transparent){
13757         this.el.setOpacity(0);
13758     }
13759     this.el.on("mousedown", this.onMouseDown, this);
13760     if(!disableTrackOver){
13761         this.el.on("mouseover", this.onMouseOver, this);
13762         this.el.on("mouseout", this.onMouseOut, this);
13763     }
13764 };
13765
13766 // private
13767 Roo.Resizable.Handle.prototype = {
13768     afterResize : function(rz){
13769         // do nothing
13770     },
13771     // private
13772     onMouseDown : function(e){
13773         this.rz.onMouseDown(this, e);
13774     },
13775     // private
13776     onMouseOver : function(e){
13777         this.rz.handleOver(this, e);
13778     },
13779     // private
13780     onMouseOut : function(e){
13781         this.rz.handleOut(this, e);
13782     }
13783 };/*
13784  * Based on:
13785  * Ext JS Library 1.1.1
13786  * Copyright(c) 2006-2007, Ext JS, LLC.
13787  *
13788  * Originally Released Under LGPL - original licence link has changed is not relivant.
13789  *
13790  * Fork - LGPL
13791  * <script type="text/javascript">
13792  */
13793
13794 /**
13795  * @class Roo.Editor
13796  * @extends Roo.Component
13797  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13798  * @constructor
13799  * Create a new Editor
13800  * @param {Roo.form.Field} field The Field object (or descendant)
13801  * @param {Object} config The config object
13802  */
13803 Roo.Editor = function(field, config){
13804     Roo.Editor.superclass.constructor.call(this, config);
13805     this.field = field;
13806     this.addEvents({
13807         /**
13808              * @event beforestartedit
13809              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13810              * false from the handler of this event.
13811              * @param {Editor} this
13812              * @param {Roo.Element} boundEl The underlying element bound to this editor
13813              * @param {Mixed} value The field value being set
13814              */
13815         "beforestartedit" : true,
13816         /**
13817              * @event startedit
13818              * Fires when this editor is displayed
13819              * @param {Roo.Element} boundEl The underlying element bound to this editor
13820              * @param {Mixed} value The starting field value
13821              */
13822         "startedit" : true,
13823         /**
13824              * @event beforecomplete
13825              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13826              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13827              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13828              * event will not fire since no edit actually occurred.
13829              * @param {Editor} this
13830              * @param {Mixed} value The current field value
13831              * @param {Mixed} startValue The original field value
13832              */
13833         "beforecomplete" : true,
13834         /**
13835              * @event complete
13836              * Fires after editing is complete and any changed value has been written to the underlying field.
13837              * @param {Editor} this
13838              * @param {Mixed} value The current field value
13839              * @param {Mixed} startValue The original field value
13840              */
13841         "complete" : true,
13842         /**
13843          * @event specialkey
13844          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13845          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13846          * @param {Roo.form.Field} this
13847          * @param {Roo.EventObject} e The event object
13848          */
13849         "specialkey" : true
13850     });
13851 };
13852
13853 Roo.extend(Roo.Editor, Roo.Component, {
13854     /**
13855      * @cfg {Boolean/String} autosize
13856      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13857      * or "height" to adopt the height only (defaults to false)
13858      */
13859     /**
13860      * @cfg {Boolean} revertInvalid
13861      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13862      * validation fails (defaults to true)
13863      */
13864     /**
13865      * @cfg {Boolean} ignoreNoChange
13866      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13867      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13868      * will never be ignored.
13869      */
13870     /**
13871      * @cfg {Boolean} hideEl
13872      * False to keep the bound element visible while the editor is displayed (defaults to true)
13873      */
13874     /**
13875      * @cfg {Mixed} value
13876      * The data value of the underlying field (defaults to "")
13877      */
13878     value : "",
13879     /**
13880      * @cfg {String} alignment
13881      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13882      */
13883     alignment: "c-c?",
13884     /**
13885      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13886      * for bottom-right shadow (defaults to "frame")
13887      */
13888     shadow : "frame",
13889     /**
13890      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13891      */
13892     constrain : false,
13893     /**
13894      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13895      */
13896     completeOnEnter : false,
13897     /**
13898      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13899      */
13900     cancelOnEsc : false,
13901     /**
13902      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13903      */
13904     updateEl : false,
13905
13906     // private
13907     onRender : function(ct, position){
13908         this.el = new Roo.Layer({
13909             shadow: this.shadow,
13910             cls: "x-editor",
13911             parentEl : ct,
13912             shim : this.shim,
13913             shadowOffset:4,
13914             id: this.id,
13915             constrain: this.constrain
13916         });
13917         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13918         if(this.field.msgTarget != 'title'){
13919             this.field.msgTarget = 'qtip';
13920         }
13921         this.field.render(this.el);
13922         if(Roo.isGecko){
13923             this.field.el.dom.setAttribute('autocomplete', 'off');
13924         }
13925         this.field.on("specialkey", this.onSpecialKey, this);
13926         if(this.swallowKeys){
13927             this.field.el.swallowEvent(['keydown','keypress']);
13928         }
13929         this.field.show();
13930         this.field.on("blur", this.onBlur, this);
13931         if(this.field.grow){
13932             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13933         }
13934     },
13935
13936     onSpecialKey : function(field, e)
13937     {
13938         //Roo.log('editor onSpecialKey');
13939         if(this.completeOnEnter && e.getKey() == e.ENTER){
13940             e.stopEvent();
13941             this.completeEdit();
13942             return;
13943         }
13944         // do not fire special key otherwise it might hide close the editor...
13945         if(e.getKey() == e.ENTER){    
13946             return;
13947         }
13948         if(this.cancelOnEsc && e.getKey() == e.ESC){
13949             this.cancelEdit();
13950             return;
13951         } 
13952         this.fireEvent('specialkey', field, e);
13953     
13954     },
13955
13956     /**
13957      * Starts the editing process and shows the editor.
13958      * @param {String/HTMLElement/Element} el The element to edit
13959      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13960       * to the innerHTML of el.
13961      */
13962     startEdit : function(el, value){
13963         if(this.editing){
13964             this.completeEdit();
13965         }
13966         this.boundEl = Roo.get(el);
13967         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13968         if(!this.rendered){
13969             this.render(this.parentEl || document.body);
13970         }
13971         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13972             return;
13973         }
13974         this.startValue = v;
13975         this.field.setValue(v);
13976         if(this.autoSize){
13977             var sz = this.boundEl.getSize();
13978             switch(this.autoSize){
13979                 case "width":
13980                 this.setSize(sz.width,  "");
13981                 break;
13982                 case "height":
13983                 this.setSize("",  sz.height);
13984                 break;
13985                 default:
13986                 this.setSize(sz.width,  sz.height);
13987             }
13988         }
13989         this.el.alignTo(this.boundEl, this.alignment);
13990         this.editing = true;
13991         if(Roo.QuickTips){
13992             Roo.QuickTips.disable();
13993         }
13994         this.show();
13995     },
13996
13997     /**
13998      * Sets the height and width of this editor.
13999      * @param {Number} width The new width
14000      * @param {Number} height The new height
14001      */
14002     setSize : function(w, h){
14003         this.field.setSize(w, h);
14004         if(this.el){
14005             this.el.sync();
14006         }
14007     },
14008
14009     /**
14010      * Realigns the editor to the bound field based on the current alignment config value.
14011      */
14012     realign : function(){
14013         this.el.alignTo(this.boundEl, this.alignment);
14014     },
14015
14016     /**
14017      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14018      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14019      */
14020     completeEdit : function(remainVisible){
14021         if(!this.editing){
14022             return;
14023         }
14024         var v = this.getValue();
14025         if(this.revertInvalid !== false && !this.field.isValid()){
14026             v = this.startValue;
14027             this.cancelEdit(true);
14028         }
14029         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14030             this.editing = false;
14031             this.hide();
14032             return;
14033         }
14034         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14035             this.editing = false;
14036             if(this.updateEl && this.boundEl){
14037                 this.boundEl.update(v);
14038             }
14039             if(remainVisible !== true){
14040                 this.hide();
14041             }
14042             this.fireEvent("complete", this, v, this.startValue);
14043         }
14044     },
14045
14046     // private
14047     onShow : function(){
14048         this.el.show();
14049         if(this.hideEl !== false){
14050             this.boundEl.hide();
14051         }
14052         this.field.show();
14053         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14054             this.fixIEFocus = true;
14055             this.deferredFocus.defer(50, this);
14056         }else{
14057             this.field.focus();
14058         }
14059         this.fireEvent("startedit", this.boundEl, this.startValue);
14060     },
14061
14062     deferredFocus : function(){
14063         if(this.editing){
14064             this.field.focus();
14065         }
14066     },
14067
14068     /**
14069      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14070      * reverted to the original starting value.
14071      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14072      * cancel (defaults to false)
14073      */
14074     cancelEdit : function(remainVisible){
14075         if(this.editing){
14076             this.setValue(this.startValue);
14077             if(remainVisible !== true){
14078                 this.hide();
14079             }
14080         }
14081     },
14082
14083     // private
14084     onBlur : function(){
14085         if(this.allowBlur !== true && this.editing){
14086             this.completeEdit();
14087         }
14088     },
14089
14090     // private
14091     onHide : function(){
14092         if(this.editing){
14093             this.completeEdit();
14094             return;
14095         }
14096         this.field.blur();
14097         if(this.field.collapse){
14098             this.field.collapse();
14099         }
14100         this.el.hide();
14101         if(this.hideEl !== false){
14102             this.boundEl.show();
14103         }
14104         if(Roo.QuickTips){
14105             Roo.QuickTips.enable();
14106         }
14107     },
14108
14109     /**
14110      * Sets the data value of the editor
14111      * @param {Mixed} value Any valid value supported by the underlying field
14112      */
14113     setValue : function(v){
14114         this.field.setValue(v);
14115     },
14116
14117     /**
14118      * Gets the data value of the editor
14119      * @return {Mixed} The data value
14120      */
14121     getValue : function(){
14122         return this.field.getValue();
14123     }
14124 });/*
14125  * Based on:
14126  * Ext JS Library 1.1.1
14127  * Copyright(c) 2006-2007, Ext JS, LLC.
14128  *
14129  * Originally Released Under LGPL - original licence link has changed is not relivant.
14130  *
14131  * Fork - LGPL
14132  * <script type="text/javascript">
14133  */
14134  
14135 /**
14136  * @class Roo.BasicDialog
14137  * @extends Roo.util.Observable
14138  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14139  * <pre><code>
14140 var dlg = new Roo.BasicDialog("my-dlg", {
14141     height: 200,
14142     width: 300,
14143     minHeight: 100,
14144     minWidth: 150,
14145     modal: true,
14146     proxyDrag: true,
14147     shadow: true
14148 });
14149 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14150 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14151 dlg.addButton('Cancel', dlg.hide, dlg);
14152 dlg.show();
14153 </code></pre>
14154   <b>A Dialog should always be a direct child of the body element.</b>
14155  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14156  * @cfg {String} title Default text to display in the title bar (defaults to null)
14157  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14158  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14159  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14160  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14161  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14162  * (defaults to null with no animation)
14163  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14164  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14165  * property for valid values (defaults to 'all')
14166  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14167  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14168  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14169  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14170  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14171  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14172  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14173  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14174  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14175  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14176  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14177  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14178  * draggable = true (defaults to false)
14179  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14180  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14181  * shadow (defaults to false)
14182  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14183  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14184  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14185  * @cfg {Array} buttons Array of buttons
14186  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14187  * @constructor
14188  * Create a new BasicDialog.
14189  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14190  * @param {Object} config Configuration options
14191  */
14192 Roo.BasicDialog = function(el, config){
14193     this.el = Roo.get(el);
14194     var dh = Roo.DomHelper;
14195     if(!this.el && config && config.autoCreate){
14196         if(typeof config.autoCreate == "object"){
14197             if(!config.autoCreate.id){
14198                 config.autoCreate.id = el;
14199             }
14200             this.el = dh.append(document.body,
14201                         config.autoCreate, true);
14202         }else{
14203             this.el = dh.append(document.body,
14204                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14205         }
14206     }
14207     el = this.el;
14208     el.setDisplayed(true);
14209     el.hide = this.hideAction;
14210     this.id = el.id;
14211     el.addClass("x-dlg");
14212
14213     Roo.apply(this, config);
14214
14215     this.proxy = el.createProxy("x-dlg-proxy");
14216     this.proxy.hide = this.hideAction;
14217     this.proxy.setOpacity(.5);
14218     this.proxy.hide();
14219
14220     if(config.width){
14221         el.setWidth(config.width);
14222     }
14223     if(config.height){
14224         el.setHeight(config.height);
14225     }
14226     this.size = el.getSize();
14227     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14228         this.xy = [config.x,config.y];
14229     }else{
14230         this.xy = el.getCenterXY(true);
14231     }
14232     /** The header element @type Roo.Element */
14233     this.header = el.child("> .x-dlg-hd");
14234     /** The body element @type Roo.Element */
14235     this.body = el.child("> .x-dlg-bd");
14236     /** The footer element @type Roo.Element */
14237     this.footer = el.child("> .x-dlg-ft");
14238
14239     if(!this.header){
14240         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14241     }
14242     if(!this.body){
14243         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14244     }
14245
14246     this.header.unselectable();
14247     if(this.title){
14248         this.header.update(this.title);
14249     }
14250     // this element allows the dialog to be focused for keyboard event
14251     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14252     this.focusEl.swallowEvent("click", true);
14253
14254     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14255
14256     // wrap the body and footer for special rendering
14257     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14258     if(this.footer){
14259         this.bwrap.dom.appendChild(this.footer.dom);
14260     }
14261
14262     this.bg = this.el.createChild({
14263         tag: "div", cls:"x-dlg-bg",
14264         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14265     });
14266     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14267
14268
14269     if(this.autoScroll !== false && !this.autoTabs){
14270         this.body.setStyle("overflow", "auto");
14271     }
14272
14273     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14274
14275     if(this.closable !== false){
14276         this.el.addClass("x-dlg-closable");
14277         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14278         this.close.on("click", this.closeClick, this);
14279         this.close.addClassOnOver("x-dlg-close-over");
14280     }
14281     if(this.collapsible !== false){
14282         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14283         this.collapseBtn.on("click", this.collapseClick, this);
14284         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14285         this.header.on("dblclick", this.collapseClick, this);
14286     }
14287     if(this.resizable !== false){
14288         this.el.addClass("x-dlg-resizable");
14289         this.resizer = new Roo.Resizable(el, {
14290             minWidth: this.minWidth || 80,
14291             minHeight:this.minHeight || 80,
14292             handles: this.resizeHandles || "all",
14293             pinned: true
14294         });
14295         this.resizer.on("beforeresize", this.beforeResize, this);
14296         this.resizer.on("resize", this.onResize, this);
14297     }
14298     if(this.draggable !== false){
14299         el.addClass("x-dlg-draggable");
14300         if (!this.proxyDrag) {
14301             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14302         }
14303         else {
14304             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14305         }
14306         dd.setHandleElId(this.header.id);
14307         dd.endDrag = this.endMove.createDelegate(this);
14308         dd.startDrag = this.startMove.createDelegate(this);
14309         dd.onDrag = this.onDrag.createDelegate(this);
14310         dd.scroll = false;
14311         this.dd = dd;
14312     }
14313     if(this.modal){
14314         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14315         this.mask.enableDisplayMode("block");
14316         this.mask.hide();
14317         this.el.addClass("x-dlg-modal");
14318     }
14319     if(this.shadow){
14320         this.shadow = new Roo.Shadow({
14321             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14322             offset : this.shadowOffset
14323         });
14324     }else{
14325         this.shadowOffset = 0;
14326     }
14327     if(Roo.useShims && this.shim !== false){
14328         this.shim = this.el.createShim();
14329         this.shim.hide = this.hideAction;
14330         this.shim.hide();
14331     }else{
14332         this.shim = false;
14333     }
14334     if(this.autoTabs){
14335         this.initTabs();
14336     }
14337     if (this.buttons) { 
14338         var bts= this.buttons;
14339         this.buttons = [];
14340         Roo.each(bts, function(b) {
14341             this.addButton(b);
14342         }, this);
14343     }
14344     
14345     
14346     this.addEvents({
14347         /**
14348          * @event keydown
14349          * Fires when a key is pressed
14350          * @param {Roo.BasicDialog} this
14351          * @param {Roo.EventObject} e
14352          */
14353         "keydown" : true,
14354         /**
14355          * @event move
14356          * Fires when this dialog is moved by the user.
14357          * @param {Roo.BasicDialog} this
14358          * @param {Number} x The new page X
14359          * @param {Number} y The new page Y
14360          */
14361         "move" : true,
14362         /**
14363          * @event resize
14364          * Fires when this dialog is resized by the user.
14365          * @param {Roo.BasicDialog} this
14366          * @param {Number} width The new width
14367          * @param {Number} height The new height
14368          */
14369         "resize" : true,
14370         /**
14371          * @event beforehide
14372          * Fires before this dialog is hidden.
14373          * @param {Roo.BasicDialog} this
14374          */
14375         "beforehide" : true,
14376         /**
14377          * @event hide
14378          * Fires when this dialog is hidden.
14379          * @param {Roo.BasicDialog} this
14380          */
14381         "hide" : true,
14382         /**
14383          * @event beforeshow
14384          * Fires before this dialog is shown.
14385          * @param {Roo.BasicDialog} this
14386          */
14387         "beforeshow" : true,
14388         /**
14389          * @event show
14390          * Fires when this dialog is shown.
14391          * @param {Roo.BasicDialog} this
14392          */
14393         "show" : true
14394     });
14395     el.on("keydown", this.onKeyDown, this);
14396     el.on("mousedown", this.toFront, this);
14397     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14398     this.el.hide();
14399     Roo.DialogManager.register(this);
14400     Roo.BasicDialog.superclass.constructor.call(this);
14401 };
14402
14403 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14404     shadowOffset: Roo.isIE ? 6 : 5,
14405     minHeight: 80,
14406     minWidth: 200,
14407     minButtonWidth: 75,
14408     defaultButton: null,
14409     buttonAlign: "right",
14410     tabTag: 'div',
14411     firstShow: true,
14412
14413     /**
14414      * Sets the dialog title text
14415      * @param {String} text The title text to display
14416      * @return {Roo.BasicDialog} this
14417      */
14418     setTitle : function(text){
14419         this.header.update(text);
14420         return this;
14421     },
14422
14423     // private
14424     closeClick : function(){
14425         this.hide();
14426     },
14427
14428     // private
14429     collapseClick : function(){
14430         this[this.collapsed ? "expand" : "collapse"]();
14431     },
14432
14433     /**
14434      * Collapses the dialog to its minimized state (only the title bar is visible).
14435      * Equivalent to the user clicking the collapse dialog button.
14436      */
14437     collapse : function(){
14438         if(!this.collapsed){
14439             this.collapsed = true;
14440             this.el.addClass("x-dlg-collapsed");
14441             this.restoreHeight = this.el.getHeight();
14442             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14443         }
14444     },
14445
14446     /**
14447      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14448      * clicking the expand dialog button.
14449      */
14450     expand : function(){
14451         if(this.collapsed){
14452             this.collapsed = false;
14453             this.el.removeClass("x-dlg-collapsed");
14454             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14455         }
14456     },
14457
14458     /**
14459      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14460      * @return {Roo.TabPanel} The tabs component
14461      */
14462     initTabs : function(){
14463         var tabs = this.getTabs();
14464         while(tabs.getTab(0)){
14465             tabs.removeTab(0);
14466         }
14467         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14468             var dom = el.dom;
14469             tabs.addTab(Roo.id(dom), dom.title);
14470             dom.title = "";
14471         });
14472         tabs.activate(0);
14473         return tabs;
14474     },
14475
14476     // private
14477     beforeResize : function(){
14478         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14479     },
14480
14481     // private
14482     onResize : function(){
14483         this.refreshSize();
14484         this.syncBodyHeight();
14485         this.adjustAssets();
14486         this.focus();
14487         this.fireEvent("resize", this, this.size.width, this.size.height);
14488     },
14489
14490     // private
14491     onKeyDown : function(e){
14492         if(this.isVisible()){
14493             this.fireEvent("keydown", this, e);
14494         }
14495     },
14496
14497     /**
14498      * Resizes the dialog.
14499      * @param {Number} width
14500      * @param {Number} height
14501      * @return {Roo.BasicDialog} this
14502      */
14503     resizeTo : function(width, height){
14504         this.el.setSize(width, height);
14505         this.size = {width: width, height: height};
14506         this.syncBodyHeight();
14507         if(this.fixedcenter){
14508             this.center();
14509         }
14510         if(this.isVisible()){
14511             this.constrainXY();
14512             this.adjustAssets();
14513         }
14514         this.fireEvent("resize", this, width, height);
14515         return this;
14516     },
14517
14518
14519     /**
14520      * Resizes the dialog to fit the specified content size.
14521      * @param {Number} width
14522      * @param {Number} height
14523      * @return {Roo.BasicDialog} this
14524      */
14525     setContentSize : function(w, h){
14526         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14527         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14528         //if(!this.el.isBorderBox()){
14529             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14530             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14531         //}
14532         if(this.tabs){
14533             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14534             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14535         }
14536         this.resizeTo(w, h);
14537         return this;
14538     },
14539
14540     /**
14541      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14542      * executed in response to a particular key being pressed while the dialog is active.
14543      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14544      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14545      * @param {Function} fn The function to call
14546      * @param {Object} scope (optional) The scope of the function
14547      * @return {Roo.BasicDialog} this
14548      */
14549     addKeyListener : function(key, fn, scope){
14550         var keyCode, shift, ctrl, alt;
14551         if(typeof key == "object" && !(key instanceof Array)){
14552             keyCode = key["key"];
14553             shift = key["shift"];
14554             ctrl = key["ctrl"];
14555             alt = key["alt"];
14556         }else{
14557             keyCode = key;
14558         }
14559         var handler = function(dlg, e){
14560             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14561                 var k = e.getKey();
14562                 if(keyCode instanceof Array){
14563                     for(var i = 0, len = keyCode.length; i < len; i++){
14564                         if(keyCode[i] == k){
14565                           fn.call(scope || window, dlg, k, e);
14566                           return;
14567                         }
14568                     }
14569                 }else{
14570                     if(k == keyCode){
14571                         fn.call(scope || window, dlg, k, e);
14572                     }
14573                 }
14574             }
14575         };
14576         this.on("keydown", handler);
14577         return this;
14578     },
14579
14580     /**
14581      * Returns the TabPanel component (creates it if it doesn't exist).
14582      * Note: If you wish to simply check for the existence of tabs without creating them,
14583      * check for a null 'tabs' property.
14584      * @return {Roo.TabPanel} The tabs component
14585      */
14586     getTabs : function(){
14587         if(!this.tabs){
14588             this.el.addClass("x-dlg-auto-tabs");
14589             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14590             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14591         }
14592         return this.tabs;
14593     },
14594
14595     /**
14596      * Adds a button to the footer section of the dialog.
14597      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14598      * object or a valid Roo.DomHelper element config
14599      * @param {Function} handler The function called when the button is clicked
14600      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14601      * @return {Roo.Button} The new button
14602      */
14603     addButton : function(config, handler, scope){
14604         var dh = Roo.DomHelper;
14605         if(!this.footer){
14606             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14607         }
14608         if(!this.btnContainer){
14609             var tb = this.footer.createChild({
14610
14611                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14612                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14613             }, null, true);
14614             this.btnContainer = tb.firstChild.firstChild.firstChild;
14615         }
14616         var bconfig = {
14617             handler: handler,
14618             scope: scope,
14619             minWidth: this.minButtonWidth,
14620             hideParent:true
14621         };
14622         if(typeof config == "string"){
14623             bconfig.text = config;
14624         }else{
14625             if(config.tag){
14626                 bconfig.dhconfig = config;
14627             }else{
14628                 Roo.apply(bconfig, config);
14629             }
14630         }
14631         var fc = false;
14632         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14633             bconfig.position = Math.max(0, bconfig.position);
14634             fc = this.btnContainer.childNodes[bconfig.position];
14635         }
14636          
14637         var btn = new Roo.Button(
14638             fc ? 
14639                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14640                 : this.btnContainer.appendChild(document.createElement("td")),
14641             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14642             bconfig
14643         );
14644         this.syncBodyHeight();
14645         if(!this.buttons){
14646             /**
14647              * Array of all the buttons that have been added to this dialog via addButton
14648              * @type Array
14649              */
14650             this.buttons = [];
14651         }
14652         this.buttons.push(btn);
14653         return btn;
14654     },
14655
14656     /**
14657      * Sets the default button to be focused when the dialog is displayed.
14658      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14659      * @return {Roo.BasicDialog} this
14660      */
14661     setDefaultButton : function(btn){
14662         this.defaultButton = btn;
14663         return this;
14664     },
14665
14666     // private
14667     getHeaderFooterHeight : function(safe){
14668         var height = 0;
14669         if(this.header){
14670            height += this.header.getHeight();
14671         }
14672         if(this.footer){
14673            var fm = this.footer.getMargins();
14674             height += (this.footer.getHeight()+fm.top+fm.bottom);
14675         }
14676         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14677         height += this.centerBg.getPadding("tb");
14678         return height;
14679     },
14680
14681     // private
14682     syncBodyHeight : function(){
14683         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14684         var height = this.size.height - this.getHeaderFooterHeight(false);
14685         bd.setHeight(height-bd.getMargins("tb"));
14686         var hh = this.header.getHeight();
14687         var h = this.size.height-hh;
14688         cb.setHeight(h);
14689         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14690         bw.setHeight(h-cb.getPadding("tb"));
14691         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14692         bd.setWidth(bw.getWidth(true));
14693         if(this.tabs){
14694             this.tabs.syncHeight();
14695             if(Roo.isIE){
14696                 this.tabs.el.repaint();
14697             }
14698         }
14699     },
14700
14701     /**
14702      * Restores the previous state of the dialog if Roo.state is configured.
14703      * @return {Roo.BasicDialog} this
14704      */
14705     restoreState : function(){
14706         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14707         if(box && box.width){
14708             this.xy = [box.x, box.y];
14709             this.resizeTo(box.width, box.height);
14710         }
14711         return this;
14712     },
14713
14714     // private
14715     beforeShow : function(){
14716         this.expand();
14717         if(this.fixedcenter){
14718             this.xy = this.el.getCenterXY(true);
14719         }
14720         if(this.modal){
14721             Roo.get(document.body).addClass("x-body-masked");
14722             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14723             this.mask.show();
14724         }
14725         this.constrainXY();
14726     },
14727
14728     // private
14729     animShow : function(){
14730         var b = Roo.get(this.animateTarget).getBox();
14731         this.proxy.setSize(b.width, b.height);
14732         this.proxy.setLocation(b.x, b.y);
14733         this.proxy.show();
14734         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14735                     true, .35, this.showEl.createDelegate(this));
14736     },
14737
14738     /**
14739      * Shows the dialog.
14740      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14741      * @return {Roo.BasicDialog} this
14742      */
14743     show : function(animateTarget){
14744         if (this.fireEvent("beforeshow", this) === false){
14745             return;
14746         }
14747         if(this.syncHeightBeforeShow){
14748             this.syncBodyHeight();
14749         }else if(this.firstShow){
14750             this.firstShow = false;
14751             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14752         }
14753         this.animateTarget = animateTarget || this.animateTarget;
14754         if(!this.el.isVisible()){
14755             this.beforeShow();
14756             if(this.animateTarget && Roo.get(this.animateTarget)){
14757                 this.animShow();
14758             }else{
14759                 this.showEl();
14760             }
14761         }
14762         return this;
14763     },
14764
14765     // private
14766     showEl : function(){
14767         this.proxy.hide();
14768         this.el.setXY(this.xy);
14769         this.el.show();
14770         this.adjustAssets(true);
14771         this.toFront();
14772         this.focus();
14773         // IE peekaboo bug - fix found by Dave Fenwick
14774         if(Roo.isIE){
14775             this.el.repaint();
14776         }
14777         this.fireEvent("show", this);
14778     },
14779
14780     /**
14781      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14782      * dialog itself will receive focus.
14783      */
14784     focus : function(){
14785         if(this.defaultButton){
14786             this.defaultButton.focus();
14787         }else{
14788             this.focusEl.focus();
14789         }
14790     },
14791
14792     // private
14793     constrainXY : function(){
14794         if(this.constraintoviewport !== false){
14795             if(!this.viewSize){
14796                 if(this.container){
14797                     var s = this.container.getSize();
14798                     this.viewSize = [s.width, s.height];
14799                 }else{
14800                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14801                 }
14802             }
14803             var s = Roo.get(this.container||document).getScroll();
14804
14805             var x = this.xy[0], y = this.xy[1];
14806             var w = this.size.width, h = this.size.height;
14807             var vw = this.viewSize[0], vh = this.viewSize[1];
14808             // only move it if it needs it
14809             var moved = false;
14810             // first validate right/bottom
14811             if(x + w > vw+s.left){
14812                 x = vw - w;
14813                 moved = true;
14814             }
14815             if(y + h > vh+s.top){
14816                 y = vh - h;
14817                 moved = true;
14818             }
14819             // then make sure top/left isn't negative
14820             if(x < s.left){
14821                 x = s.left;
14822                 moved = true;
14823             }
14824             if(y < s.top){
14825                 y = s.top;
14826                 moved = true;
14827             }
14828             if(moved){
14829                 // cache xy
14830                 this.xy = [x, y];
14831                 if(this.isVisible()){
14832                     this.el.setLocation(x, y);
14833                     this.adjustAssets();
14834                 }
14835             }
14836         }
14837     },
14838
14839     // private
14840     onDrag : function(){
14841         if(!this.proxyDrag){
14842             this.xy = this.el.getXY();
14843             this.adjustAssets();
14844         }
14845     },
14846
14847     // private
14848     adjustAssets : function(doShow){
14849         var x = this.xy[0], y = this.xy[1];
14850         var w = this.size.width, h = this.size.height;
14851         if(doShow === true){
14852             if(this.shadow){
14853                 this.shadow.show(this.el);
14854             }
14855             if(this.shim){
14856                 this.shim.show();
14857             }
14858         }
14859         if(this.shadow && this.shadow.isVisible()){
14860             this.shadow.show(this.el);
14861         }
14862         if(this.shim && this.shim.isVisible()){
14863             this.shim.setBounds(x, y, w, h);
14864         }
14865     },
14866
14867     // private
14868     adjustViewport : function(w, h){
14869         if(!w || !h){
14870             w = Roo.lib.Dom.getViewWidth();
14871             h = Roo.lib.Dom.getViewHeight();
14872         }
14873         // cache the size
14874         this.viewSize = [w, h];
14875         if(this.modal && this.mask.isVisible()){
14876             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14877             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14878         }
14879         if(this.isVisible()){
14880             this.constrainXY();
14881         }
14882     },
14883
14884     /**
14885      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14886      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14887      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14888      */
14889     destroy : function(removeEl){
14890         if(this.isVisible()){
14891             this.animateTarget = null;
14892             this.hide();
14893         }
14894         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14895         if(this.tabs){
14896             this.tabs.destroy(removeEl);
14897         }
14898         Roo.destroy(
14899              this.shim,
14900              this.proxy,
14901              this.resizer,
14902              this.close,
14903              this.mask
14904         );
14905         if(this.dd){
14906             this.dd.unreg();
14907         }
14908         if(this.buttons){
14909            for(var i = 0, len = this.buttons.length; i < len; i++){
14910                this.buttons[i].destroy();
14911            }
14912         }
14913         this.el.removeAllListeners();
14914         if(removeEl === true){
14915             this.el.update("");
14916             this.el.remove();
14917         }
14918         Roo.DialogManager.unregister(this);
14919     },
14920
14921     // private
14922     startMove : function(){
14923         if(this.proxyDrag){
14924             this.proxy.show();
14925         }
14926         if(this.constraintoviewport !== false){
14927             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14928         }
14929     },
14930
14931     // private
14932     endMove : function(){
14933         if(!this.proxyDrag){
14934             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14935         }else{
14936             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14937             this.proxy.hide();
14938         }
14939         this.refreshSize();
14940         this.adjustAssets();
14941         this.focus();
14942         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14943     },
14944
14945     /**
14946      * Brings this dialog to the front of any other visible dialogs
14947      * @return {Roo.BasicDialog} this
14948      */
14949     toFront : function(){
14950         Roo.DialogManager.bringToFront(this);
14951         return this;
14952     },
14953
14954     /**
14955      * Sends this dialog to the back (under) of any other visible dialogs
14956      * @return {Roo.BasicDialog} this
14957      */
14958     toBack : function(){
14959         Roo.DialogManager.sendToBack(this);
14960         return this;
14961     },
14962
14963     /**
14964      * Centers this dialog in the viewport
14965      * @return {Roo.BasicDialog} this
14966      */
14967     center : function(){
14968         var xy = this.el.getCenterXY(true);
14969         this.moveTo(xy[0], xy[1]);
14970         return this;
14971     },
14972
14973     /**
14974      * Moves the dialog's top-left corner to the specified point
14975      * @param {Number} x
14976      * @param {Number} y
14977      * @return {Roo.BasicDialog} this
14978      */
14979     moveTo : function(x, y){
14980         this.xy = [x,y];
14981         if(this.isVisible()){
14982             this.el.setXY(this.xy);
14983             this.adjustAssets();
14984         }
14985         return this;
14986     },
14987
14988     /**
14989      * Aligns the dialog to the specified element
14990      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14991      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14992      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14993      * @return {Roo.BasicDialog} this
14994      */
14995     alignTo : function(element, position, offsets){
14996         this.xy = this.el.getAlignToXY(element, position, offsets);
14997         if(this.isVisible()){
14998             this.el.setXY(this.xy);
14999             this.adjustAssets();
15000         }
15001         return this;
15002     },
15003
15004     /**
15005      * Anchors an element to another element and realigns it when the window is resized.
15006      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15007      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15008      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15009      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15010      * is a number, it is used as the buffer delay (defaults to 50ms).
15011      * @return {Roo.BasicDialog} this
15012      */
15013     anchorTo : function(el, alignment, offsets, monitorScroll){
15014         var action = function(){
15015             this.alignTo(el, alignment, offsets);
15016         };
15017         Roo.EventManager.onWindowResize(action, this);
15018         var tm = typeof monitorScroll;
15019         if(tm != 'undefined'){
15020             Roo.EventManager.on(window, 'scroll', action, this,
15021                 {buffer: tm == 'number' ? monitorScroll : 50});
15022         }
15023         action.call(this);
15024         return this;
15025     },
15026
15027     /**
15028      * Returns true if the dialog is visible
15029      * @return {Boolean}
15030      */
15031     isVisible : function(){
15032         return this.el.isVisible();
15033     },
15034
15035     // private
15036     animHide : function(callback){
15037         var b = Roo.get(this.animateTarget).getBox();
15038         this.proxy.show();
15039         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15040         this.el.hide();
15041         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15042                     this.hideEl.createDelegate(this, [callback]));
15043     },
15044
15045     /**
15046      * Hides the dialog.
15047      * @param {Function} callback (optional) Function to call when the dialog is hidden
15048      * @return {Roo.BasicDialog} this
15049      */
15050     hide : function(callback){
15051         if (this.fireEvent("beforehide", this) === false){
15052             return;
15053         }
15054         if(this.shadow){
15055             this.shadow.hide();
15056         }
15057         if(this.shim) {
15058           this.shim.hide();
15059         }
15060         // sometimes animateTarget seems to get set.. causing problems...
15061         // this just double checks..
15062         if(this.animateTarget && Roo.get(this.animateTarget)) {
15063            this.animHide(callback);
15064         }else{
15065             this.el.hide();
15066             this.hideEl(callback);
15067         }
15068         return this;
15069     },
15070
15071     // private
15072     hideEl : function(callback){
15073         this.proxy.hide();
15074         if(this.modal){
15075             this.mask.hide();
15076             Roo.get(document.body).removeClass("x-body-masked");
15077         }
15078         this.fireEvent("hide", this);
15079         if(typeof callback == "function"){
15080             callback();
15081         }
15082     },
15083
15084     // private
15085     hideAction : function(){
15086         this.setLeft("-10000px");
15087         this.setTop("-10000px");
15088         this.setStyle("visibility", "hidden");
15089     },
15090
15091     // private
15092     refreshSize : function(){
15093         this.size = this.el.getSize();
15094         this.xy = this.el.getXY();
15095         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15096     },
15097
15098     // private
15099     // z-index is managed by the DialogManager and may be overwritten at any time
15100     setZIndex : function(index){
15101         if(this.modal){
15102             this.mask.setStyle("z-index", index);
15103         }
15104         if(this.shim){
15105             this.shim.setStyle("z-index", ++index);
15106         }
15107         if(this.shadow){
15108             this.shadow.setZIndex(++index);
15109         }
15110         this.el.setStyle("z-index", ++index);
15111         if(this.proxy){
15112             this.proxy.setStyle("z-index", ++index);
15113         }
15114         if(this.resizer){
15115             this.resizer.proxy.setStyle("z-index", ++index);
15116         }
15117
15118         this.lastZIndex = index;
15119     },
15120
15121     /**
15122      * Returns the element for this dialog
15123      * @return {Roo.Element} The underlying dialog Element
15124      */
15125     getEl : function(){
15126         return this.el;
15127     }
15128 });
15129
15130 /**
15131  * @class Roo.DialogManager
15132  * Provides global access to BasicDialogs that have been created and
15133  * support for z-indexing (layering) multiple open dialogs.
15134  */
15135 Roo.DialogManager = function(){
15136     var list = {};
15137     var accessList = [];
15138     var front = null;
15139
15140     // private
15141     var sortDialogs = function(d1, d2){
15142         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15143     };
15144
15145     // private
15146     var orderDialogs = function(){
15147         accessList.sort(sortDialogs);
15148         var seed = Roo.DialogManager.zseed;
15149         for(var i = 0, len = accessList.length; i < len; i++){
15150             var dlg = accessList[i];
15151             if(dlg){
15152                 dlg.setZIndex(seed + (i*10));
15153             }
15154         }
15155     };
15156
15157     return {
15158         /**
15159          * The starting z-index for BasicDialogs (defaults to 9000)
15160          * @type Number The z-index value
15161          */
15162         zseed : 9000,
15163
15164         // private
15165         register : function(dlg){
15166             list[dlg.id] = dlg;
15167             accessList.push(dlg);
15168         },
15169
15170         // private
15171         unregister : function(dlg){
15172             delete list[dlg.id];
15173             var i=0;
15174             var len=0;
15175             if(!accessList.indexOf){
15176                 for(  i = 0, len = accessList.length; i < len; i++){
15177                     if(accessList[i] == dlg){
15178                         accessList.splice(i, 1);
15179                         return;
15180                     }
15181                 }
15182             }else{
15183                  i = accessList.indexOf(dlg);
15184                 if(i != -1){
15185                     accessList.splice(i, 1);
15186                 }
15187             }
15188         },
15189
15190         /**
15191          * Gets a registered dialog by id
15192          * @param {String/Object} id The id of the dialog or a dialog
15193          * @return {Roo.BasicDialog} this
15194          */
15195         get : function(id){
15196             return typeof id == "object" ? id : list[id];
15197         },
15198
15199         /**
15200          * Brings the specified dialog to the front
15201          * @param {String/Object} dlg The id of the dialog or a dialog
15202          * @return {Roo.BasicDialog} this
15203          */
15204         bringToFront : function(dlg){
15205             dlg = this.get(dlg);
15206             if(dlg != front){
15207                 front = dlg;
15208                 dlg._lastAccess = new Date().getTime();
15209                 orderDialogs();
15210             }
15211             return dlg;
15212         },
15213
15214         /**
15215          * Sends the specified dialog to the back
15216          * @param {String/Object} dlg The id of the dialog or a dialog
15217          * @return {Roo.BasicDialog} this
15218          */
15219         sendToBack : function(dlg){
15220             dlg = this.get(dlg);
15221             dlg._lastAccess = -(new Date().getTime());
15222             orderDialogs();
15223             return dlg;
15224         },
15225
15226         /**
15227          * Hides all dialogs
15228          */
15229         hideAll : function(){
15230             for(var id in list){
15231                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15232                     list[id].hide();
15233                 }
15234             }
15235         }
15236     };
15237 }();
15238
15239 /**
15240  * @class Roo.LayoutDialog
15241  * @extends Roo.BasicDialog
15242  * Dialog which provides adjustments for working with a layout in a Dialog.
15243  * Add your necessary layout config options to the dialog's config.<br>
15244  * Example usage (including a nested layout):
15245  * <pre><code>
15246 if(!dialog){
15247     dialog = new Roo.LayoutDialog("download-dlg", {
15248         modal: true,
15249         width:600,
15250         height:450,
15251         shadow:true,
15252         minWidth:500,
15253         minHeight:350,
15254         autoTabs:true,
15255         proxyDrag:true,
15256         // layout config merges with the dialog config
15257         center:{
15258             tabPosition: "top",
15259             alwaysShowTabs: true
15260         }
15261     });
15262     dialog.addKeyListener(27, dialog.hide, dialog);
15263     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15264     dialog.addButton("Build It!", this.getDownload, this);
15265
15266     // we can even add nested layouts
15267     var innerLayout = new Roo.BorderLayout("dl-inner", {
15268         east: {
15269             initialSize: 200,
15270             autoScroll:true,
15271             split:true
15272         },
15273         center: {
15274             autoScroll:true
15275         }
15276     });
15277     innerLayout.beginUpdate();
15278     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15279     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15280     innerLayout.endUpdate(true);
15281
15282     var layout = dialog.getLayout();
15283     layout.beginUpdate();
15284     layout.add("center", new Roo.ContentPanel("standard-panel",
15285                         {title: "Download the Source", fitToFrame:true}));
15286     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15287                {title: "Build your own roo.js"}));
15288     layout.getRegion("center").showPanel(sp);
15289     layout.endUpdate();
15290 }
15291 </code></pre>
15292     * @constructor
15293     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15294     * @param {Object} config configuration options
15295   */
15296 Roo.LayoutDialog = function(el, cfg){
15297     
15298     var config=  cfg;
15299     if (typeof(cfg) == 'undefined') {
15300         config = Roo.apply({}, el);
15301         // not sure why we use documentElement here.. - it should always be body.
15302         // IE7 borks horribly if we use documentElement.
15303         // webkit also does not like documentElement - it creates a body element...
15304         el = Roo.get( document.body || document.documentElement ).createChild();
15305         //config.autoCreate = true;
15306     }
15307     
15308     
15309     config.autoTabs = false;
15310     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15311     this.body.setStyle({overflow:"hidden", position:"relative"});
15312     this.layout = new Roo.BorderLayout(this.body.dom, config);
15313     this.layout.monitorWindowResize = false;
15314     this.el.addClass("x-dlg-auto-layout");
15315     // fix case when center region overwrites center function
15316     this.center = Roo.BasicDialog.prototype.center;
15317     this.on("show", this.layout.layout, this.layout, true);
15318     if (config.items) {
15319         var xitems = config.items;
15320         delete config.items;
15321         Roo.each(xitems, this.addxtype, this);
15322     }
15323     
15324     
15325 };
15326 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15327     /**
15328      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15329      * @deprecated
15330      */
15331     endUpdate : function(){
15332         this.layout.endUpdate();
15333     },
15334
15335     /**
15336      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15337      *  @deprecated
15338      */
15339     beginUpdate : function(){
15340         this.layout.beginUpdate();
15341     },
15342
15343     /**
15344      * Get the BorderLayout for this dialog
15345      * @return {Roo.BorderLayout}
15346      */
15347     getLayout : function(){
15348         return this.layout;
15349     },
15350
15351     showEl : function(){
15352         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15353         if(Roo.isIE7){
15354             this.layout.layout();
15355         }
15356     },
15357
15358     // private
15359     // Use the syncHeightBeforeShow config option to control this automatically
15360     syncBodyHeight : function(){
15361         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15362         if(this.layout){this.layout.layout();}
15363     },
15364     
15365       /**
15366      * Add an xtype element (actually adds to the layout.)
15367      * @return {Object} xdata xtype object data.
15368      */
15369     
15370     addxtype : function(c) {
15371         return this.layout.addxtype(c);
15372     }
15373 });/*
15374  * Based on:
15375  * Ext JS Library 1.1.1
15376  * Copyright(c) 2006-2007, Ext JS, LLC.
15377  *
15378  * Originally Released Under LGPL - original licence link has changed is not relivant.
15379  *
15380  * Fork - LGPL
15381  * <script type="text/javascript">
15382  */
15383  
15384 /**
15385  * @class Roo.MessageBox
15386  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15387  * Example usage:
15388  *<pre><code>
15389 // Basic alert:
15390 Roo.Msg.alert('Status', 'Changes saved successfully.');
15391
15392 // Prompt for user data:
15393 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15394     if (btn == 'ok'){
15395         // process text value...
15396     }
15397 });
15398
15399 // Show a dialog using config options:
15400 Roo.Msg.show({
15401    title:'Save Changes?',
15402    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15403    buttons: Roo.Msg.YESNOCANCEL,
15404    fn: processResult,
15405    animEl: 'elId'
15406 });
15407 </code></pre>
15408  * @singleton
15409  */
15410 Roo.MessageBox = function(){
15411     var dlg, opt, mask, waitTimer;
15412     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15413     var buttons, activeTextEl, bwidth;
15414
15415     // private
15416     var handleButton = function(button){
15417         dlg.hide();
15418         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15419     };
15420
15421     // private
15422     var handleHide = function(){
15423         if(opt && opt.cls){
15424             dlg.el.removeClass(opt.cls);
15425         }
15426         if(waitTimer){
15427             Roo.TaskMgr.stop(waitTimer);
15428             waitTimer = null;
15429         }
15430     };
15431
15432     // private
15433     var updateButtons = function(b){
15434         var width = 0;
15435         if(!b){
15436             buttons["ok"].hide();
15437             buttons["cancel"].hide();
15438             buttons["yes"].hide();
15439             buttons["no"].hide();
15440             dlg.footer.dom.style.display = 'none';
15441             return width;
15442         }
15443         dlg.footer.dom.style.display = '';
15444         for(var k in buttons){
15445             if(typeof buttons[k] != "function"){
15446                 if(b[k]){
15447                     buttons[k].show();
15448                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15449                     width += buttons[k].el.getWidth()+15;
15450                 }else{
15451                     buttons[k].hide();
15452                 }
15453             }
15454         }
15455         return width;
15456     };
15457
15458     // private
15459     var handleEsc = function(d, k, e){
15460         if(opt && opt.closable !== false){
15461             dlg.hide();
15462         }
15463         if(e){
15464             e.stopEvent();
15465         }
15466     };
15467
15468     return {
15469         /**
15470          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15471          * @return {Roo.BasicDialog} The BasicDialog element
15472          */
15473         getDialog : function(){
15474            if(!dlg){
15475                 dlg = new Roo.BasicDialog("x-msg-box", {
15476                     autoCreate : true,
15477                     shadow: true,
15478                     draggable: true,
15479                     resizable:false,
15480                     constraintoviewport:false,
15481                     fixedcenter:true,
15482                     collapsible : false,
15483                     shim:true,
15484                     modal: true,
15485                     width:400, height:100,
15486                     buttonAlign:"center",
15487                     closeClick : function(){
15488                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15489                             handleButton("no");
15490                         }else{
15491                             handleButton("cancel");
15492                         }
15493                     }
15494                 });
15495                 dlg.on("hide", handleHide);
15496                 mask = dlg.mask;
15497                 dlg.addKeyListener(27, handleEsc);
15498                 buttons = {};
15499                 var bt = this.buttonText;
15500                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15501                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15502                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15503                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15504                 bodyEl = dlg.body.createChild({
15505
15506                     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>'
15507                 });
15508                 msgEl = bodyEl.dom.firstChild;
15509                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15510                 textboxEl.enableDisplayMode();
15511                 textboxEl.addKeyListener([10,13], function(){
15512                     if(dlg.isVisible() && opt && opt.buttons){
15513                         if(opt.buttons.ok){
15514                             handleButton("ok");
15515                         }else if(opt.buttons.yes){
15516                             handleButton("yes");
15517                         }
15518                     }
15519                 });
15520                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15521                 textareaEl.enableDisplayMode();
15522                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15523                 progressEl.enableDisplayMode();
15524                 var pf = progressEl.dom.firstChild;
15525                 if (pf) {
15526                     pp = Roo.get(pf.firstChild);
15527                     pp.setHeight(pf.offsetHeight);
15528                 }
15529                 
15530             }
15531             return dlg;
15532         },
15533
15534         /**
15535          * Updates the message box body text
15536          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15537          * the XHTML-compliant non-breaking space character '&amp;#160;')
15538          * @return {Roo.MessageBox} This message box
15539          */
15540         updateText : function(text){
15541             if(!dlg.isVisible() && !opt.width){
15542                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15543             }
15544             msgEl.innerHTML = text || '&#160;';
15545             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15546                         Math.max(opt.minWidth || this.minWidth, bwidth));
15547             if(opt.prompt){
15548                 activeTextEl.setWidth(w);
15549             }
15550             if(dlg.isVisible()){
15551                 dlg.fixedcenter = false;
15552             }
15553             dlg.setContentSize(w, bodyEl.getHeight());
15554             if(dlg.isVisible()){
15555                 dlg.fixedcenter = true;
15556             }
15557             return this;
15558         },
15559
15560         /**
15561          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15562          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15563          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15564          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15565          * @return {Roo.MessageBox} This message box
15566          */
15567         updateProgress : function(value, text){
15568             if(text){
15569                 this.updateText(text);
15570             }
15571             if (pp) { // weird bug on my firefox - for some reason this is not defined
15572                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15573             }
15574             return this;
15575         },        
15576
15577         /**
15578          * Returns true if the message box is currently displayed
15579          * @return {Boolean} True if the message box is visible, else false
15580          */
15581         isVisible : function(){
15582             return dlg && dlg.isVisible();  
15583         },
15584
15585         /**
15586          * Hides the message box if it is displayed
15587          */
15588         hide : function(){
15589             if(this.isVisible()){
15590                 dlg.hide();
15591             }  
15592         },
15593
15594         /**
15595          * Displays a new message box, or reinitializes an existing message box, based on the config options
15596          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15597          * The following config object properties are supported:
15598          * <pre>
15599 Property    Type             Description
15600 ----------  ---------------  ------------------------------------------------------------------------------------
15601 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15602                                    closes (defaults to undefined)
15603 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15604                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15605 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15606                                    progress and wait dialogs will ignore this property and always hide the
15607                                    close button as they can only be closed programmatically.
15608 cls               String           A custom CSS class to apply to the message box element
15609 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15610                                    displayed (defaults to 75)
15611 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15612                                    function will be btn (the name of the button that was clicked, if applicable,
15613                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15614                                    Progress and wait dialogs will ignore this option since they do not respond to
15615                                    user actions and can only be closed programmatically, so any required function
15616                                    should be called by the same code after it closes the dialog.
15617 icon              String           A CSS class that provides a background image to be used as an icon for
15618                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15619 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15620 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15621 modal             Boolean          False to allow user interaction with the page while the message box is
15622                                    displayed (defaults to true)
15623 msg               String           A string that will replace the existing message box body text (defaults
15624                                    to the XHTML-compliant non-breaking space character '&#160;')
15625 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15626 progress          Boolean          True to display a progress bar (defaults to false)
15627 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15628 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15629 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15630 title             String           The title text
15631 value             String           The string value to set into the active textbox element if displayed
15632 wait              Boolean          True to display a progress bar (defaults to false)
15633 width             Number           The width of the dialog in pixels
15634 </pre>
15635          *
15636          * Example usage:
15637          * <pre><code>
15638 Roo.Msg.show({
15639    title: 'Address',
15640    msg: 'Please enter your address:',
15641    width: 300,
15642    buttons: Roo.MessageBox.OKCANCEL,
15643    multiline: true,
15644    fn: saveAddress,
15645    animEl: 'addAddressBtn'
15646 });
15647 </code></pre>
15648          * @param {Object} config Configuration options
15649          * @return {Roo.MessageBox} This message box
15650          */
15651         show : function(options){
15652             if(this.isVisible()){
15653                 this.hide();
15654             }
15655             var d = this.getDialog();
15656             opt = options;
15657             d.setTitle(opt.title || "&#160;");
15658             d.close.setDisplayed(opt.closable !== false);
15659             activeTextEl = textboxEl;
15660             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15661             if(opt.prompt){
15662                 if(opt.multiline){
15663                     textboxEl.hide();
15664                     textareaEl.show();
15665                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15666                         opt.multiline : this.defaultTextHeight);
15667                     activeTextEl = textareaEl;
15668                 }else{
15669                     textboxEl.show();
15670                     textareaEl.hide();
15671                 }
15672             }else{
15673                 textboxEl.hide();
15674                 textareaEl.hide();
15675             }
15676             progressEl.setDisplayed(opt.progress === true);
15677             this.updateProgress(0);
15678             activeTextEl.dom.value = opt.value || "";
15679             if(opt.prompt){
15680                 dlg.setDefaultButton(activeTextEl);
15681             }else{
15682                 var bs = opt.buttons;
15683                 var db = null;
15684                 if(bs && bs.ok){
15685                     db = buttons["ok"];
15686                 }else if(bs && bs.yes){
15687                     db = buttons["yes"];
15688                 }
15689                 dlg.setDefaultButton(db);
15690             }
15691             bwidth = updateButtons(opt.buttons);
15692             this.updateText(opt.msg);
15693             if(opt.cls){
15694                 d.el.addClass(opt.cls);
15695             }
15696             d.proxyDrag = opt.proxyDrag === true;
15697             d.modal = opt.modal !== false;
15698             d.mask = opt.modal !== false ? mask : false;
15699             if(!d.isVisible()){
15700                 // force it to the end of the z-index stack so it gets a cursor in FF
15701                 document.body.appendChild(dlg.el.dom);
15702                 d.animateTarget = null;
15703                 d.show(options.animEl);
15704             }
15705             return this;
15706         },
15707
15708         /**
15709          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15710          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15711          * and closing the message box when the process is complete.
15712          * @param {String} title The title bar text
15713          * @param {String} msg The message box body text
15714          * @return {Roo.MessageBox} This message box
15715          */
15716         progress : function(title, msg){
15717             this.show({
15718                 title : title,
15719                 msg : msg,
15720                 buttons: false,
15721                 progress:true,
15722                 closable:false,
15723                 minWidth: this.minProgressWidth,
15724                 modal : true
15725             });
15726             return this;
15727         },
15728
15729         /**
15730          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15731          * If a callback function is passed it will be called after the user clicks the button, and the
15732          * id of the button that was clicked will be passed as the only parameter to the callback
15733          * (could also be the top-right close button).
15734          * @param {String} title The title bar text
15735          * @param {String} msg The message box body text
15736          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15737          * @param {Object} scope (optional) The scope of the callback function
15738          * @return {Roo.MessageBox} This message box
15739          */
15740         alert : function(title, msg, fn, scope){
15741             this.show({
15742                 title : title,
15743                 msg : msg,
15744                 buttons: this.OK,
15745                 fn: fn,
15746                 scope : scope,
15747                 modal : true
15748             });
15749             return this;
15750         },
15751
15752         /**
15753          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15754          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15755          * You are responsible for closing the message box when the process is complete.
15756          * @param {String} msg The message box body text
15757          * @param {String} title (optional) The title bar text
15758          * @return {Roo.MessageBox} This message box
15759          */
15760         wait : function(msg, title){
15761             this.show({
15762                 title : title,
15763                 msg : msg,
15764                 buttons: false,
15765                 closable:false,
15766                 progress:true,
15767                 modal:true,
15768                 width:300,
15769                 wait:true
15770             });
15771             waitTimer = Roo.TaskMgr.start({
15772                 run: function(i){
15773                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15774                 },
15775                 interval: 1000
15776             });
15777             return this;
15778         },
15779
15780         /**
15781          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15782          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15783          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15784          * @param {String} title The title bar text
15785          * @param {String} msg The message box body text
15786          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15787          * @param {Object} scope (optional) The scope of the callback function
15788          * @return {Roo.MessageBox} This message box
15789          */
15790         confirm : function(title, msg, fn, scope){
15791             this.show({
15792                 title : title,
15793                 msg : msg,
15794                 buttons: this.YESNO,
15795                 fn: fn,
15796                 scope : scope,
15797                 modal : true
15798             });
15799             return this;
15800         },
15801
15802         /**
15803          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15804          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15805          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15806          * (could also be the top-right close button) and the text that was entered will be passed as the two
15807          * parameters to the callback.
15808          * @param {String} title The title bar text
15809          * @param {String} msg The message box body text
15810          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15811          * @param {Object} scope (optional) The scope of the callback function
15812          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15813          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15814          * @return {Roo.MessageBox} This message box
15815          */
15816         prompt : function(title, msg, fn, scope, multiline){
15817             this.show({
15818                 title : title,
15819                 msg : msg,
15820                 buttons: this.OKCANCEL,
15821                 fn: fn,
15822                 minWidth:250,
15823                 scope : scope,
15824                 prompt:true,
15825                 multiline: multiline,
15826                 modal : true
15827             });
15828             return this;
15829         },
15830
15831         /**
15832          * Button config that displays a single OK button
15833          * @type Object
15834          */
15835         OK : {ok:true},
15836         /**
15837          * Button config that displays Yes and No buttons
15838          * @type Object
15839          */
15840         YESNO : {yes:true, no:true},
15841         /**
15842          * Button config that displays OK and Cancel buttons
15843          * @type Object
15844          */
15845         OKCANCEL : {ok:true, cancel:true},
15846         /**
15847          * Button config that displays Yes, No and Cancel buttons
15848          * @type Object
15849          */
15850         YESNOCANCEL : {yes:true, no:true, cancel:true},
15851
15852         /**
15853          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15854          * @type Number
15855          */
15856         defaultTextHeight : 75,
15857         /**
15858          * The maximum width in pixels of the message box (defaults to 600)
15859          * @type Number
15860          */
15861         maxWidth : 600,
15862         /**
15863          * The minimum width in pixels of the message box (defaults to 100)
15864          * @type Number
15865          */
15866         minWidth : 100,
15867         /**
15868          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15869          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15870          * @type Number
15871          */
15872         minProgressWidth : 250,
15873         /**
15874          * An object containing the default button text strings that can be overriden for localized language support.
15875          * Supported properties are: ok, cancel, yes and no.
15876          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15877          * @type Object
15878          */
15879         buttonText : {
15880             ok : "OK",
15881             cancel : "Cancel",
15882             yes : "Yes",
15883             no : "No"
15884         }
15885     };
15886 }();
15887
15888 /**
15889  * Shorthand for {@link Roo.MessageBox}
15890  */
15891 Roo.Msg = Roo.MessageBox;/*
15892  * Based on:
15893  * Ext JS Library 1.1.1
15894  * Copyright(c) 2006-2007, Ext JS, LLC.
15895  *
15896  * Originally Released Under LGPL - original licence link has changed is not relivant.
15897  *
15898  * Fork - LGPL
15899  * <script type="text/javascript">
15900  */
15901 /**
15902  * @class Roo.QuickTips
15903  * Provides attractive and customizable tooltips for any element.
15904  * @singleton
15905  */
15906 Roo.QuickTips = function(){
15907     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15908     var ce, bd, xy, dd;
15909     var visible = false, disabled = true, inited = false;
15910     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15911     
15912     var onOver = function(e){
15913         if(disabled){
15914             return;
15915         }
15916         var t = e.getTarget();
15917         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15918             return;
15919         }
15920         if(ce && t == ce.el){
15921             clearTimeout(hideProc);
15922             return;
15923         }
15924         if(t && tagEls[t.id]){
15925             tagEls[t.id].el = t;
15926             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15927             return;
15928         }
15929         var ttp, et = Roo.fly(t);
15930         var ns = cfg.namespace;
15931         if(tm.interceptTitles && t.title){
15932             ttp = t.title;
15933             t.qtip = ttp;
15934             t.removeAttribute("title");
15935             e.preventDefault();
15936         }else{
15937             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15938         }
15939         if(ttp){
15940             showProc = show.defer(tm.showDelay, tm, [{
15941                 el: t, 
15942                 text: ttp, 
15943                 width: et.getAttributeNS(ns, cfg.width),
15944                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15945                 title: et.getAttributeNS(ns, cfg.title),
15946                     cls: et.getAttributeNS(ns, cfg.cls)
15947             }]);
15948         }
15949     };
15950     
15951     var onOut = function(e){
15952         clearTimeout(showProc);
15953         var t = e.getTarget();
15954         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15955             hideProc = setTimeout(hide, tm.hideDelay);
15956         }
15957     };
15958     
15959     var onMove = function(e){
15960         if(disabled){
15961             return;
15962         }
15963         xy = e.getXY();
15964         xy[1] += 18;
15965         if(tm.trackMouse && ce){
15966             el.setXY(xy);
15967         }
15968     };
15969     
15970     var onDown = function(e){
15971         clearTimeout(showProc);
15972         clearTimeout(hideProc);
15973         if(!e.within(el)){
15974             if(tm.hideOnClick){
15975                 hide();
15976                 tm.disable();
15977                 tm.enable.defer(100, tm);
15978             }
15979         }
15980     };
15981     
15982     var getPad = function(){
15983         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15984     };
15985
15986     var show = function(o){
15987         if(disabled){
15988             return;
15989         }
15990         clearTimeout(dismissProc);
15991         ce = o;
15992         if(removeCls){ // in case manually hidden
15993             el.removeClass(removeCls);
15994             removeCls = null;
15995         }
15996         if(ce.cls){
15997             el.addClass(ce.cls);
15998             removeCls = ce.cls;
15999         }
16000         if(ce.title){
16001             tipTitle.update(ce.title);
16002             tipTitle.show();
16003         }else{
16004             tipTitle.update('');
16005             tipTitle.hide();
16006         }
16007         el.dom.style.width  = tm.maxWidth+'px';
16008         //tipBody.dom.style.width = '';
16009         tipBodyText.update(o.text);
16010         var p = getPad(), w = ce.width;
16011         if(!w){
16012             var td = tipBodyText.dom;
16013             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16014             if(aw > tm.maxWidth){
16015                 w = tm.maxWidth;
16016             }else if(aw < tm.minWidth){
16017                 w = tm.minWidth;
16018             }else{
16019                 w = aw;
16020             }
16021         }
16022         //tipBody.setWidth(w);
16023         el.setWidth(parseInt(w, 10) + p);
16024         if(ce.autoHide === false){
16025             close.setDisplayed(true);
16026             if(dd){
16027                 dd.unlock();
16028             }
16029         }else{
16030             close.setDisplayed(false);
16031             if(dd){
16032                 dd.lock();
16033             }
16034         }
16035         if(xy){
16036             el.avoidY = xy[1]-18;
16037             el.setXY(xy);
16038         }
16039         if(tm.animate){
16040             el.setOpacity(.1);
16041             el.setStyle("visibility", "visible");
16042             el.fadeIn({callback: afterShow});
16043         }else{
16044             afterShow();
16045         }
16046     };
16047     
16048     var afterShow = function(){
16049         if(ce){
16050             el.show();
16051             esc.enable();
16052             if(tm.autoDismiss && ce.autoHide !== false){
16053                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16054             }
16055         }
16056     };
16057     
16058     var hide = function(noanim){
16059         clearTimeout(dismissProc);
16060         clearTimeout(hideProc);
16061         ce = null;
16062         if(el.isVisible()){
16063             esc.disable();
16064             if(noanim !== true && tm.animate){
16065                 el.fadeOut({callback: afterHide});
16066             }else{
16067                 afterHide();
16068             } 
16069         }
16070     };
16071     
16072     var afterHide = function(){
16073         el.hide();
16074         if(removeCls){
16075             el.removeClass(removeCls);
16076             removeCls = null;
16077         }
16078     };
16079     
16080     return {
16081         /**
16082         * @cfg {Number} minWidth
16083         * The minimum width of the quick tip (defaults to 40)
16084         */
16085        minWidth : 40,
16086         /**
16087         * @cfg {Number} maxWidth
16088         * The maximum width of the quick tip (defaults to 300)
16089         */
16090        maxWidth : 300,
16091         /**
16092         * @cfg {Boolean} interceptTitles
16093         * True to automatically use the element's DOM title value if available (defaults to false)
16094         */
16095        interceptTitles : false,
16096         /**
16097         * @cfg {Boolean} trackMouse
16098         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16099         */
16100        trackMouse : false,
16101         /**
16102         * @cfg {Boolean} hideOnClick
16103         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16104         */
16105        hideOnClick : true,
16106         /**
16107         * @cfg {Number} showDelay
16108         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16109         */
16110        showDelay : 500,
16111         /**
16112         * @cfg {Number} hideDelay
16113         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16114         */
16115        hideDelay : 200,
16116         /**
16117         * @cfg {Boolean} autoHide
16118         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16119         * Used in conjunction with hideDelay.
16120         */
16121        autoHide : true,
16122         /**
16123         * @cfg {Boolean}
16124         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16125         * (defaults to true).  Used in conjunction with autoDismissDelay.
16126         */
16127        autoDismiss : true,
16128         /**
16129         * @cfg {Number}
16130         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16131         */
16132        autoDismissDelay : 5000,
16133        /**
16134         * @cfg {Boolean} animate
16135         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16136         */
16137        animate : false,
16138
16139        /**
16140         * @cfg {String} title
16141         * Title text to display (defaults to '').  This can be any valid HTML markup.
16142         */
16143         title: '',
16144        /**
16145         * @cfg {String} text
16146         * Body text to display (defaults to '').  This can be any valid HTML markup.
16147         */
16148         text : '',
16149        /**
16150         * @cfg {String} cls
16151         * A CSS class to apply to the base quick tip element (defaults to '').
16152         */
16153         cls : '',
16154        /**
16155         * @cfg {Number} width
16156         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16157         * minWidth or maxWidth.
16158         */
16159         width : null,
16160
16161     /**
16162      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16163      * or display QuickTips in a page.
16164      */
16165        init : function(){
16166           tm = Roo.QuickTips;
16167           cfg = tm.tagConfig;
16168           if(!inited){
16169               if(!Roo.isReady){ // allow calling of init() before onReady
16170                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16171                   return;
16172               }
16173               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16174               el.fxDefaults = {stopFx: true};
16175               // maximum custom styling
16176               //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>');
16177               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>');              
16178               tipTitle = el.child('h3');
16179               tipTitle.enableDisplayMode("block");
16180               tipBody = el.child('div.x-tip-bd');
16181               tipBodyText = el.child('div.x-tip-bd-inner');
16182               //bdLeft = el.child('div.x-tip-bd-left');
16183               //bdRight = el.child('div.x-tip-bd-right');
16184               close = el.child('div.x-tip-close');
16185               close.enableDisplayMode("block");
16186               close.on("click", hide);
16187               var d = Roo.get(document);
16188               d.on("mousedown", onDown);
16189               d.on("mouseover", onOver);
16190               d.on("mouseout", onOut);
16191               d.on("mousemove", onMove);
16192               esc = d.addKeyListener(27, hide);
16193               esc.disable();
16194               if(Roo.dd.DD){
16195                   dd = el.initDD("default", null, {
16196                       onDrag : function(){
16197                           el.sync();  
16198                       }
16199                   });
16200                   dd.setHandleElId(tipTitle.id);
16201                   dd.lock();
16202               }
16203               inited = true;
16204           }
16205           this.enable(); 
16206        },
16207
16208     /**
16209      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16210      * are supported:
16211      * <pre>
16212 Property    Type                   Description
16213 ----------  ---------------------  ------------------------------------------------------------------------
16214 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16215      * </ul>
16216      * @param {Object} config The config object
16217      */
16218        register : function(config){
16219            var cs = config instanceof Array ? config : arguments;
16220            for(var i = 0, len = cs.length; i < len; i++) {
16221                var c = cs[i];
16222                var target = c.target;
16223                if(target){
16224                    if(target instanceof Array){
16225                        for(var j = 0, jlen = target.length; j < jlen; j++){
16226                            tagEls[target[j]] = c;
16227                        }
16228                    }else{
16229                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16230                    }
16231                }
16232            }
16233        },
16234
16235     /**
16236      * Removes this quick tip from its element and destroys it.
16237      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16238      */
16239        unregister : function(el){
16240            delete tagEls[Roo.id(el)];
16241        },
16242
16243     /**
16244      * Enable this quick tip.
16245      */
16246        enable : function(){
16247            if(inited && disabled){
16248                locks.pop();
16249                if(locks.length < 1){
16250                    disabled = false;
16251                }
16252            }
16253        },
16254
16255     /**
16256      * Disable this quick tip.
16257      */
16258        disable : function(){
16259           disabled = true;
16260           clearTimeout(showProc);
16261           clearTimeout(hideProc);
16262           clearTimeout(dismissProc);
16263           if(ce){
16264               hide(true);
16265           }
16266           locks.push(1);
16267        },
16268
16269     /**
16270      * Returns true if the quick tip is enabled, else false.
16271      */
16272        isEnabled : function(){
16273             return !disabled;
16274        },
16275
16276         // private
16277        tagConfig : {
16278            namespace : "ext",
16279            attribute : "qtip",
16280            width : "width",
16281            target : "target",
16282            title : "qtitle",
16283            hide : "hide",
16284            cls : "qclass"
16285        }
16286    };
16287 }();
16288
16289 // backwards compat
16290 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16291  * Based on:
16292  * Ext JS Library 1.1.1
16293  * Copyright(c) 2006-2007, Ext JS, LLC.
16294  *
16295  * Originally Released Under LGPL - original licence link has changed is not relivant.
16296  *
16297  * Fork - LGPL
16298  * <script type="text/javascript">
16299  */
16300  
16301
16302 /**
16303  * @class Roo.tree.TreePanel
16304  * @extends Roo.data.Tree
16305
16306  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16307  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16308  * @cfg {Boolean} enableDD true to enable drag and drop
16309  * @cfg {Boolean} enableDrag true to enable just drag
16310  * @cfg {Boolean} enableDrop true to enable just drop
16311  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16312  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16313  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16314  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16315  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16316  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16317  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16318  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16319  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16320  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16321  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16322  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16323  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16324  * @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>
16325  * @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>
16326  * 
16327  * @constructor
16328  * @param {String/HTMLElement/Element} el The container element
16329  * @param {Object} config
16330  */
16331 Roo.tree.TreePanel = function(el, config){
16332     var root = false;
16333     var loader = false;
16334     if (config.root) {
16335         root = config.root;
16336         delete config.root;
16337     }
16338     if (config.loader) {
16339         loader = config.loader;
16340         delete config.loader;
16341     }
16342     
16343     Roo.apply(this, config);
16344     Roo.tree.TreePanel.superclass.constructor.call(this);
16345     this.el = Roo.get(el);
16346     this.el.addClass('x-tree');
16347     //console.log(root);
16348     if (root) {
16349         this.setRootNode( Roo.factory(root, Roo.tree));
16350     }
16351     if (loader) {
16352         this.loader = Roo.factory(loader, Roo.tree);
16353     }
16354    /**
16355     * Read-only. The id of the container element becomes this TreePanel's id.
16356     */
16357    this.id = this.el.id;
16358    this.addEvents({
16359         /**
16360         * @event beforeload
16361         * Fires before a node is loaded, return false to cancel
16362         * @param {Node} node The node being loaded
16363         */
16364         "beforeload" : true,
16365         /**
16366         * @event load
16367         * Fires when a node is loaded
16368         * @param {Node} node The node that was loaded
16369         */
16370         "load" : true,
16371         /**
16372         * @event textchange
16373         * Fires when the text for a node is changed
16374         * @param {Node} node The node
16375         * @param {String} text The new text
16376         * @param {String} oldText The old text
16377         */
16378         "textchange" : true,
16379         /**
16380         * @event beforeexpand
16381         * Fires before a node is expanded, return false to cancel.
16382         * @param {Node} node The node
16383         * @param {Boolean} deep
16384         * @param {Boolean} anim
16385         */
16386         "beforeexpand" : true,
16387         /**
16388         * @event beforecollapse
16389         * Fires before a node is collapsed, return false to cancel.
16390         * @param {Node} node The node
16391         * @param {Boolean} deep
16392         * @param {Boolean} anim
16393         */
16394         "beforecollapse" : true,
16395         /**
16396         * @event expand
16397         * Fires when a node is expanded
16398         * @param {Node} node The node
16399         */
16400         "expand" : true,
16401         /**
16402         * @event disabledchange
16403         * Fires when the disabled status of a node changes
16404         * @param {Node} node The node
16405         * @param {Boolean} disabled
16406         */
16407         "disabledchange" : true,
16408         /**
16409         * @event collapse
16410         * Fires when a node is collapsed
16411         * @param {Node} node The node
16412         */
16413         "collapse" : true,
16414         /**
16415         * @event beforeclick
16416         * Fires before click processing on a node. Return false to cancel the default action.
16417         * @param {Node} node The node
16418         * @param {Roo.EventObject} e The event object
16419         */
16420         "beforeclick":true,
16421         /**
16422         * @event checkchange
16423         * Fires when a node with a checkbox's checked property changes
16424         * @param {Node} this This node
16425         * @param {Boolean} checked
16426         */
16427         "checkchange":true,
16428         /**
16429         * @event click
16430         * Fires when a node is clicked
16431         * @param {Node} node The node
16432         * @param {Roo.EventObject} e The event object
16433         */
16434         "click":true,
16435         /**
16436         * @event dblclick
16437         * Fires when a node is double clicked
16438         * @param {Node} node The node
16439         * @param {Roo.EventObject} e The event object
16440         */
16441         "dblclick":true,
16442         /**
16443         * @event contextmenu
16444         * Fires when a node is right clicked
16445         * @param {Node} node The node
16446         * @param {Roo.EventObject} e The event object
16447         */
16448         "contextmenu":true,
16449         /**
16450         * @event beforechildrenrendered
16451         * Fires right before the child nodes for a node are rendered
16452         * @param {Node} node The node
16453         */
16454         "beforechildrenrendered":true,
16455        /**
16456              * @event startdrag
16457              * Fires when a node starts being dragged
16458              * @param {Roo.tree.TreePanel} this
16459              * @param {Roo.tree.TreeNode} node
16460              * @param {event} e The raw browser event
16461              */ 
16462             "startdrag" : true,
16463             /**
16464              * @event enddrag
16465              * Fires when a drag operation is complete
16466              * @param {Roo.tree.TreePanel} this
16467              * @param {Roo.tree.TreeNode} node
16468              * @param {event} e The raw browser event
16469              */
16470             "enddrag" : true,
16471             /**
16472              * @event dragdrop
16473              * Fires when a dragged node is dropped on a valid DD target
16474              * @param {Roo.tree.TreePanel} this
16475              * @param {Roo.tree.TreeNode} node
16476              * @param {DD} dd The dd it was dropped on
16477              * @param {event} e The raw browser event
16478              */
16479             "dragdrop" : true,
16480             /**
16481              * @event beforenodedrop
16482              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16483              * passed to handlers has the following properties:<br />
16484              * <ul style="padding:5px;padding-left:16px;">
16485              * <li>tree - The TreePanel</li>
16486              * <li>target - The node being targeted for the drop</li>
16487              * <li>data - The drag data from the drag source</li>
16488              * <li>point - The point of the drop - append, above or below</li>
16489              * <li>source - The drag source</li>
16490              * <li>rawEvent - Raw mouse event</li>
16491              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16492              * to be inserted by setting them on this object.</li>
16493              * <li>cancel - Set this to true to cancel the drop.</li>
16494              * </ul>
16495              * @param {Object} dropEvent
16496              */
16497             "beforenodedrop" : true,
16498             /**
16499              * @event nodedrop
16500              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16501              * passed to handlers has the following properties:<br />
16502              * <ul style="padding:5px;padding-left:16px;">
16503              * <li>tree - The TreePanel</li>
16504              * <li>target - The node being targeted for the drop</li>
16505              * <li>data - The drag data from the drag source</li>
16506              * <li>point - The point of the drop - append, above or below</li>
16507              * <li>source - The drag source</li>
16508              * <li>rawEvent - Raw mouse event</li>
16509              * <li>dropNode - Dropped node(s).</li>
16510              * </ul>
16511              * @param {Object} dropEvent
16512              */
16513             "nodedrop" : true,
16514              /**
16515              * @event nodedragover
16516              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16517              * passed to handlers has the following properties:<br />
16518              * <ul style="padding:5px;padding-left:16px;">
16519              * <li>tree - The TreePanel</li>
16520              * <li>target - The node being targeted for the drop</li>
16521              * <li>data - The drag data from the drag source</li>
16522              * <li>point - The point of the drop - append, above or below</li>
16523              * <li>source - The drag source</li>
16524              * <li>rawEvent - Raw mouse event</li>
16525              * <li>dropNode - Drop node(s) provided by the source.</li>
16526              * <li>cancel - Set this to true to signal drop not allowed.</li>
16527              * </ul>
16528              * @param {Object} dragOverEvent
16529              */
16530             "nodedragover" : true
16531         
16532    });
16533    if(this.singleExpand){
16534        this.on("beforeexpand", this.restrictExpand, this);
16535    }
16536 };
16537 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16538     rootVisible : true,
16539     animate: Roo.enableFx,
16540     lines : true,
16541     enableDD : false,
16542     hlDrop : Roo.enableFx,
16543   
16544     renderer: false,
16545     
16546     rendererTip: false,
16547     // private
16548     restrictExpand : function(node){
16549         var p = node.parentNode;
16550         if(p){
16551             if(p.expandedChild && p.expandedChild.parentNode == p){
16552                 p.expandedChild.collapse();
16553             }
16554             p.expandedChild = node;
16555         }
16556     },
16557
16558     // private override
16559     setRootNode : function(node){
16560         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16561         if(!this.rootVisible){
16562             node.ui = new Roo.tree.RootTreeNodeUI(node);
16563         }
16564         return node;
16565     },
16566
16567     /**
16568      * Returns the container element for this TreePanel
16569      */
16570     getEl : function(){
16571         return this.el;
16572     },
16573
16574     /**
16575      * Returns the default TreeLoader for this TreePanel
16576      */
16577     getLoader : function(){
16578         return this.loader;
16579     },
16580
16581     /**
16582      * Expand all nodes
16583      */
16584     expandAll : function(){
16585         this.root.expand(true);
16586     },
16587
16588     /**
16589      * Collapse all nodes
16590      */
16591     collapseAll : function(){
16592         this.root.collapse(true);
16593     },
16594
16595     /**
16596      * Returns the selection model used by this TreePanel
16597      */
16598     getSelectionModel : function(){
16599         if(!this.selModel){
16600             this.selModel = new Roo.tree.DefaultSelectionModel();
16601         }
16602         return this.selModel;
16603     },
16604
16605     /**
16606      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16607      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16608      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16609      * @return {Array}
16610      */
16611     getChecked : function(a, startNode){
16612         startNode = startNode || this.root;
16613         var r = [];
16614         var f = function(){
16615             if(this.attributes.checked){
16616                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16617             }
16618         }
16619         startNode.cascade(f);
16620         return r;
16621     },
16622
16623     /**
16624      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16625      * @param {String} path
16626      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16627      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16628      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16629      */
16630     expandPath : function(path, attr, callback){
16631         attr = attr || "id";
16632         var keys = path.split(this.pathSeparator);
16633         var curNode = this.root;
16634         if(curNode.attributes[attr] != keys[1]){ // invalid root
16635             if(callback){
16636                 callback(false, null);
16637             }
16638             return;
16639         }
16640         var index = 1;
16641         var f = function(){
16642             if(++index == keys.length){
16643                 if(callback){
16644                     callback(true, curNode);
16645                 }
16646                 return;
16647             }
16648             var c = curNode.findChild(attr, keys[index]);
16649             if(!c){
16650                 if(callback){
16651                     callback(false, curNode);
16652                 }
16653                 return;
16654             }
16655             curNode = c;
16656             c.expand(false, false, f);
16657         };
16658         curNode.expand(false, false, f);
16659     },
16660
16661     /**
16662      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16663      * @param {String} path
16664      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16665      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16666      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16667      */
16668     selectPath : function(path, attr, callback){
16669         attr = attr || "id";
16670         var keys = path.split(this.pathSeparator);
16671         var v = keys.pop();
16672         if(keys.length > 0){
16673             var f = function(success, node){
16674                 if(success && node){
16675                     var n = node.findChild(attr, v);
16676                     if(n){
16677                         n.select();
16678                         if(callback){
16679                             callback(true, n);
16680                         }
16681                     }else if(callback){
16682                         callback(false, n);
16683                     }
16684                 }else{
16685                     if(callback){
16686                         callback(false, n);
16687                     }
16688                 }
16689             };
16690             this.expandPath(keys.join(this.pathSeparator), attr, f);
16691         }else{
16692             this.root.select();
16693             if(callback){
16694                 callback(true, this.root);
16695             }
16696         }
16697     },
16698
16699     getTreeEl : function(){
16700         return this.el;
16701     },
16702
16703     /**
16704      * Trigger rendering of this TreePanel
16705      */
16706     render : function(){
16707         if (this.innerCt) {
16708             return this; // stop it rendering more than once!!
16709         }
16710         
16711         this.innerCt = this.el.createChild({tag:"ul",
16712                cls:"x-tree-root-ct " +
16713                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16714
16715         if(this.containerScroll){
16716             Roo.dd.ScrollManager.register(this.el);
16717         }
16718         if((this.enableDD || this.enableDrop) && !this.dropZone){
16719            /**
16720             * The dropZone used by this tree if drop is enabled
16721             * @type Roo.tree.TreeDropZone
16722             */
16723              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16724                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16725            });
16726         }
16727         if((this.enableDD || this.enableDrag) && !this.dragZone){
16728            /**
16729             * The dragZone used by this tree if drag is enabled
16730             * @type Roo.tree.TreeDragZone
16731             */
16732             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16733                ddGroup: this.ddGroup || "TreeDD",
16734                scroll: this.ddScroll
16735            });
16736         }
16737         this.getSelectionModel().init(this);
16738         if (!this.root) {
16739             console.log("ROOT not set in tree");
16740             return;
16741         }
16742         this.root.render();
16743         if(!this.rootVisible){
16744             this.root.renderChildren();
16745         }
16746         return this;
16747     }
16748 });/*
16749  * Based on:
16750  * Ext JS Library 1.1.1
16751  * Copyright(c) 2006-2007, Ext JS, LLC.
16752  *
16753  * Originally Released Under LGPL - original licence link has changed is not relivant.
16754  *
16755  * Fork - LGPL
16756  * <script type="text/javascript">
16757  */
16758  
16759
16760 /**
16761  * @class Roo.tree.DefaultSelectionModel
16762  * @extends Roo.util.Observable
16763  * The default single selection for a TreePanel.
16764  */
16765 Roo.tree.DefaultSelectionModel = function(){
16766    this.selNode = null;
16767    
16768    this.addEvents({
16769        /**
16770         * @event selectionchange
16771         * Fires when the selected node changes
16772         * @param {DefaultSelectionModel} this
16773         * @param {TreeNode} node the new selection
16774         */
16775        "selectionchange" : true,
16776
16777        /**
16778         * @event beforeselect
16779         * Fires before the selected node changes, return false to cancel the change
16780         * @param {DefaultSelectionModel} this
16781         * @param {TreeNode} node the new selection
16782         * @param {TreeNode} node the old selection
16783         */
16784        "beforeselect" : true
16785    });
16786 };
16787
16788 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16789     init : function(tree){
16790         this.tree = tree;
16791         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16792         tree.on("click", this.onNodeClick, this);
16793     },
16794     
16795     onNodeClick : function(node, e){
16796         if (e.ctrlKey && this.selNode == node)  {
16797             this.unselect(node);
16798             return;
16799         }
16800         this.select(node);
16801     },
16802     
16803     /**
16804      * Select a node.
16805      * @param {TreeNode} node The node to select
16806      * @return {TreeNode} The selected node
16807      */
16808     select : function(node){
16809         var last = this.selNode;
16810         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16811             if(last){
16812                 last.ui.onSelectedChange(false);
16813             }
16814             this.selNode = node;
16815             node.ui.onSelectedChange(true);
16816             this.fireEvent("selectionchange", this, node, last);
16817         }
16818         return node;
16819     },
16820     
16821     /**
16822      * Deselect a node.
16823      * @param {TreeNode} node The node to unselect
16824      */
16825     unselect : function(node){
16826         if(this.selNode == node){
16827             this.clearSelections();
16828         }    
16829     },
16830     
16831     /**
16832      * Clear all selections
16833      */
16834     clearSelections : function(){
16835         var n = this.selNode;
16836         if(n){
16837             n.ui.onSelectedChange(false);
16838             this.selNode = null;
16839             this.fireEvent("selectionchange", this, null);
16840         }
16841         return n;
16842     },
16843     
16844     /**
16845      * Get the selected node
16846      * @return {TreeNode} The selected node
16847      */
16848     getSelectedNode : function(){
16849         return this.selNode;    
16850     },
16851     
16852     /**
16853      * Returns true if the node is selected
16854      * @param {TreeNode} node The node to check
16855      * @return {Boolean}
16856      */
16857     isSelected : function(node){
16858         return this.selNode == node;  
16859     },
16860
16861     /**
16862      * Selects the node above the selected node in the tree, intelligently walking the nodes
16863      * @return TreeNode The new selection
16864      */
16865     selectPrevious : function(){
16866         var s = this.selNode || this.lastSelNode;
16867         if(!s){
16868             return null;
16869         }
16870         var ps = s.previousSibling;
16871         if(ps){
16872             if(!ps.isExpanded() || ps.childNodes.length < 1){
16873                 return this.select(ps);
16874             } else{
16875                 var lc = ps.lastChild;
16876                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16877                     lc = lc.lastChild;
16878                 }
16879                 return this.select(lc);
16880             }
16881         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16882             return this.select(s.parentNode);
16883         }
16884         return null;
16885     },
16886
16887     /**
16888      * Selects the node above the selected node in the tree, intelligently walking the nodes
16889      * @return TreeNode The new selection
16890      */
16891     selectNext : function(){
16892         var s = this.selNode || this.lastSelNode;
16893         if(!s){
16894             return null;
16895         }
16896         if(s.firstChild && s.isExpanded()){
16897              return this.select(s.firstChild);
16898          }else if(s.nextSibling){
16899              return this.select(s.nextSibling);
16900          }else if(s.parentNode){
16901             var newS = null;
16902             s.parentNode.bubble(function(){
16903                 if(this.nextSibling){
16904                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16905                     return false;
16906                 }
16907             });
16908             return newS;
16909          }
16910         return null;
16911     },
16912
16913     onKeyDown : function(e){
16914         var s = this.selNode || this.lastSelNode;
16915         // undesirable, but required
16916         var sm = this;
16917         if(!s){
16918             return;
16919         }
16920         var k = e.getKey();
16921         switch(k){
16922              case e.DOWN:
16923                  e.stopEvent();
16924                  this.selectNext();
16925              break;
16926              case e.UP:
16927                  e.stopEvent();
16928                  this.selectPrevious();
16929              break;
16930              case e.RIGHT:
16931                  e.preventDefault();
16932                  if(s.hasChildNodes()){
16933                      if(!s.isExpanded()){
16934                          s.expand();
16935                      }else if(s.firstChild){
16936                          this.select(s.firstChild, e);
16937                      }
16938                  }
16939              break;
16940              case e.LEFT:
16941                  e.preventDefault();
16942                  if(s.hasChildNodes() && s.isExpanded()){
16943                      s.collapse();
16944                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16945                      this.select(s.parentNode, e);
16946                  }
16947              break;
16948         };
16949     }
16950 });
16951
16952 /**
16953  * @class Roo.tree.MultiSelectionModel
16954  * @extends Roo.util.Observable
16955  * Multi selection for a TreePanel.
16956  */
16957 Roo.tree.MultiSelectionModel = function(){
16958    this.selNodes = [];
16959    this.selMap = {};
16960    this.addEvents({
16961        /**
16962         * @event selectionchange
16963         * Fires when the selected nodes change
16964         * @param {MultiSelectionModel} this
16965         * @param {Array} nodes Array of the selected nodes
16966         */
16967        "selectionchange" : true
16968    });
16969 };
16970
16971 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16972     init : function(tree){
16973         this.tree = tree;
16974         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16975         tree.on("click", this.onNodeClick, this);
16976     },
16977     
16978     onNodeClick : function(node, e){
16979         this.select(node, e, e.ctrlKey);
16980     },
16981     
16982     /**
16983      * Select a node.
16984      * @param {TreeNode} node The node to select
16985      * @param {EventObject} e (optional) An event associated with the selection
16986      * @param {Boolean} keepExisting True to retain existing selections
16987      * @return {TreeNode} The selected node
16988      */
16989     select : function(node, e, keepExisting){
16990         if(keepExisting !== true){
16991             this.clearSelections(true);
16992         }
16993         if(this.isSelected(node)){
16994             this.lastSelNode = node;
16995             return node;
16996         }
16997         this.selNodes.push(node);
16998         this.selMap[node.id] = node;
16999         this.lastSelNode = node;
17000         node.ui.onSelectedChange(true);
17001         this.fireEvent("selectionchange", this, this.selNodes);
17002         return node;
17003     },
17004     
17005     /**
17006      * Deselect a node.
17007      * @param {TreeNode} node The node to unselect
17008      */
17009     unselect : function(node){
17010         if(this.selMap[node.id]){
17011             node.ui.onSelectedChange(false);
17012             var sn = this.selNodes;
17013             var index = -1;
17014             if(sn.indexOf){
17015                 index = sn.indexOf(node);
17016             }else{
17017                 for(var i = 0, len = sn.length; i < len; i++){
17018                     if(sn[i] == node){
17019                         index = i;
17020                         break;
17021                     }
17022                 }
17023             }
17024             if(index != -1){
17025                 this.selNodes.splice(index, 1);
17026             }
17027             delete this.selMap[node.id];
17028             this.fireEvent("selectionchange", this, this.selNodes);
17029         }
17030     },
17031     
17032     /**
17033      * Clear all selections
17034      */
17035     clearSelections : function(suppressEvent){
17036         var sn = this.selNodes;
17037         if(sn.length > 0){
17038             for(var i = 0, len = sn.length; i < len; i++){
17039                 sn[i].ui.onSelectedChange(false);
17040             }
17041             this.selNodes = [];
17042             this.selMap = {};
17043             if(suppressEvent !== true){
17044                 this.fireEvent("selectionchange", this, this.selNodes);
17045             }
17046         }
17047     },
17048     
17049     /**
17050      * Returns true if the node is selected
17051      * @param {TreeNode} node The node to check
17052      * @return {Boolean}
17053      */
17054     isSelected : function(node){
17055         return this.selMap[node.id] ? true : false;  
17056     },
17057     
17058     /**
17059      * Returns an array of the selected nodes
17060      * @return {Array}
17061      */
17062     getSelectedNodes : function(){
17063         return this.selNodes;    
17064     },
17065
17066     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17067
17068     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17069
17070     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17071 });/*
17072  * Based on:
17073  * Ext JS Library 1.1.1
17074  * Copyright(c) 2006-2007, Ext JS, LLC.
17075  *
17076  * Originally Released Under LGPL - original licence link has changed is not relivant.
17077  *
17078  * Fork - LGPL
17079  * <script type="text/javascript">
17080  */
17081  
17082 /**
17083  * @class Roo.tree.TreeNode
17084  * @extends Roo.data.Node
17085  * @cfg {String} text The text for this node
17086  * @cfg {Boolean} expanded true to start the node expanded
17087  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17088  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17089  * @cfg {Boolean} disabled true to start the node disabled
17090  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17091  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17092  * @cfg {String} cls A css class to be added to the node
17093  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17094  * @cfg {String} href URL of the link used for the node (defaults to #)
17095  * @cfg {String} hrefTarget target frame for the link
17096  * @cfg {String} qtip An Ext QuickTip for the node
17097  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17098  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17099  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17100  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17101  * (defaults to undefined with no checkbox rendered)
17102  * @constructor
17103  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17104  */
17105 Roo.tree.TreeNode = function(attributes){
17106     attributes = attributes || {};
17107     if(typeof attributes == "string"){
17108         attributes = {text: attributes};
17109     }
17110     this.childrenRendered = false;
17111     this.rendered = false;
17112     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17113     this.expanded = attributes.expanded === true;
17114     this.isTarget = attributes.isTarget !== false;
17115     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17116     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17117
17118     /**
17119      * Read-only. The text for this node. To change it use setText().
17120      * @type String
17121      */
17122     this.text = attributes.text;
17123     /**
17124      * True if this node is disabled.
17125      * @type Boolean
17126      */
17127     this.disabled = attributes.disabled === true;
17128
17129     this.addEvents({
17130         /**
17131         * @event textchange
17132         * Fires when the text for this node is changed
17133         * @param {Node} this This node
17134         * @param {String} text The new text
17135         * @param {String} oldText The old text
17136         */
17137         "textchange" : true,
17138         /**
17139         * @event beforeexpand
17140         * Fires before this node is expanded, return false to cancel.
17141         * @param {Node} this This node
17142         * @param {Boolean} deep
17143         * @param {Boolean} anim
17144         */
17145         "beforeexpand" : true,
17146         /**
17147         * @event beforecollapse
17148         * Fires before this node is collapsed, return false to cancel.
17149         * @param {Node} this This node
17150         * @param {Boolean} deep
17151         * @param {Boolean} anim
17152         */
17153         "beforecollapse" : true,
17154         /**
17155         * @event expand
17156         * Fires when this node is expanded
17157         * @param {Node} this This node
17158         */
17159         "expand" : true,
17160         /**
17161         * @event disabledchange
17162         * Fires when the disabled status of this node changes
17163         * @param {Node} this This node
17164         * @param {Boolean} disabled
17165         */
17166         "disabledchange" : true,
17167         /**
17168         * @event collapse
17169         * Fires when this node is collapsed
17170         * @param {Node} this This node
17171         */
17172         "collapse" : true,
17173         /**
17174         * @event beforeclick
17175         * Fires before click processing. Return false to cancel the default action.
17176         * @param {Node} this This node
17177         * @param {Roo.EventObject} e The event object
17178         */
17179         "beforeclick":true,
17180         /**
17181         * @event checkchange
17182         * Fires when a node with a checkbox's checked property changes
17183         * @param {Node} this This node
17184         * @param {Boolean} checked
17185         */
17186         "checkchange":true,
17187         /**
17188         * @event click
17189         * Fires when this node is clicked
17190         * @param {Node} this This node
17191         * @param {Roo.EventObject} e The event object
17192         */
17193         "click":true,
17194         /**
17195         * @event dblclick
17196         * Fires when this node is double clicked
17197         * @param {Node} this This node
17198         * @param {Roo.EventObject} e The event object
17199         */
17200         "dblclick":true,
17201         /**
17202         * @event contextmenu
17203         * Fires when this node is right clicked
17204         * @param {Node} this This node
17205         * @param {Roo.EventObject} e The event object
17206         */
17207         "contextmenu":true,
17208         /**
17209         * @event beforechildrenrendered
17210         * Fires right before the child nodes for this node are rendered
17211         * @param {Node} this This node
17212         */
17213         "beforechildrenrendered":true
17214     });
17215
17216     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17217
17218     /**
17219      * Read-only. The UI for this node
17220      * @type TreeNodeUI
17221      */
17222     this.ui = new uiClass(this);
17223 };
17224 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17225     preventHScroll: true,
17226     /**
17227      * Returns true if this node is expanded
17228      * @return {Boolean}
17229      */
17230     isExpanded : function(){
17231         return this.expanded;
17232     },
17233
17234     /**
17235      * Returns the UI object for this node
17236      * @return {TreeNodeUI}
17237      */
17238     getUI : function(){
17239         return this.ui;
17240     },
17241
17242     // private override
17243     setFirstChild : function(node){
17244         var of = this.firstChild;
17245         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17246         if(this.childrenRendered && of && node != of){
17247             of.renderIndent(true, true);
17248         }
17249         if(this.rendered){
17250             this.renderIndent(true, true);
17251         }
17252     },
17253
17254     // private override
17255     setLastChild : function(node){
17256         var ol = this.lastChild;
17257         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17258         if(this.childrenRendered && ol && node != ol){
17259             ol.renderIndent(true, true);
17260         }
17261         if(this.rendered){
17262             this.renderIndent(true, true);
17263         }
17264     },
17265
17266     // these methods are overridden to provide lazy rendering support
17267     // private override
17268     appendChild : function(){
17269         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17270         if(node && this.childrenRendered){
17271             node.render();
17272         }
17273         this.ui.updateExpandIcon();
17274         return node;
17275     },
17276
17277     // private override
17278     removeChild : function(node){
17279         this.ownerTree.getSelectionModel().unselect(node);
17280         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17281         // if it's been rendered remove dom node
17282         if(this.childrenRendered){
17283             node.ui.remove();
17284         }
17285         if(this.childNodes.length < 1){
17286             this.collapse(false, false);
17287         }else{
17288             this.ui.updateExpandIcon();
17289         }
17290         if(!this.firstChild) {
17291             this.childrenRendered = false;
17292         }
17293         return node;
17294     },
17295
17296     // private override
17297     insertBefore : function(node, refNode){
17298         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17299         if(newNode && refNode && this.childrenRendered){
17300             node.render();
17301         }
17302         this.ui.updateExpandIcon();
17303         return newNode;
17304     },
17305
17306     /**
17307      * Sets the text for this node
17308      * @param {String} text
17309      */
17310     setText : function(text){
17311         var oldText = this.text;
17312         this.text = text;
17313         this.attributes.text = text;
17314         if(this.rendered){ // event without subscribing
17315             this.ui.onTextChange(this, text, oldText);
17316         }
17317         this.fireEvent("textchange", this, text, oldText);
17318     },
17319
17320     /**
17321      * Triggers selection of this node
17322      */
17323     select : function(){
17324         this.getOwnerTree().getSelectionModel().select(this);
17325     },
17326
17327     /**
17328      * Triggers deselection of this node
17329      */
17330     unselect : function(){
17331         this.getOwnerTree().getSelectionModel().unselect(this);
17332     },
17333
17334     /**
17335      * Returns true if this node is selected
17336      * @return {Boolean}
17337      */
17338     isSelected : function(){
17339         return this.getOwnerTree().getSelectionModel().isSelected(this);
17340     },
17341
17342     /**
17343      * Expand this node.
17344      * @param {Boolean} deep (optional) True to expand all children as well
17345      * @param {Boolean} anim (optional) false to cancel the default animation
17346      * @param {Function} callback (optional) A callback to be called when
17347      * expanding this node completes (does not wait for deep expand to complete).
17348      * Called with 1 parameter, this node.
17349      */
17350     expand : function(deep, anim, callback){
17351         if(!this.expanded){
17352             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17353                 return;
17354             }
17355             if(!this.childrenRendered){
17356                 this.renderChildren();
17357             }
17358             this.expanded = true;
17359             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17360                 this.ui.animExpand(function(){
17361                     this.fireEvent("expand", this);
17362                     if(typeof callback == "function"){
17363                         callback(this);
17364                     }
17365                     if(deep === true){
17366                         this.expandChildNodes(true);
17367                     }
17368                 }.createDelegate(this));
17369                 return;
17370             }else{
17371                 this.ui.expand();
17372                 this.fireEvent("expand", this);
17373                 if(typeof callback == "function"){
17374                     callback(this);
17375                 }
17376             }
17377         }else{
17378            if(typeof callback == "function"){
17379                callback(this);
17380            }
17381         }
17382         if(deep === true){
17383             this.expandChildNodes(true);
17384         }
17385     },
17386
17387     isHiddenRoot : function(){
17388         return this.isRoot && !this.getOwnerTree().rootVisible;
17389     },
17390
17391     /**
17392      * Collapse this node.
17393      * @param {Boolean} deep (optional) True to collapse all children as well
17394      * @param {Boolean} anim (optional) false to cancel the default animation
17395      */
17396     collapse : function(deep, anim){
17397         if(this.expanded && !this.isHiddenRoot()){
17398             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17399                 return;
17400             }
17401             this.expanded = false;
17402             if((this.getOwnerTree().animate && anim !== false) || anim){
17403                 this.ui.animCollapse(function(){
17404                     this.fireEvent("collapse", this);
17405                     if(deep === true){
17406                         this.collapseChildNodes(true);
17407                     }
17408                 }.createDelegate(this));
17409                 return;
17410             }else{
17411                 this.ui.collapse();
17412                 this.fireEvent("collapse", this);
17413             }
17414         }
17415         if(deep === true){
17416             var cs = this.childNodes;
17417             for(var i = 0, len = cs.length; i < len; i++) {
17418                 cs[i].collapse(true, false);
17419             }
17420         }
17421     },
17422
17423     // private
17424     delayedExpand : function(delay){
17425         if(!this.expandProcId){
17426             this.expandProcId = this.expand.defer(delay, this);
17427         }
17428     },
17429
17430     // private
17431     cancelExpand : function(){
17432         if(this.expandProcId){
17433             clearTimeout(this.expandProcId);
17434         }
17435         this.expandProcId = false;
17436     },
17437
17438     /**
17439      * Toggles expanded/collapsed state of the node
17440      */
17441     toggle : function(){
17442         if(this.expanded){
17443             this.collapse();
17444         }else{
17445             this.expand();
17446         }
17447     },
17448
17449     /**
17450      * Ensures all parent nodes are expanded
17451      */
17452     ensureVisible : function(callback){
17453         var tree = this.getOwnerTree();
17454         tree.expandPath(this.parentNode.getPath(), false, function(){
17455             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17456             Roo.callback(callback);
17457         }.createDelegate(this));
17458     },
17459
17460     /**
17461      * Expand all child nodes
17462      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17463      */
17464     expandChildNodes : function(deep){
17465         var cs = this.childNodes;
17466         for(var i = 0, len = cs.length; i < len; i++) {
17467                 cs[i].expand(deep);
17468         }
17469     },
17470
17471     /**
17472      * Collapse all child nodes
17473      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17474      */
17475     collapseChildNodes : function(deep){
17476         var cs = this.childNodes;
17477         for(var i = 0, len = cs.length; i < len; i++) {
17478                 cs[i].collapse(deep);
17479         }
17480     },
17481
17482     /**
17483      * Disables this node
17484      */
17485     disable : function(){
17486         this.disabled = true;
17487         this.unselect();
17488         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17489             this.ui.onDisableChange(this, true);
17490         }
17491         this.fireEvent("disabledchange", this, true);
17492     },
17493
17494     /**
17495      * Enables this node
17496      */
17497     enable : function(){
17498         this.disabled = false;
17499         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17500             this.ui.onDisableChange(this, false);
17501         }
17502         this.fireEvent("disabledchange", this, false);
17503     },
17504
17505     // private
17506     renderChildren : function(suppressEvent){
17507         if(suppressEvent !== false){
17508             this.fireEvent("beforechildrenrendered", this);
17509         }
17510         var cs = this.childNodes;
17511         for(var i = 0, len = cs.length; i < len; i++){
17512             cs[i].render(true);
17513         }
17514         this.childrenRendered = true;
17515     },
17516
17517     // private
17518     sort : function(fn, scope){
17519         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17520         if(this.childrenRendered){
17521             var cs = this.childNodes;
17522             for(var i = 0, len = cs.length; i < len; i++){
17523                 cs[i].render(true);
17524             }
17525         }
17526     },
17527
17528     // private
17529     render : function(bulkRender){
17530         this.ui.render(bulkRender);
17531         if(!this.rendered){
17532             this.rendered = true;
17533             if(this.expanded){
17534                 this.expanded = false;
17535                 this.expand(false, false);
17536             }
17537         }
17538     },
17539
17540     // private
17541     renderIndent : function(deep, refresh){
17542         if(refresh){
17543             this.ui.childIndent = null;
17544         }
17545         this.ui.renderIndent();
17546         if(deep === true && this.childrenRendered){
17547             var cs = this.childNodes;
17548             for(var i = 0, len = cs.length; i < len; i++){
17549                 cs[i].renderIndent(true, refresh);
17550             }
17551         }
17552     }
17553 });/*
17554  * Based on:
17555  * Ext JS Library 1.1.1
17556  * Copyright(c) 2006-2007, Ext JS, LLC.
17557  *
17558  * Originally Released Under LGPL - original licence link has changed is not relivant.
17559  *
17560  * Fork - LGPL
17561  * <script type="text/javascript">
17562  */
17563  
17564 /**
17565  * @class Roo.tree.AsyncTreeNode
17566  * @extends Roo.tree.TreeNode
17567  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17568  * @constructor
17569  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17570  */
17571  Roo.tree.AsyncTreeNode = function(config){
17572     this.loaded = false;
17573     this.loading = false;
17574     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17575     /**
17576     * @event beforeload
17577     * Fires before this node is loaded, return false to cancel
17578     * @param {Node} this This node
17579     */
17580     this.addEvents({'beforeload':true, 'load': true});
17581     /**
17582     * @event load
17583     * Fires when this node is loaded
17584     * @param {Node} this This node
17585     */
17586     /**
17587      * The loader used by this node (defaults to using the tree's defined loader)
17588      * @type TreeLoader
17589      * @property loader
17590      */
17591 };
17592 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17593     expand : function(deep, anim, callback){
17594         if(this.loading){ // if an async load is already running, waiting til it's done
17595             var timer;
17596             var f = function(){
17597                 if(!this.loading){ // done loading
17598                     clearInterval(timer);
17599                     this.expand(deep, anim, callback);
17600                 }
17601             }.createDelegate(this);
17602             timer = setInterval(f, 200);
17603             return;
17604         }
17605         if(!this.loaded){
17606             if(this.fireEvent("beforeload", this) === false){
17607                 return;
17608             }
17609             this.loading = true;
17610             this.ui.beforeLoad(this);
17611             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17612             if(loader){
17613                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17614                 return;
17615             }
17616         }
17617         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17618     },
17619     
17620     /**
17621      * Returns true if this node is currently loading
17622      * @return {Boolean}
17623      */
17624     isLoading : function(){
17625         return this.loading;  
17626     },
17627     
17628     loadComplete : function(deep, anim, callback){
17629         this.loading = false;
17630         this.loaded = true;
17631         this.ui.afterLoad(this);
17632         this.fireEvent("load", this);
17633         this.expand(deep, anim, callback);
17634     },
17635     
17636     /**
17637      * Returns true if this node has been loaded
17638      * @return {Boolean}
17639      */
17640     isLoaded : function(){
17641         return this.loaded;
17642     },
17643     
17644     hasChildNodes : function(){
17645         if(!this.isLeaf() && !this.loaded){
17646             return true;
17647         }else{
17648             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17649         }
17650     },
17651
17652     /**
17653      * Trigger a reload for this node
17654      * @param {Function} callback
17655      */
17656     reload : function(callback){
17657         this.collapse(false, false);
17658         while(this.firstChild){
17659             this.removeChild(this.firstChild);
17660         }
17661         this.childrenRendered = false;
17662         this.loaded = false;
17663         if(this.isHiddenRoot()){
17664             this.expanded = false;
17665         }
17666         this.expand(false, false, callback);
17667     }
17668 });/*
17669  * Based on:
17670  * Ext JS Library 1.1.1
17671  * Copyright(c) 2006-2007, Ext JS, LLC.
17672  *
17673  * Originally Released Under LGPL - original licence link has changed is not relivant.
17674  *
17675  * Fork - LGPL
17676  * <script type="text/javascript">
17677  */
17678  
17679 /**
17680  * @class Roo.tree.TreeNodeUI
17681  * @constructor
17682  * @param {Object} node The node to render
17683  * The TreeNode UI implementation is separate from the
17684  * tree implementation. Unless you are customizing the tree UI,
17685  * you should never have to use this directly.
17686  */
17687 Roo.tree.TreeNodeUI = function(node){
17688     this.node = node;
17689     this.rendered = false;
17690     this.animating = false;
17691     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17692 };
17693
17694 Roo.tree.TreeNodeUI.prototype = {
17695     removeChild : function(node){
17696         if(this.rendered){
17697             this.ctNode.removeChild(node.ui.getEl());
17698         }
17699     },
17700
17701     beforeLoad : function(){
17702          this.addClass("x-tree-node-loading");
17703     },
17704
17705     afterLoad : function(){
17706          this.removeClass("x-tree-node-loading");
17707     },
17708
17709     onTextChange : function(node, text, oldText){
17710         if(this.rendered){
17711             this.textNode.innerHTML = text;
17712         }
17713     },
17714
17715     onDisableChange : function(node, state){
17716         this.disabled = state;
17717         if(state){
17718             this.addClass("x-tree-node-disabled");
17719         }else{
17720             this.removeClass("x-tree-node-disabled");
17721         }
17722     },
17723
17724     onSelectedChange : function(state){
17725         if(state){
17726             this.focus();
17727             this.addClass("x-tree-selected");
17728         }else{
17729             //this.blur();
17730             this.removeClass("x-tree-selected");
17731         }
17732     },
17733
17734     onMove : function(tree, node, oldParent, newParent, index, refNode){
17735         this.childIndent = null;
17736         if(this.rendered){
17737             var targetNode = newParent.ui.getContainer();
17738             if(!targetNode){//target not rendered
17739                 this.holder = document.createElement("div");
17740                 this.holder.appendChild(this.wrap);
17741                 return;
17742             }
17743             var insertBefore = refNode ? refNode.ui.getEl() : null;
17744             if(insertBefore){
17745                 targetNode.insertBefore(this.wrap, insertBefore);
17746             }else{
17747                 targetNode.appendChild(this.wrap);
17748             }
17749             this.node.renderIndent(true);
17750         }
17751     },
17752
17753     addClass : function(cls){
17754         if(this.elNode){
17755             Roo.fly(this.elNode).addClass(cls);
17756         }
17757     },
17758
17759     removeClass : function(cls){
17760         if(this.elNode){
17761             Roo.fly(this.elNode).removeClass(cls);
17762         }
17763     },
17764
17765     remove : function(){
17766         if(this.rendered){
17767             this.holder = document.createElement("div");
17768             this.holder.appendChild(this.wrap);
17769         }
17770     },
17771
17772     fireEvent : function(){
17773         return this.node.fireEvent.apply(this.node, arguments);
17774     },
17775
17776     initEvents : function(){
17777         this.node.on("move", this.onMove, this);
17778         var E = Roo.EventManager;
17779         var a = this.anchor;
17780
17781         var el = Roo.fly(a, '_treeui');
17782
17783         if(Roo.isOpera){ // opera render bug ignores the CSS
17784             el.setStyle("text-decoration", "none");
17785         }
17786
17787         el.on("click", this.onClick, this);
17788         el.on("dblclick", this.onDblClick, this);
17789
17790         if(this.checkbox){
17791             Roo.EventManager.on(this.checkbox,
17792                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17793         }
17794
17795         el.on("contextmenu", this.onContextMenu, this);
17796
17797         var icon = Roo.fly(this.iconNode);
17798         icon.on("click", this.onClick, this);
17799         icon.on("dblclick", this.onDblClick, this);
17800         icon.on("contextmenu", this.onContextMenu, this);
17801         E.on(this.ecNode, "click", this.ecClick, this, true);
17802
17803         if(this.node.disabled){
17804             this.addClass("x-tree-node-disabled");
17805         }
17806         if(this.node.hidden){
17807             this.addClass("x-tree-node-disabled");
17808         }
17809         var ot = this.node.getOwnerTree();
17810         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17811         if(dd && (!this.node.isRoot || ot.rootVisible)){
17812             Roo.dd.Registry.register(this.elNode, {
17813                 node: this.node,
17814                 handles: this.getDDHandles(),
17815                 isHandle: false
17816             });
17817         }
17818     },
17819
17820     getDDHandles : function(){
17821         return [this.iconNode, this.textNode];
17822     },
17823
17824     hide : function(){
17825         if(this.rendered){
17826             this.wrap.style.display = "none";
17827         }
17828     },
17829
17830     show : function(){
17831         if(this.rendered){
17832             this.wrap.style.display = "";
17833         }
17834     },
17835
17836     onContextMenu : function(e){
17837         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17838             e.preventDefault();
17839             this.focus();
17840             this.fireEvent("contextmenu", this.node, e);
17841         }
17842     },
17843
17844     onClick : function(e){
17845         if(this.dropping){
17846             e.stopEvent();
17847             return;
17848         }
17849         if(this.fireEvent("beforeclick", this.node, e) !== false){
17850             if(!this.disabled && this.node.attributes.href){
17851                 this.fireEvent("click", this.node, e);
17852                 return;
17853             }
17854             e.preventDefault();
17855             if(this.disabled){
17856                 return;
17857             }
17858
17859             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17860                 this.node.toggle();
17861             }
17862
17863             this.fireEvent("click", this.node, e);
17864         }else{
17865             e.stopEvent();
17866         }
17867     },
17868
17869     onDblClick : function(e){
17870         e.preventDefault();
17871         if(this.disabled){
17872             return;
17873         }
17874         if(this.checkbox){
17875             this.toggleCheck();
17876         }
17877         if(!this.animating && this.node.hasChildNodes()){
17878             this.node.toggle();
17879         }
17880         this.fireEvent("dblclick", this.node, e);
17881     },
17882
17883     onCheckChange : function(){
17884         var checked = this.checkbox.checked;
17885         this.node.attributes.checked = checked;
17886         this.fireEvent('checkchange', this.node, checked);
17887     },
17888
17889     ecClick : function(e){
17890         if(!this.animating && this.node.hasChildNodes()){
17891             this.node.toggle();
17892         }
17893     },
17894
17895     startDrop : function(){
17896         this.dropping = true;
17897     },
17898
17899     // delayed drop so the click event doesn't get fired on a drop
17900     endDrop : function(){
17901        setTimeout(function(){
17902            this.dropping = false;
17903        }.createDelegate(this), 50);
17904     },
17905
17906     expand : function(){
17907         this.updateExpandIcon();
17908         this.ctNode.style.display = "";
17909     },
17910
17911     focus : function(){
17912         if(!this.node.preventHScroll){
17913             try{this.anchor.focus();
17914             }catch(e){}
17915         }else if(!Roo.isIE){
17916             try{
17917                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17918                 var l = noscroll.scrollLeft;
17919                 this.anchor.focus();
17920                 noscroll.scrollLeft = l;
17921             }catch(e){}
17922         }
17923     },
17924
17925     toggleCheck : function(value){
17926         var cb = this.checkbox;
17927         if(cb){
17928             cb.checked = (value === undefined ? !cb.checked : value);
17929         }
17930     },
17931
17932     blur : function(){
17933         try{
17934             this.anchor.blur();
17935         }catch(e){}
17936     },
17937
17938     animExpand : function(callback){
17939         var ct = Roo.get(this.ctNode);
17940         ct.stopFx();
17941         if(!this.node.hasChildNodes()){
17942             this.updateExpandIcon();
17943             this.ctNode.style.display = "";
17944             Roo.callback(callback);
17945             return;
17946         }
17947         this.animating = true;
17948         this.updateExpandIcon();
17949
17950         ct.slideIn('t', {
17951            callback : function(){
17952                this.animating = false;
17953                Roo.callback(callback);
17954             },
17955             scope: this,
17956             duration: this.node.ownerTree.duration || .25
17957         });
17958     },
17959
17960     highlight : function(){
17961         var tree = this.node.getOwnerTree();
17962         Roo.fly(this.wrap).highlight(
17963             tree.hlColor || "C3DAF9",
17964             {endColor: tree.hlBaseColor}
17965         );
17966     },
17967
17968     collapse : function(){
17969         this.updateExpandIcon();
17970         this.ctNode.style.display = "none";
17971     },
17972
17973     animCollapse : function(callback){
17974         var ct = Roo.get(this.ctNode);
17975         ct.enableDisplayMode('block');
17976         ct.stopFx();
17977
17978         this.animating = true;
17979         this.updateExpandIcon();
17980
17981         ct.slideOut('t', {
17982             callback : function(){
17983                this.animating = false;
17984                Roo.callback(callback);
17985             },
17986             scope: this,
17987             duration: this.node.ownerTree.duration || .25
17988         });
17989     },
17990
17991     getContainer : function(){
17992         return this.ctNode;
17993     },
17994
17995     getEl : function(){
17996         return this.wrap;
17997     },
17998
17999     appendDDGhost : function(ghostNode){
18000         ghostNode.appendChild(this.elNode.cloneNode(true));
18001     },
18002
18003     getDDRepairXY : function(){
18004         return Roo.lib.Dom.getXY(this.iconNode);
18005     },
18006
18007     onRender : function(){
18008         this.render();
18009     },
18010
18011     render : function(bulkRender){
18012         var n = this.node, a = n.attributes;
18013         var targetNode = n.parentNode ?
18014               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18015
18016         if(!this.rendered){
18017             this.rendered = true;
18018
18019             this.renderElements(n, a, targetNode, bulkRender);
18020
18021             if(a.qtip){
18022                if(this.textNode.setAttributeNS){
18023                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18024                    if(a.qtipTitle){
18025                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18026                    }
18027                }else{
18028                    this.textNode.setAttribute("ext:qtip", a.qtip);
18029                    if(a.qtipTitle){
18030                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18031                    }
18032                }
18033             }else if(a.qtipCfg){
18034                 a.qtipCfg.target = Roo.id(this.textNode);
18035                 Roo.QuickTips.register(a.qtipCfg);
18036             }
18037             this.initEvents();
18038             if(!this.node.expanded){
18039                 this.updateExpandIcon();
18040             }
18041         }else{
18042             if(bulkRender === true) {
18043                 targetNode.appendChild(this.wrap);
18044             }
18045         }
18046     },
18047
18048     renderElements : function(n, a, targetNode, bulkRender){
18049         // add some indent caching, this helps performance when rendering a large tree
18050         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18051         var t = n.getOwnerTree();
18052         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18053         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18054         var cb = typeof a.checked == 'boolean';
18055         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18056         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18057             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18058             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18059             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18060             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18061             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18062              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18063                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18064             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18065             "</li>"];
18066
18067         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18068             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18069                                 n.nextSibling.ui.getEl(), buf.join(""));
18070         }else{
18071             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18072         }
18073
18074         this.elNode = this.wrap.childNodes[0];
18075         this.ctNode = this.wrap.childNodes[1];
18076         var cs = this.elNode.childNodes;
18077         this.indentNode = cs[0];
18078         this.ecNode = cs[1];
18079         this.iconNode = cs[2];
18080         var index = 3;
18081         if(cb){
18082             this.checkbox = cs[3];
18083             index++;
18084         }
18085         this.anchor = cs[index];
18086         this.textNode = cs[index].firstChild;
18087     },
18088
18089     getAnchor : function(){
18090         return this.anchor;
18091     },
18092
18093     getTextEl : function(){
18094         return this.textNode;
18095     },
18096
18097     getIconEl : function(){
18098         return this.iconNode;
18099     },
18100
18101     isChecked : function(){
18102         return this.checkbox ? this.checkbox.checked : false;
18103     },
18104
18105     updateExpandIcon : function(){
18106         if(this.rendered){
18107             var n = this.node, c1, c2;
18108             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18109             var hasChild = n.hasChildNodes();
18110             if(hasChild){
18111                 if(n.expanded){
18112                     cls += "-minus";
18113                     c1 = "x-tree-node-collapsed";
18114                     c2 = "x-tree-node-expanded";
18115                 }else{
18116                     cls += "-plus";
18117                     c1 = "x-tree-node-expanded";
18118                     c2 = "x-tree-node-collapsed";
18119                 }
18120                 if(this.wasLeaf){
18121                     this.removeClass("x-tree-node-leaf");
18122                     this.wasLeaf = false;
18123                 }
18124                 if(this.c1 != c1 || this.c2 != c2){
18125                     Roo.fly(this.elNode).replaceClass(c1, c2);
18126                     this.c1 = c1; this.c2 = c2;
18127                 }
18128             }else{
18129                 if(!this.wasLeaf){
18130                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18131                     delete this.c1;
18132                     delete this.c2;
18133                     this.wasLeaf = true;
18134                 }
18135             }
18136             var ecc = "x-tree-ec-icon "+cls;
18137             if(this.ecc != ecc){
18138                 this.ecNode.className = ecc;
18139                 this.ecc = ecc;
18140             }
18141         }
18142     },
18143
18144     getChildIndent : function(){
18145         if(!this.childIndent){
18146             var buf = [];
18147             var p = this.node;
18148             while(p){
18149                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18150                     if(!p.isLast()) {
18151                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18152                     } else {
18153                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18154                     }
18155                 }
18156                 p = p.parentNode;
18157             }
18158             this.childIndent = buf.join("");
18159         }
18160         return this.childIndent;
18161     },
18162
18163     renderIndent : function(){
18164         if(this.rendered){
18165             var indent = "";
18166             var p = this.node.parentNode;
18167             if(p){
18168                 indent = p.ui.getChildIndent();
18169             }
18170             if(this.indentMarkup != indent){ // don't rerender if not required
18171                 this.indentNode.innerHTML = indent;
18172                 this.indentMarkup = indent;
18173             }
18174             this.updateExpandIcon();
18175         }
18176     }
18177 };
18178
18179 Roo.tree.RootTreeNodeUI = function(){
18180     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18181 };
18182 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18183     render : function(){
18184         if(!this.rendered){
18185             var targetNode = this.node.ownerTree.innerCt.dom;
18186             this.node.expanded = true;
18187             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18188             this.wrap = this.ctNode = targetNode.firstChild;
18189         }
18190     },
18191     collapse : function(){
18192     },
18193     expand : function(){
18194     }
18195 });/*
18196  * Based on:
18197  * Ext JS Library 1.1.1
18198  * Copyright(c) 2006-2007, Ext JS, LLC.
18199  *
18200  * Originally Released Under LGPL - original licence link has changed is not relivant.
18201  *
18202  * Fork - LGPL
18203  * <script type="text/javascript">
18204  */
18205 /**
18206  * @class Roo.tree.TreeLoader
18207  * @extends Roo.util.Observable
18208  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18209  * nodes from a specified URL. The response must be a javascript Array definition
18210  * who's elements are node definition objects. eg:
18211  * <pre><code>
18212    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18213     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18214 </code></pre>
18215  * <br><br>
18216  * A server request is sent, and child nodes are loaded only when a node is expanded.
18217  * The loading node's id is passed to the server under the parameter name "node" to
18218  * enable the server to produce the correct child nodes.
18219  * <br><br>
18220  * To pass extra parameters, an event handler may be attached to the "beforeload"
18221  * event, and the parameters specified in the TreeLoader's baseParams property:
18222  * <pre><code>
18223     myTreeLoader.on("beforeload", function(treeLoader, node) {
18224         this.baseParams.category = node.attributes.category;
18225     }, this);
18226 </code></pre><
18227  * This would pass an HTTP parameter called "category" to the server containing
18228  * the value of the Node's "category" attribute.
18229  * @constructor
18230  * Creates a new Treeloader.
18231  * @param {Object} config A config object containing config properties.
18232  */
18233 Roo.tree.TreeLoader = function(config){
18234     this.baseParams = {};
18235     this.requestMethod = "POST";
18236     Roo.apply(this, config);
18237
18238     this.addEvents({
18239     
18240         /**
18241          * @event beforeload
18242          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18243          * @param {Object} This TreeLoader object.
18244          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18245          * @param {Object} callback The callback function specified in the {@link #load} call.
18246          */
18247         beforeload : true,
18248         /**
18249          * @event load
18250          * Fires when the node has been successfuly loaded.
18251          * @param {Object} This TreeLoader object.
18252          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18253          * @param {Object} response The response object containing the data from the server.
18254          */
18255         load : true,
18256         /**
18257          * @event loadexception
18258          * Fires if the network request failed.
18259          * @param {Object} This TreeLoader object.
18260          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18261          * @param {Object} response The response object containing the data from the server.
18262          */
18263         loadexception : true,
18264         /**
18265          * @event create
18266          * Fires before a node is created, enabling you to return custom Node types 
18267          * @param {Object} This TreeLoader object.
18268          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18269          */
18270         create : true
18271     });
18272
18273     Roo.tree.TreeLoader.superclass.constructor.call(this);
18274 };
18275
18276 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18277     /**
18278     * @cfg {String} dataUrl The URL from which to request a Json string which
18279     * specifies an array of node definition object representing the child nodes
18280     * to be loaded.
18281     */
18282     /**
18283     * @cfg {Object} baseParams (optional) An object containing properties which
18284     * specify HTTP parameters to be passed to each request for child nodes.
18285     */
18286     /**
18287     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18288     * created by this loader. If the attributes sent by the server have an attribute in this object,
18289     * they take priority.
18290     */
18291     /**
18292     * @cfg {Object} uiProviders (optional) An object containing properties which
18293     * 
18294     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18295     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18296     * <i>uiProvider</i> attribute of a returned child node is a string rather
18297     * than a reference to a TreeNodeUI implementation, this that string value
18298     * is used as a property name in the uiProviders object. You can define the provider named
18299     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18300     */
18301     uiProviders : {},
18302
18303     /**
18304     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18305     * child nodes before loading.
18306     */
18307     clearOnLoad : true,
18308
18309     /**
18310     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18311     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18312     * Grid query { data : [ .....] }
18313     */
18314     
18315     root : false,
18316      /**
18317     * @cfg {String} queryParam (optional) 
18318     * Name of the query as it will be passed on the querystring (defaults to 'node')
18319     * eg. the request will be ?node=[id]
18320     */
18321     
18322     
18323     queryParam: false,
18324     
18325     /**
18326      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18327      * This is called automatically when a node is expanded, but may be used to reload
18328      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18329      * @param {Roo.tree.TreeNode} node
18330      * @param {Function} callback
18331      */
18332     load : function(node, callback){
18333         if(this.clearOnLoad){
18334             while(node.firstChild){
18335                 node.removeChild(node.firstChild);
18336             }
18337         }
18338         if(node.attributes.children){ // preloaded json children
18339             var cs = node.attributes.children;
18340             for(var i = 0, len = cs.length; i < len; i++){
18341                 node.appendChild(this.createNode(cs[i]));
18342             }
18343             if(typeof callback == "function"){
18344                 callback();
18345             }
18346         }else if(this.dataUrl){
18347             this.requestData(node, callback);
18348         }
18349     },
18350
18351     getParams: function(node){
18352         var buf = [], bp = this.baseParams;
18353         for(var key in bp){
18354             if(typeof bp[key] != "function"){
18355                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18356             }
18357         }
18358         var n = this.queryParam === false ? 'node' : this.queryParam;
18359         buf.push(n + "=", encodeURIComponent(node.id));
18360         return buf.join("");
18361     },
18362
18363     requestData : function(node, callback){
18364         if(this.fireEvent("beforeload", this, node, callback) !== false){
18365             this.transId = Roo.Ajax.request({
18366                 method:this.requestMethod,
18367                 url: this.dataUrl||this.url,
18368                 success: this.handleResponse,
18369                 failure: this.handleFailure,
18370                 scope: this,
18371                 argument: {callback: callback, node: node},
18372                 params: this.getParams(node)
18373             });
18374         }else{
18375             // if the load is cancelled, make sure we notify
18376             // the node that we are done
18377             if(typeof callback == "function"){
18378                 callback();
18379             }
18380         }
18381     },
18382
18383     isLoading : function(){
18384         return this.transId ? true : false;
18385     },
18386
18387     abort : function(){
18388         if(this.isLoading()){
18389             Roo.Ajax.abort(this.transId);
18390         }
18391     },
18392
18393     // private
18394     createNode : function(attr){
18395         // apply baseAttrs, nice idea Corey!
18396         if(this.baseAttrs){
18397             Roo.applyIf(attr, this.baseAttrs);
18398         }
18399         if(this.applyLoader !== false){
18400             attr.loader = this;
18401         }
18402         // uiProvider = depreciated..
18403         
18404         if(typeof(attr.uiProvider) == 'string'){
18405            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18406                 /**  eval:var:attr */ eval(attr.uiProvider);
18407         }
18408         if(typeof(this.uiProviders['default']) != 'undefined') {
18409             attr.uiProvider = this.uiProviders['default'];
18410         }
18411         
18412         this.fireEvent('create', this, attr);
18413         
18414         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18415         return(attr.leaf ?
18416                         new Roo.tree.TreeNode(attr) :
18417                         new Roo.tree.AsyncTreeNode(attr));
18418     },
18419
18420     processResponse : function(response, node, callback){
18421         var json = response.responseText;
18422         try {
18423             
18424             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18425             if (this.root !== false) {
18426                 o = o[this.root];
18427             }
18428             
18429             for(var i = 0, len = o.length; i < len; i++){
18430                 var n = this.createNode(o[i]);
18431                 if(n){
18432                     node.appendChild(n);
18433                 }
18434             }
18435             if(typeof callback == "function"){
18436                 callback(this, node);
18437             }
18438         }catch(e){
18439             this.handleFailure(response);
18440         }
18441     },
18442
18443     handleResponse : function(response){
18444         this.transId = false;
18445         var a = response.argument;
18446         this.processResponse(response, a.node, a.callback);
18447         this.fireEvent("load", this, a.node, response);
18448     },
18449
18450     handleFailure : function(response){
18451         this.transId = false;
18452         var a = response.argument;
18453         this.fireEvent("loadexception", this, a.node, response);
18454         if(typeof a.callback == "function"){
18455             a.callback(this, a.node);
18456         }
18457     }
18458 });/*
18459  * Based on:
18460  * Ext JS Library 1.1.1
18461  * Copyright(c) 2006-2007, Ext JS, LLC.
18462  *
18463  * Originally Released Under LGPL - original licence link has changed is not relivant.
18464  *
18465  * Fork - LGPL
18466  * <script type="text/javascript">
18467  */
18468
18469 /**
18470 * @class Roo.tree.TreeFilter
18471 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18472 * @param {TreePanel} tree
18473 * @param {Object} config (optional)
18474  */
18475 Roo.tree.TreeFilter = function(tree, config){
18476     this.tree = tree;
18477     this.filtered = {};
18478     Roo.apply(this, config);
18479 };
18480
18481 Roo.tree.TreeFilter.prototype = {
18482     clearBlank:false,
18483     reverse:false,
18484     autoClear:false,
18485     remove:false,
18486
18487      /**
18488      * Filter the data by a specific attribute.
18489      * @param {String/RegExp} value Either string that the attribute value
18490      * should start with or a RegExp to test against the attribute
18491      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18492      * @param {TreeNode} startNode (optional) The node to start the filter at.
18493      */
18494     filter : function(value, attr, startNode){
18495         attr = attr || "text";
18496         var f;
18497         if(typeof value == "string"){
18498             var vlen = value.length;
18499             // auto clear empty filter
18500             if(vlen == 0 && this.clearBlank){
18501                 this.clear();
18502                 return;
18503             }
18504             value = value.toLowerCase();
18505             f = function(n){
18506                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18507             };
18508         }else if(value.exec){ // regex?
18509             f = function(n){
18510                 return value.test(n.attributes[attr]);
18511             };
18512         }else{
18513             throw 'Illegal filter type, must be string or regex';
18514         }
18515         this.filterBy(f, null, startNode);
18516         },
18517
18518     /**
18519      * Filter by a function. The passed function will be called with each
18520      * node in the tree (or from the startNode). If the function returns true, the node is kept
18521      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18522      * @param {Function} fn The filter function
18523      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18524      */
18525     filterBy : function(fn, scope, startNode){
18526         startNode = startNode || this.tree.root;
18527         if(this.autoClear){
18528             this.clear();
18529         }
18530         var af = this.filtered, rv = this.reverse;
18531         var f = function(n){
18532             if(n == startNode){
18533                 return true;
18534             }
18535             if(af[n.id]){
18536                 return false;
18537             }
18538             var m = fn.call(scope || n, n);
18539             if(!m || rv){
18540                 af[n.id] = n;
18541                 n.ui.hide();
18542                 return false;
18543             }
18544             return true;
18545         };
18546         startNode.cascade(f);
18547         if(this.remove){
18548            for(var id in af){
18549                if(typeof id != "function"){
18550                    var n = af[id];
18551                    if(n && n.parentNode){
18552                        n.parentNode.removeChild(n);
18553                    }
18554                }
18555            }
18556         }
18557     },
18558
18559     /**
18560      * Clears the current filter. Note: with the "remove" option
18561      * set a filter cannot be cleared.
18562      */
18563     clear : function(){
18564         var t = this.tree;
18565         var af = this.filtered;
18566         for(var id in af){
18567             if(typeof id != "function"){
18568                 var n = af[id];
18569                 if(n){
18570                     n.ui.show();
18571                 }
18572             }
18573         }
18574         this.filtered = {};
18575     }
18576 };
18577 /*
18578  * Based on:
18579  * Ext JS Library 1.1.1
18580  * Copyright(c) 2006-2007, Ext JS, LLC.
18581  *
18582  * Originally Released Under LGPL - original licence link has changed is not relivant.
18583  *
18584  * Fork - LGPL
18585  * <script type="text/javascript">
18586  */
18587  
18588
18589 /**
18590  * @class Roo.tree.TreeSorter
18591  * Provides sorting of nodes in a TreePanel
18592  * 
18593  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18594  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18595  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18596  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18597  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18598  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18599  * @constructor
18600  * @param {TreePanel} tree
18601  * @param {Object} config
18602  */
18603 Roo.tree.TreeSorter = function(tree, config){
18604     Roo.apply(this, config);
18605     tree.on("beforechildrenrendered", this.doSort, this);
18606     tree.on("append", this.updateSort, this);
18607     tree.on("insert", this.updateSort, this);
18608     
18609     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18610     var p = this.property || "text";
18611     var sortType = this.sortType;
18612     var fs = this.folderSort;
18613     var cs = this.caseSensitive === true;
18614     var leafAttr = this.leafAttr || 'leaf';
18615
18616     this.sortFn = function(n1, n2){
18617         if(fs){
18618             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18619                 return 1;
18620             }
18621             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18622                 return -1;
18623             }
18624         }
18625         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18626         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18627         if(v1 < v2){
18628                         return dsc ? +1 : -1;
18629                 }else if(v1 > v2){
18630                         return dsc ? -1 : +1;
18631         }else{
18632                 return 0;
18633         }
18634     };
18635 };
18636
18637 Roo.tree.TreeSorter.prototype = {
18638     doSort : function(node){
18639         node.sort(this.sortFn);
18640     },
18641     
18642     compareNodes : function(n1, n2){
18643         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18644     },
18645     
18646     updateSort : function(tree, node){
18647         if(node.childrenRendered){
18648             this.doSort.defer(1, this, [node]);
18649         }
18650     }
18651 };/*
18652  * Based on:
18653  * Ext JS Library 1.1.1
18654  * Copyright(c) 2006-2007, Ext JS, LLC.
18655  *
18656  * Originally Released Under LGPL - original licence link has changed is not relivant.
18657  *
18658  * Fork - LGPL
18659  * <script type="text/javascript">
18660  */
18661
18662 if(Roo.dd.DropZone){
18663     
18664 Roo.tree.TreeDropZone = function(tree, config){
18665     this.allowParentInsert = false;
18666     this.allowContainerDrop = false;
18667     this.appendOnly = false;
18668     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18669     this.tree = tree;
18670     this.lastInsertClass = "x-tree-no-status";
18671     this.dragOverData = {};
18672 };
18673
18674 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18675     ddGroup : "TreeDD",
18676     
18677     expandDelay : 1000,
18678     
18679     expandNode : function(node){
18680         if(node.hasChildNodes() && !node.isExpanded()){
18681             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18682         }
18683     },
18684     
18685     queueExpand : function(node){
18686         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18687     },
18688     
18689     cancelExpand : function(){
18690         if(this.expandProcId){
18691             clearTimeout(this.expandProcId);
18692             this.expandProcId = false;
18693         }
18694     },
18695     
18696     isValidDropPoint : function(n, pt, dd, e, data){
18697         if(!n || !data){ return false; }
18698         var targetNode = n.node;
18699         var dropNode = data.node;
18700         // default drop rules
18701         if(!(targetNode && targetNode.isTarget && pt)){
18702             return false;
18703         }
18704         if(pt == "append" && targetNode.allowChildren === false){
18705             return false;
18706         }
18707         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18708             return false;
18709         }
18710         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18711             return false;
18712         }
18713         // reuse the object
18714         var overEvent = this.dragOverData;
18715         overEvent.tree = this.tree;
18716         overEvent.target = targetNode;
18717         overEvent.data = data;
18718         overEvent.point = pt;
18719         overEvent.source = dd;
18720         overEvent.rawEvent = e;
18721         overEvent.dropNode = dropNode;
18722         overEvent.cancel = false;  
18723         var result = this.tree.fireEvent("nodedragover", overEvent);
18724         return overEvent.cancel === false && result !== false;
18725     },
18726     
18727     getDropPoint : function(e, n, dd){
18728         var tn = n.node;
18729         if(tn.isRoot){
18730             return tn.allowChildren !== false ? "append" : false; // always append for root
18731         }
18732         var dragEl = n.ddel;
18733         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18734         var y = Roo.lib.Event.getPageY(e);
18735         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18736         
18737         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18738         var noAppend = tn.allowChildren === false;
18739         if(this.appendOnly || tn.parentNode.allowChildren === false){
18740             return noAppend ? false : "append";
18741         }
18742         var noBelow = false;
18743         if(!this.allowParentInsert){
18744             noBelow = tn.hasChildNodes() && tn.isExpanded();
18745         }
18746         var q = (b - t) / (noAppend ? 2 : 3);
18747         if(y >= t && y < (t + q)){
18748             return "above";
18749         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18750             return "below";
18751         }else{
18752             return "append";
18753         }
18754     },
18755     
18756     onNodeEnter : function(n, dd, e, data){
18757         this.cancelExpand();
18758     },
18759     
18760     onNodeOver : function(n, dd, e, data){
18761         var pt = this.getDropPoint(e, n, dd);
18762         var node = n.node;
18763         
18764         // auto node expand check
18765         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18766             this.queueExpand(node);
18767         }else if(pt != "append"){
18768             this.cancelExpand();
18769         }
18770         
18771         // set the insert point style on the target node
18772         var returnCls = this.dropNotAllowed;
18773         if(this.isValidDropPoint(n, pt, dd, e, data)){
18774            if(pt){
18775                var el = n.ddel;
18776                var cls;
18777                if(pt == "above"){
18778                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18779                    cls = "x-tree-drag-insert-above";
18780                }else if(pt == "below"){
18781                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18782                    cls = "x-tree-drag-insert-below";
18783                }else{
18784                    returnCls = "x-tree-drop-ok-append";
18785                    cls = "x-tree-drag-append";
18786                }
18787                if(this.lastInsertClass != cls){
18788                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18789                    this.lastInsertClass = cls;
18790                }
18791            }
18792        }
18793        return returnCls;
18794     },
18795     
18796     onNodeOut : function(n, dd, e, data){
18797         this.cancelExpand();
18798         this.removeDropIndicators(n);
18799     },
18800     
18801     onNodeDrop : function(n, dd, e, data){
18802         var point = this.getDropPoint(e, n, dd);
18803         var targetNode = n.node;
18804         targetNode.ui.startDrop();
18805         if(!this.isValidDropPoint(n, point, dd, e, data)){
18806             targetNode.ui.endDrop();
18807             return false;
18808         }
18809         // first try to find the drop node
18810         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18811         var dropEvent = {
18812             tree : this.tree,
18813             target: targetNode,
18814             data: data,
18815             point: point,
18816             source: dd,
18817             rawEvent: e,
18818             dropNode: dropNode,
18819             cancel: !dropNode   
18820         };
18821         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18822         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18823             targetNode.ui.endDrop();
18824             return false;
18825         }
18826         // allow target changing
18827         targetNode = dropEvent.target;
18828         if(point == "append" && !targetNode.isExpanded()){
18829             targetNode.expand(false, null, function(){
18830                 this.completeDrop(dropEvent);
18831             }.createDelegate(this));
18832         }else{
18833             this.completeDrop(dropEvent);
18834         }
18835         return true;
18836     },
18837     
18838     completeDrop : function(de){
18839         var ns = de.dropNode, p = de.point, t = de.target;
18840         if(!(ns instanceof Array)){
18841             ns = [ns];
18842         }
18843         var n;
18844         for(var i = 0, len = ns.length; i < len; i++){
18845             n = ns[i];
18846             if(p == "above"){
18847                 t.parentNode.insertBefore(n, t);
18848             }else if(p == "below"){
18849                 t.parentNode.insertBefore(n, t.nextSibling);
18850             }else{
18851                 t.appendChild(n);
18852             }
18853         }
18854         n.ui.focus();
18855         if(this.tree.hlDrop){
18856             n.ui.highlight();
18857         }
18858         t.ui.endDrop();
18859         this.tree.fireEvent("nodedrop", de);
18860     },
18861     
18862     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18863         if(this.tree.hlDrop){
18864             dropNode.ui.focus();
18865             dropNode.ui.highlight();
18866         }
18867         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18868     },
18869     
18870     getTree : function(){
18871         return this.tree;
18872     },
18873     
18874     removeDropIndicators : function(n){
18875         if(n && n.ddel){
18876             var el = n.ddel;
18877             Roo.fly(el).removeClass([
18878                     "x-tree-drag-insert-above",
18879                     "x-tree-drag-insert-below",
18880                     "x-tree-drag-append"]);
18881             this.lastInsertClass = "_noclass";
18882         }
18883     },
18884     
18885     beforeDragDrop : function(target, e, id){
18886         this.cancelExpand();
18887         return true;
18888     },
18889     
18890     afterRepair : function(data){
18891         if(data && Roo.enableFx){
18892             data.node.ui.highlight();
18893         }
18894         this.hideProxy();
18895     }    
18896 });
18897
18898 }
18899 /*
18900  * Based on:
18901  * Ext JS Library 1.1.1
18902  * Copyright(c) 2006-2007, Ext JS, LLC.
18903  *
18904  * Originally Released Under LGPL - original licence link has changed is not relivant.
18905  *
18906  * Fork - LGPL
18907  * <script type="text/javascript">
18908  */
18909  
18910
18911 if(Roo.dd.DragZone){
18912 Roo.tree.TreeDragZone = function(tree, config){
18913     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18914     this.tree = tree;
18915 };
18916
18917 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18918     ddGroup : "TreeDD",
18919     
18920     onBeforeDrag : function(data, e){
18921         var n = data.node;
18922         return n && n.draggable && !n.disabled;
18923     },
18924     
18925     onInitDrag : function(e){
18926         var data = this.dragData;
18927         this.tree.getSelectionModel().select(data.node);
18928         this.proxy.update("");
18929         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18930         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18931     },
18932     
18933     getRepairXY : function(e, data){
18934         return data.node.ui.getDDRepairXY();
18935     },
18936     
18937     onEndDrag : function(data, e){
18938         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18939     },
18940     
18941     onValidDrop : function(dd, e, id){
18942         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18943         this.hideProxy();
18944     },
18945     
18946     beforeInvalidDrop : function(e, id){
18947         // this scrolls the original position back into view
18948         var sm = this.tree.getSelectionModel();
18949         sm.clearSelections();
18950         sm.select(this.dragData.node);
18951     }
18952 });
18953 }/*
18954  * Based on:
18955  * Ext JS Library 1.1.1
18956  * Copyright(c) 2006-2007, Ext JS, LLC.
18957  *
18958  * Originally Released Under LGPL - original licence link has changed is not relivant.
18959  *
18960  * Fork - LGPL
18961  * <script type="text/javascript">
18962  */
18963 /**
18964  * @class Roo.tree.TreeEditor
18965  * @extends Roo.Editor
18966  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18967  * as the editor field.
18968  * @constructor
18969  * @param {TreePanel} tree
18970  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18971  */
18972 Roo.tree.TreeEditor = function(tree, config){
18973     config = config || {};
18974     var field = config.events ? config : new Roo.form.TextField(config);
18975     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18976
18977     this.tree = tree;
18978
18979     tree.on('beforeclick', this.beforeNodeClick, this);
18980     tree.getTreeEl().on('mousedown', this.hide, this);
18981     this.on('complete', this.updateNode, this);
18982     this.on('beforestartedit', this.fitToTree, this);
18983     this.on('startedit', this.bindScroll, this, {delay:10});
18984     this.on('specialkey', this.onSpecialKey, this);
18985 };
18986
18987 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18988     /**
18989      * @cfg {String} alignment
18990      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18991      */
18992     alignment: "l-l",
18993     // inherit
18994     autoSize: false,
18995     /**
18996      * @cfg {Boolean} hideEl
18997      * True to hide the bound element while the editor is displayed (defaults to false)
18998      */
18999     hideEl : false,
19000     /**
19001      * @cfg {String} cls
19002      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19003      */
19004     cls: "x-small-editor x-tree-editor",
19005     /**
19006      * @cfg {Boolean} shim
19007      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19008      */
19009     shim:false,
19010     // inherit
19011     shadow:"frame",
19012     /**
19013      * @cfg {Number} maxWidth
19014      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19015      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19016      * scroll and client offsets into account prior to each edit.
19017      */
19018     maxWidth: 250,
19019
19020     editDelay : 350,
19021
19022     // private
19023     fitToTree : function(ed, el){
19024         var td = this.tree.getTreeEl().dom, nd = el.dom;
19025         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19026             td.scrollLeft = nd.offsetLeft;
19027         }
19028         var w = Math.min(
19029                 this.maxWidth,
19030                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19031         this.setSize(w, '');
19032     },
19033
19034     // private
19035     triggerEdit : function(node){
19036         this.completeEdit();
19037         this.editNode = node;
19038         this.startEdit(node.ui.textNode, node.text);
19039     },
19040
19041     // private
19042     bindScroll : function(){
19043         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19044     },
19045
19046     // private
19047     beforeNodeClick : function(node, e){
19048         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19049         this.lastClick = new Date();
19050         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19051             e.stopEvent();
19052             this.triggerEdit(node);
19053             return false;
19054         }
19055     },
19056
19057     // private
19058     updateNode : function(ed, value){
19059         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19060         this.editNode.setText(value);
19061     },
19062
19063     // private
19064     onHide : function(){
19065         Roo.tree.TreeEditor.superclass.onHide.call(this);
19066         if(this.editNode){
19067             this.editNode.ui.focus();
19068         }
19069     },
19070
19071     // private
19072     onSpecialKey : function(field, e){
19073         var k = e.getKey();
19074         if(k == e.ESC){
19075             e.stopEvent();
19076             this.cancelEdit();
19077         }else if(k == e.ENTER && !e.hasModifier()){
19078             e.stopEvent();
19079             this.completeEdit();
19080         }
19081     }
19082 });//<Script type="text/javascript">
19083 /*
19084  * Based on:
19085  * Ext JS Library 1.1.1
19086  * Copyright(c) 2006-2007, Ext JS, LLC.
19087  *
19088  * Originally Released Under LGPL - original licence link has changed is not relivant.
19089  *
19090  * Fork - LGPL
19091  * <script type="text/javascript">
19092  */
19093  
19094 /**
19095  * Not documented??? - probably should be...
19096  */
19097
19098 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19099     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19100     
19101     renderElements : function(n, a, targetNode, bulkRender){
19102         //consel.log("renderElements?");
19103         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19104
19105         var t = n.getOwnerTree();
19106         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19107         
19108         var cols = t.columns;
19109         var bw = t.borderWidth;
19110         var c = cols[0];
19111         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19112          var cb = typeof a.checked == "boolean";
19113         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19114         var colcls = 'x-t-' + tid + '-c0';
19115         var buf = [
19116             '<li class="x-tree-node">',
19117             
19118                 
19119                 '<div class="x-tree-node-el ', a.cls,'">',
19120                     // extran...
19121                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19122                 
19123                 
19124                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19125                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19126                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19127                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19128                            (a.iconCls ? ' '+a.iconCls : ''),
19129                            '" unselectable="on" />',
19130                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19131                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19132                              
19133                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19134                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19135                             '<span unselectable="on" qtip="' + tx + '">',
19136                              tx,
19137                              '</span></a>' ,
19138                     '</div>',
19139                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19140                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19141                  ];
19142         for(var i = 1, len = cols.length; i < len; i++){
19143             c = cols[i];
19144             colcls = 'x-t-' + tid + '-c' +i;
19145             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19146             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19147                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19148                       "</div>");
19149          }
19150          
19151          buf.push(
19152             '</a>',
19153             '<div class="x-clear"></div></div>',
19154             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19155             "</li>");
19156         
19157         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19158             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19159                                 n.nextSibling.ui.getEl(), buf.join(""));
19160         }else{
19161             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19162         }
19163         var el = this.wrap.firstChild;
19164         this.elRow = el;
19165         this.elNode = el.firstChild;
19166         this.ranchor = el.childNodes[1];
19167         this.ctNode = this.wrap.childNodes[1];
19168         var cs = el.firstChild.childNodes;
19169         this.indentNode = cs[0];
19170         this.ecNode = cs[1];
19171         this.iconNode = cs[2];
19172         var index = 3;
19173         if(cb){
19174             this.checkbox = cs[3];
19175             index++;
19176         }
19177         this.anchor = cs[index];
19178         
19179         this.textNode = cs[index].firstChild;
19180         
19181         //el.on("click", this.onClick, this);
19182         //el.on("dblclick", this.onDblClick, this);
19183         
19184         
19185        // console.log(this);
19186     },
19187     initEvents : function(){
19188         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19189         
19190             
19191         var a = this.ranchor;
19192
19193         var el = Roo.get(a);
19194
19195         if(Roo.isOpera){ // opera render bug ignores the CSS
19196             el.setStyle("text-decoration", "none");
19197         }
19198
19199         el.on("click", this.onClick, this);
19200         el.on("dblclick", this.onDblClick, this);
19201         el.on("contextmenu", this.onContextMenu, this);
19202         
19203     },
19204     
19205     /*onSelectedChange : function(state){
19206         if(state){
19207             this.focus();
19208             this.addClass("x-tree-selected");
19209         }else{
19210             //this.blur();
19211             this.removeClass("x-tree-selected");
19212         }
19213     },*/
19214     addClass : function(cls){
19215         if(this.elRow){
19216             Roo.fly(this.elRow).addClass(cls);
19217         }
19218         
19219     },
19220     
19221     
19222     removeClass : function(cls){
19223         if(this.elRow){
19224             Roo.fly(this.elRow).removeClass(cls);
19225         }
19226     }
19227
19228     
19229     
19230 });//<Script type="text/javascript">
19231
19232 /*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242  
19243
19244 /**
19245  * @class Roo.tree.ColumnTree
19246  * @extends Roo.data.TreePanel
19247  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19248  * @cfg {int} borderWidth  compined right/left border allowance
19249  * @constructor
19250  * @param {String/HTMLElement/Element} el The container element
19251  * @param {Object} config
19252  */
19253 Roo.tree.ColumnTree =  function(el, config)
19254 {
19255    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19256    this.addEvents({
19257         /**
19258         * @event resize
19259         * Fire this event on a container when it resizes
19260         * @param {int} w Width
19261         * @param {int} h Height
19262         */
19263        "resize" : true
19264     });
19265     this.on('resize', this.onResize, this);
19266 };
19267
19268 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19269     //lines:false,
19270     
19271     
19272     borderWidth: Roo.isBorderBox ? 0 : 2, 
19273     headEls : false,
19274     
19275     render : function(){
19276         // add the header.....
19277        
19278         Roo.tree.ColumnTree.superclass.render.apply(this);
19279         
19280         this.el.addClass('x-column-tree');
19281         
19282         this.headers = this.el.createChild(
19283             {cls:'x-tree-headers'},this.innerCt.dom);
19284    
19285         var cols = this.columns, c;
19286         var totalWidth = 0;
19287         this.headEls = [];
19288         var  len = cols.length;
19289         for(var i = 0; i < len; i++){
19290              c = cols[i];
19291              totalWidth += c.width;
19292             this.headEls.push(this.headers.createChild({
19293                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19294                  cn: {
19295                      cls:'x-tree-hd-text',
19296                      html: c.header
19297                  },
19298                  style:'width:'+(c.width-this.borderWidth)+'px;'
19299              }));
19300         }
19301         this.headers.createChild({cls:'x-clear'});
19302         // prevent floats from wrapping when clipped
19303         this.headers.setWidth(totalWidth);
19304         //this.innerCt.setWidth(totalWidth);
19305         this.innerCt.setStyle({ overflow: 'auto' });
19306         this.onResize(this.width, this.height);
19307              
19308         
19309     },
19310     onResize : function(w,h)
19311     {
19312         this.height = h;
19313         this.width = w;
19314         // resize cols..
19315         this.innerCt.setWidth(this.width);
19316         this.innerCt.setHeight(this.height-20);
19317         
19318         // headers...
19319         var cols = this.columns, c;
19320         var totalWidth = 0;
19321         var expEl = false;
19322         var len = cols.length;
19323         for(var i = 0; i < len; i++){
19324             c = cols[i];
19325             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19326                 // it's the expander..
19327                 expEl  = this.headEls[i];
19328                 continue;
19329             }
19330             totalWidth += c.width;
19331             
19332         }
19333         if (expEl) {
19334             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19335         }
19336         this.headers.setWidth(w-20);
19337
19338         
19339         
19340         
19341     }
19342 });
19343 /*
19344  * Based on:
19345  * Ext JS Library 1.1.1
19346  * Copyright(c) 2006-2007, Ext JS, LLC.
19347  *
19348  * Originally Released Under LGPL - original licence link has changed is not relivant.
19349  *
19350  * Fork - LGPL
19351  * <script type="text/javascript">
19352  */
19353  
19354 /**
19355  * @class Roo.menu.Menu
19356  * @extends Roo.util.Observable
19357  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19358  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19359  * @constructor
19360  * Creates a new Menu
19361  * @param {Object} config Configuration options
19362  */
19363 Roo.menu.Menu = function(config){
19364     Roo.apply(this, config);
19365     this.id = this.id || Roo.id();
19366     this.addEvents({
19367         /**
19368          * @event beforeshow
19369          * Fires before this menu is displayed
19370          * @param {Roo.menu.Menu} this
19371          */
19372         beforeshow : true,
19373         /**
19374          * @event beforehide
19375          * Fires before this menu is hidden
19376          * @param {Roo.menu.Menu} this
19377          */
19378         beforehide : true,
19379         /**
19380          * @event show
19381          * Fires after this menu is displayed
19382          * @param {Roo.menu.Menu} this
19383          */
19384         show : true,
19385         /**
19386          * @event hide
19387          * Fires after this menu is hidden
19388          * @param {Roo.menu.Menu} this
19389          */
19390         hide : true,
19391         /**
19392          * @event click
19393          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19394          * @param {Roo.menu.Menu} this
19395          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19396          * @param {Roo.EventObject} e
19397          */
19398         click : true,
19399         /**
19400          * @event mouseover
19401          * Fires when the mouse is hovering over this menu
19402          * @param {Roo.menu.Menu} this
19403          * @param {Roo.EventObject} e
19404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19405          */
19406         mouseover : true,
19407         /**
19408          * @event mouseout
19409          * Fires when the mouse exits this menu
19410          * @param {Roo.menu.Menu} this
19411          * @param {Roo.EventObject} e
19412          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19413          */
19414         mouseout : true,
19415         /**
19416          * @event itemclick
19417          * Fires when a menu item contained in this menu is clicked
19418          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19419          * @param {Roo.EventObject} e
19420          */
19421         itemclick: true
19422     });
19423     if (this.registerMenu) {
19424         Roo.menu.MenuMgr.register(this);
19425     }
19426     
19427     var mis = this.items;
19428     this.items = new Roo.util.MixedCollection();
19429     if(mis){
19430         this.add.apply(this, mis);
19431     }
19432 };
19433
19434 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19435     /**
19436      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19437      */
19438     minWidth : 120,
19439     /**
19440      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19441      * for bottom-right shadow (defaults to "sides")
19442      */
19443     shadow : "sides",
19444     /**
19445      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19446      * this menu (defaults to "tl-tr?")
19447      */
19448     subMenuAlign : "tl-tr?",
19449     /**
19450      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19451      * relative to its element of origin (defaults to "tl-bl?")
19452      */
19453     defaultAlign : "tl-bl?",
19454     /**
19455      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19456      */
19457     allowOtherMenus : false,
19458     /**
19459      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19460      */
19461     registerMenu : true,
19462
19463     hidden:true,
19464
19465     // private
19466     render : function(){
19467         if(this.el){
19468             return;
19469         }
19470         var el = this.el = new Roo.Layer({
19471             cls: "x-menu",
19472             shadow:this.shadow,
19473             constrain: false,
19474             parentEl: this.parentEl || document.body,
19475             zindex:15000
19476         });
19477
19478         this.keyNav = new Roo.menu.MenuNav(this);
19479
19480         if(this.plain){
19481             el.addClass("x-menu-plain");
19482         }
19483         if(this.cls){
19484             el.addClass(this.cls);
19485         }
19486         // generic focus element
19487         this.focusEl = el.createChild({
19488             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19489         });
19490         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19491         ul.on("click", this.onClick, this);
19492         ul.on("mouseover", this.onMouseOver, this);
19493         ul.on("mouseout", this.onMouseOut, this);
19494         this.items.each(function(item){
19495             var li = document.createElement("li");
19496             li.className = "x-menu-list-item";
19497             ul.dom.appendChild(li);
19498             item.render(li, this);
19499         }, this);
19500         this.ul = ul;
19501         this.autoWidth();
19502     },
19503
19504     // private
19505     autoWidth : function(){
19506         var el = this.el, ul = this.ul;
19507         if(!el){
19508             return;
19509         }
19510         var w = this.width;
19511         if(w){
19512             el.setWidth(w);
19513         }else if(Roo.isIE){
19514             el.setWidth(this.minWidth);
19515             var t = el.dom.offsetWidth; // force recalc
19516             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19517         }
19518     },
19519
19520     // private
19521     delayAutoWidth : function(){
19522         if(this.rendered){
19523             if(!this.awTask){
19524                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19525             }
19526             this.awTask.delay(20);
19527         }
19528     },
19529
19530     // private
19531     findTargetItem : function(e){
19532         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19533         if(t && t.menuItemId){
19534             return this.items.get(t.menuItemId);
19535         }
19536     },
19537
19538     // private
19539     onClick : function(e){
19540         var t;
19541         if(t = this.findTargetItem(e)){
19542             t.onClick(e);
19543             this.fireEvent("click", this, t, e);
19544         }
19545     },
19546
19547     // private
19548     setActiveItem : function(item, autoExpand){
19549         if(item != this.activeItem){
19550             if(this.activeItem){
19551                 this.activeItem.deactivate();
19552             }
19553             this.activeItem = item;
19554             item.activate(autoExpand);
19555         }else if(autoExpand){
19556             item.expandMenu();
19557         }
19558     },
19559
19560     // private
19561     tryActivate : function(start, step){
19562         var items = this.items;
19563         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19564             var item = items.get(i);
19565             if(!item.disabled && item.canActivate){
19566                 this.setActiveItem(item, false);
19567                 return item;
19568             }
19569         }
19570         return false;
19571     },
19572
19573     // private
19574     onMouseOver : function(e){
19575         var t;
19576         if(t = this.findTargetItem(e)){
19577             if(t.canActivate && !t.disabled){
19578                 this.setActiveItem(t, true);
19579             }
19580         }
19581         this.fireEvent("mouseover", this, e, t);
19582     },
19583
19584     // private
19585     onMouseOut : function(e){
19586         var t;
19587         if(t = this.findTargetItem(e)){
19588             if(t == this.activeItem && t.shouldDeactivate(e)){
19589                 this.activeItem.deactivate();
19590                 delete this.activeItem;
19591             }
19592         }
19593         this.fireEvent("mouseout", this, e, t);
19594     },
19595
19596     /**
19597      * Read-only.  Returns true if the menu is currently displayed, else false.
19598      * @type Boolean
19599      */
19600     isVisible : function(){
19601         return this.el && !this.hidden;
19602     },
19603
19604     /**
19605      * Displays this menu relative to another element
19606      * @param {String/HTMLElement/Roo.Element} element The element to align to
19607      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19608      * the element (defaults to this.defaultAlign)
19609      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19610      */
19611     show : function(el, pos, parentMenu){
19612         this.parentMenu = parentMenu;
19613         if(!this.el){
19614             this.render();
19615         }
19616         this.fireEvent("beforeshow", this);
19617         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19618     },
19619
19620     /**
19621      * Displays this menu at a specific xy position
19622      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19623      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19624      */
19625     showAt : function(xy, parentMenu, /* private: */_e){
19626         this.parentMenu = parentMenu;
19627         if(!this.el){
19628             this.render();
19629         }
19630         if(_e !== false){
19631             this.fireEvent("beforeshow", this);
19632             xy = this.el.adjustForConstraints(xy);
19633         }
19634         this.el.setXY(xy);
19635         this.el.show();
19636         this.hidden = false;
19637         this.focus();
19638         this.fireEvent("show", this);
19639     },
19640
19641     focus : function(){
19642         if(!this.hidden){
19643             this.doFocus.defer(50, this);
19644         }
19645     },
19646
19647     doFocus : function(){
19648         if(!this.hidden){
19649             this.focusEl.focus();
19650         }
19651     },
19652
19653     /**
19654      * Hides this menu and optionally all parent menus
19655      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19656      */
19657     hide : function(deep){
19658         if(this.el && this.isVisible()){
19659             this.fireEvent("beforehide", this);
19660             if(this.activeItem){
19661                 this.activeItem.deactivate();
19662                 this.activeItem = null;
19663             }
19664             this.el.hide();
19665             this.hidden = true;
19666             this.fireEvent("hide", this);
19667         }
19668         if(deep === true && this.parentMenu){
19669             this.parentMenu.hide(true);
19670         }
19671     },
19672
19673     /**
19674      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19675      * Any of the following are valid:
19676      * <ul>
19677      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19678      * <li>An HTMLElement object which will be converted to a menu item</li>
19679      * <li>A menu item config object that will be created as a new menu item</li>
19680      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19681      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19682      * </ul>
19683      * Usage:
19684      * <pre><code>
19685 // Create the menu
19686 var menu = new Roo.menu.Menu();
19687
19688 // Create a menu item to add by reference
19689 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19690
19691 // Add a bunch of items at once using different methods.
19692 // Only the last item added will be returned.
19693 var item = menu.add(
19694     menuItem,                // add existing item by ref
19695     'Dynamic Item',          // new TextItem
19696     '-',                     // new separator
19697     { text: 'Config Item' }  // new item by config
19698 );
19699 </code></pre>
19700      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19701      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19702      */
19703     add : function(){
19704         var a = arguments, l = a.length, item;
19705         for(var i = 0; i < l; i++){
19706             var el = a[i];
19707             if ((typeof(el) == "object") && el.xtype && el.xns) {
19708                 el = Roo.factory(el, Roo.menu);
19709             }
19710             
19711             if(el.render){ // some kind of Item
19712                 item = this.addItem(el);
19713             }else if(typeof el == "string"){ // string
19714                 if(el == "separator" || el == "-"){
19715                     item = this.addSeparator();
19716                 }else{
19717                     item = this.addText(el);
19718                 }
19719             }else if(el.tagName || el.el){ // element
19720                 item = this.addElement(el);
19721             }else if(typeof el == "object"){ // must be menu item config?
19722                 item = this.addMenuItem(el);
19723             }
19724         }
19725         return item;
19726     },
19727
19728     /**
19729      * Returns this menu's underlying {@link Roo.Element} object
19730      * @return {Roo.Element} The element
19731      */
19732     getEl : function(){
19733         if(!this.el){
19734             this.render();
19735         }
19736         return this.el;
19737     },
19738
19739     /**
19740      * Adds a separator bar to the menu
19741      * @return {Roo.menu.Item} The menu item that was added
19742      */
19743     addSeparator : function(){
19744         return this.addItem(new Roo.menu.Separator());
19745     },
19746
19747     /**
19748      * Adds an {@link Roo.Element} object to the menu
19749      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19750      * @return {Roo.menu.Item} The menu item that was added
19751      */
19752     addElement : function(el){
19753         return this.addItem(new Roo.menu.BaseItem(el));
19754     },
19755
19756     /**
19757      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19758      * @param {Roo.menu.Item} item The menu item to add
19759      * @return {Roo.menu.Item} The menu item that was added
19760      */
19761     addItem : function(item){
19762         this.items.add(item);
19763         if(this.ul){
19764             var li = document.createElement("li");
19765             li.className = "x-menu-list-item";
19766             this.ul.dom.appendChild(li);
19767             item.render(li, this);
19768             this.delayAutoWidth();
19769         }
19770         return item;
19771     },
19772
19773     /**
19774      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19775      * @param {Object} config A MenuItem config object
19776      * @return {Roo.menu.Item} The menu item that was added
19777      */
19778     addMenuItem : function(config){
19779         if(!(config instanceof Roo.menu.Item)){
19780             if(typeof config.checked == "boolean"){ // must be check menu item config?
19781                 config = new Roo.menu.CheckItem(config);
19782             }else{
19783                 config = new Roo.menu.Item(config);
19784             }
19785         }
19786         return this.addItem(config);
19787     },
19788
19789     /**
19790      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19791      * @param {String} text The text to display in the menu item
19792      * @return {Roo.menu.Item} The menu item that was added
19793      */
19794     addText : function(text){
19795         return this.addItem(new Roo.menu.TextItem({ text : text }));
19796     },
19797
19798     /**
19799      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19800      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19801      * @param {Roo.menu.Item} item The menu item to add
19802      * @return {Roo.menu.Item} The menu item that was added
19803      */
19804     insert : function(index, item){
19805         this.items.insert(index, item);
19806         if(this.ul){
19807             var li = document.createElement("li");
19808             li.className = "x-menu-list-item";
19809             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19810             item.render(li, this);
19811             this.delayAutoWidth();
19812         }
19813         return item;
19814     },
19815
19816     /**
19817      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19818      * @param {Roo.menu.Item} item The menu item to remove
19819      */
19820     remove : function(item){
19821         this.items.removeKey(item.id);
19822         item.destroy();
19823     },
19824
19825     /**
19826      * Removes and destroys all items in the menu
19827      */
19828     removeAll : function(){
19829         var f;
19830         while(f = this.items.first()){
19831             this.remove(f);
19832         }
19833     }
19834 });
19835
19836 // MenuNav is a private utility class used internally by the Menu
19837 Roo.menu.MenuNav = function(menu){
19838     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19839     this.scope = this.menu = menu;
19840 };
19841
19842 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19843     doRelay : function(e, h){
19844         var k = e.getKey();
19845         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19846             this.menu.tryActivate(0, 1);
19847             return false;
19848         }
19849         return h.call(this.scope || this, e, this.menu);
19850     },
19851
19852     up : function(e, m){
19853         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19854             m.tryActivate(m.items.length-1, -1);
19855         }
19856     },
19857
19858     down : function(e, m){
19859         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19860             m.tryActivate(0, 1);
19861         }
19862     },
19863
19864     right : function(e, m){
19865         if(m.activeItem){
19866             m.activeItem.expandMenu(true);
19867         }
19868     },
19869
19870     left : function(e, m){
19871         m.hide();
19872         if(m.parentMenu && m.parentMenu.activeItem){
19873             m.parentMenu.activeItem.activate();
19874         }
19875     },
19876
19877     enter : function(e, m){
19878         if(m.activeItem){
19879             e.stopPropagation();
19880             m.activeItem.onClick(e);
19881             m.fireEvent("click", this, m.activeItem);
19882             return true;
19883         }
19884     }
19885 });/*
19886  * Based on:
19887  * Ext JS Library 1.1.1
19888  * Copyright(c) 2006-2007, Ext JS, LLC.
19889  *
19890  * Originally Released Under LGPL - original licence link has changed is not relivant.
19891  *
19892  * Fork - LGPL
19893  * <script type="text/javascript">
19894  */
19895  
19896 /**
19897  * @class Roo.menu.MenuMgr
19898  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19899  * @singleton
19900  */
19901 Roo.menu.MenuMgr = function(){
19902    var menus, active, groups = {}, attached = false, lastShow = new Date();
19903
19904    // private - called when first menu is created
19905    function init(){
19906        menus = {};
19907        active = new Roo.util.MixedCollection();
19908        Roo.get(document).addKeyListener(27, function(){
19909            if(active.length > 0){
19910                hideAll();
19911            }
19912        });
19913    }
19914
19915    // private
19916    function hideAll(){
19917        if(active && active.length > 0){
19918            var c = active.clone();
19919            c.each(function(m){
19920                m.hide();
19921            });
19922        }
19923    }
19924
19925    // private
19926    function onHide(m){
19927        active.remove(m);
19928        if(active.length < 1){
19929            Roo.get(document).un("mousedown", onMouseDown);
19930            attached = false;
19931        }
19932    }
19933
19934    // private
19935    function onShow(m){
19936        var last = active.last();
19937        lastShow = new Date();
19938        active.add(m);
19939        if(!attached){
19940            Roo.get(document).on("mousedown", onMouseDown);
19941            attached = true;
19942        }
19943        if(m.parentMenu){
19944           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19945           m.parentMenu.activeChild = m;
19946        }else if(last && last.isVisible()){
19947           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19948        }
19949    }
19950
19951    // private
19952    function onBeforeHide(m){
19953        if(m.activeChild){
19954            m.activeChild.hide();
19955        }
19956        if(m.autoHideTimer){
19957            clearTimeout(m.autoHideTimer);
19958            delete m.autoHideTimer;
19959        }
19960    }
19961
19962    // private
19963    function onBeforeShow(m){
19964        var pm = m.parentMenu;
19965        if(!pm && !m.allowOtherMenus){
19966            hideAll();
19967        }else if(pm && pm.activeChild && active != m){
19968            pm.activeChild.hide();
19969        }
19970    }
19971
19972    // private
19973    function onMouseDown(e){
19974        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19975            hideAll();
19976        }
19977    }
19978
19979    // private
19980    function onBeforeCheck(mi, state){
19981        if(state){
19982            var g = groups[mi.group];
19983            for(var i = 0, l = g.length; i < l; i++){
19984                if(g[i] != mi){
19985                    g[i].setChecked(false);
19986                }
19987            }
19988        }
19989    }
19990
19991    return {
19992
19993        /**
19994         * Hides all menus that are currently visible
19995         */
19996        hideAll : function(){
19997             hideAll();  
19998        },
19999
20000        // private
20001        register : function(menu){
20002            if(!menus){
20003                init();
20004            }
20005            menus[menu.id] = menu;
20006            menu.on("beforehide", onBeforeHide);
20007            menu.on("hide", onHide);
20008            menu.on("beforeshow", onBeforeShow);
20009            menu.on("show", onShow);
20010            var g = menu.group;
20011            if(g && menu.events["checkchange"]){
20012                if(!groups[g]){
20013                    groups[g] = [];
20014                }
20015                groups[g].push(menu);
20016                menu.on("checkchange", onCheck);
20017            }
20018        },
20019
20020         /**
20021          * Returns a {@link Roo.menu.Menu} object
20022          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20023          * be used to generate and return a new Menu instance.
20024          */
20025        get : function(menu){
20026            if(typeof menu == "string"){ // menu id
20027                return menus[menu];
20028            }else if(menu.events){  // menu instance
20029                return menu;
20030            }else if(typeof menu.length == 'number'){ // array of menu items?
20031                return new Roo.menu.Menu({items:menu});
20032            }else{ // otherwise, must be a config
20033                return new Roo.menu.Menu(menu);
20034            }
20035        },
20036
20037        // private
20038        unregister : function(menu){
20039            delete menus[menu.id];
20040            menu.un("beforehide", onBeforeHide);
20041            menu.un("hide", onHide);
20042            menu.un("beforeshow", onBeforeShow);
20043            menu.un("show", onShow);
20044            var g = menu.group;
20045            if(g && menu.events["checkchange"]){
20046                groups[g].remove(menu);
20047                menu.un("checkchange", onCheck);
20048            }
20049        },
20050
20051        // private
20052        registerCheckable : function(menuItem){
20053            var g = menuItem.group;
20054            if(g){
20055                if(!groups[g]){
20056                    groups[g] = [];
20057                }
20058                groups[g].push(menuItem);
20059                menuItem.on("beforecheckchange", onBeforeCheck);
20060            }
20061        },
20062
20063        // private
20064        unregisterCheckable : function(menuItem){
20065            var g = menuItem.group;
20066            if(g){
20067                groups[g].remove(menuItem);
20068                menuItem.un("beforecheckchange", onBeforeCheck);
20069            }
20070        }
20071    };
20072 }();/*
20073  * Based on:
20074  * Ext JS Library 1.1.1
20075  * Copyright(c) 2006-2007, Ext JS, LLC.
20076  *
20077  * Originally Released Under LGPL - original licence link has changed is not relivant.
20078  *
20079  * Fork - LGPL
20080  * <script type="text/javascript">
20081  */
20082  
20083
20084 /**
20085  * @class Roo.menu.BaseItem
20086  * @extends Roo.Component
20087  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20088  * management and base configuration options shared by all menu components.
20089  * @constructor
20090  * Creates a new BaseItem
20091  * @param {Object} config Configuration options
20092  */
20093 Roo.menu.BaseItem = function(config){
20094     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20095
20096     this.addEvents({
20097         /**
20098          * @event click
20099          * Fires when this item is clicked
20100          * @param {Roo.menu.BaseItem} this
20101          * @param {Roo.EventObject} e
20102          */
20103         click: true,
20104         /**
20105          * @event activate
20106          * Fires when this item is activated
20107          * @param {Roo.menu.BaseItem} this
20108          */
20109         activate : true,
20110         /**
20111          * @event deactivate
20112          * Fires when this item is deactivated
20113          * @param {Roo.menu.BaseItem} this
20114          */
20115         deactivate : true
20116     });
20117
20118     if(this.handler){
20119         this.on("click", this.handler, this.scope, true);
20120     }
20121 };
20122
20123 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20124     /**
20125      * @cfg {Function} handler
20126      * A function that will handle the click event of this menu item (defaults to undefined)
20127      */
20128     /**
20129      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20130      */
20131     canActivate : false,
20132     /**
20133      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20134      */
20135     activeClass : "x-menu-item-active",
20136     /**
20137      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20138      */
20139     hideOnClick : true,
20140     /**
20141      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20142      */
20143     hideDelay : 100,
20144
20145     // private
20146     ctype: "Roo.menu.BaseItem",
20147
20148     // private
20149     actionMode : "container",
20150
20151     // private
20152     render : function(container, parentMenu){
20153         this.parentMenu = parentMenu;
20154         Roo.menu.BaseItem.superclass.render.call(this, container);
20155         this.container.menuItemId = this.id;
20156     },
20157
20158     // private
20159     onRender : function(container, position){
20160         this.el = Roo.get(this.el);
20161         container.dom.appendChild(this.el.dom);
20162     },
20163
20164     // private
20165     onClick : function(e){
20166         if(!this.disabled && this.fireEvent("click", this, e) !== false
20167                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20168             this.handleClick(e);
20169         }else{
20170             e.stopEvent();
20171         }
20172     },
20173
20174     // private
20175     activate : function(){
20176         if(this.disabled){
20177             return false;
20178         }
20179         var li = this.container;
20180         li.addClass(this.activeClass);
20181         this.region = li.getRegion().adjust(2, 2, -2, -2);
20182         this.fireEvent("activate", this);
20183         return true;
20184     },
20185
20186     // private
20187     deactivate : function(){
20188         this.container.removeClass(this.activeClass);
20189         this.fireEvent("deactivate", this);
20190     },
20191
20192     // private
20193     shouldDeactivate : function(e){
20194         return !this.region || !this.region.contains(e.getPoint());
20195     },
20196
20197     // private
20198     handleClick : function(e){
20199         if(this.hideOnClick){
20200             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20201         }
20202     },
20203
20204     // private
20205     expandMenu : function(autoActivate){
20206         // do nothing
20207     },
20208
20209     // private
20210     hideMenu : function(){
20211         // do nothing
20212     }
20213 });/*
20214  * Based on:
20215  * Ext JS Library 1.1.1
20216  * Copyright(c) 2006-2007, Ext JS, LLC.
20217  *
20218  * Originally Released Under LGPL - original licence link has changed is not relivant.
20219  *
20220  * Fork - LGPL
20221  * <script type="text/javascript">
20222  */
20223  
20224 /**
20225  * @class Roo.menu.Adapter
20226  * @extends Roo.menu.BaseItem
20227  * 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.
20228  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20229  * @constructor
20230  * Creates a new Adapter
20231  * @param {Object} config Configuration options
20232  */
20233 Roo.menu.Adapter = function(component, config){
20234     Roo.menu.Adapter.superclass.constructor.call(this, config);
20235     this.component = component;
20236 };
20237 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20238     // private
20239     canActivate : true,
20240
20241     // private
20242     onRender : function(container, position){
20243         this.component.render(container);
20244         this.el = this.component.getEl();
20245     },
20246
20247     // private
20248     activate : function(){
20249         if(this.disabled){
20250             return false;
20251         }
20252         this.component.focus();
20253         this.fireEvent("activate", this);
20254         return true;
20255     },
20256
20257     // private
20258     deactivate : function(){
20259         this.fireEvent("deactivate", this);
20260     },
20261
20262     // private
20263     disable : function(){
20264         this.component.disable();
20265         Roo.menu.Adapter.superclass.disable.call(this);
20266     },
20267
20268     // private
20269     enable : function(){
20270         this.component.enable();
20271         Roo.menu.Adapter.superclass.enable.call(this);
20272     }
20273 });/*
20274  * Based on:
20275  * Ext JS Library 1.1.1
20276  * Copyright(c) 2006-2007, Ext JS, LLC.
20277  *
20278  * Originally Released Under LGPL - original licence link has changed is not relivant.
20279  *
20280  * Fork - LGPL
20281  * <script type="text/javascript">
20282  */
20283
20284 /**
20285  * @class Roo.menu.TextItem
20286  * @extends Roo.menu.BaseItem
20287  * Adds a static text string to a menu, usually used as either a heading or group separator.
20288  * Note: old style constructor with text is still supported.
20289  * 
20290  * @constructor
20291  * Creates a new TextItem
20292  * @param {Object} cfg Configuration
20293  */
20294 Roo.menu.TextItem = function(cfg){
20295     if (typeof(cfg) == 'string') {
20296         this.text = cfg;
20297     } else {
20298         Roo.apply(this,cfg);
20299     }
20300     
20301     Roo.menu.TextItem.superclass.constructor.call(this);
20302 };
20303
20304 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20305     /**
20306      * @cfg {Boolean} text Text to show on item.
20307      */
20308     text : '',
20309     
20310     /**
20311      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20312      */
20313     hideOnClick : false,
20314     /**
20315      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20316      */
20317     itemCls : "x-menu-text",
20318
20319     // private
20320     onRender : function(){
20321         var s = document.createElement("span");
20322         s.className = this.itemCls;
20323         s.innerHTML = this.text;
20324         this.el = s;
20325         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20326     }
20327 });/*
20328  * Based on:
20329  * Ext JS Library 1.1.1
20330  * Copyright(c) 2006-2007, Ext JS, LLC.
20331  *
20332  * Originally Released Under LGPL - original licence link has changed is not relivant.
20333  *
20334  * Fork - LGPL
20335  * <script type="text/javascript">
20336  */
20337
20338 /**
20339  * @class Roo.menu.Separator
20340  * @extends Roo.menu.BaseItem
20341  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20342  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20343  * @constructor
20344  * @param {Object} config Configuration options
20345  */
20346 Roo.menu.Separator = function(config){
20347     Roo.menu.Separator.superclass.constructor.call(this, config);
20348 };
20349
20350 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20351     /**
20352      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20353      */
20354     itemCls : "x-menu-sep",
20355     /**
20356      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20357      */
20358     hideOnClick : false,
20359
20360     // private
20361     onRender : function(li){
20362         var s = document.createElement("span");
20363         s.className = this.itemCls;
20364         s.innerHTML = "&#160;";
20365         this.el = s;
20366         li.addClass("x-menu-sep-li");
20367         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20368     }
20369 });/*
20370  * Based on:
20371  * Ext JS Library 1.1.1
20372  * Copyright(c) 2006-2007, Ext JS, LLC.
20373  *
20374  * Originally Released Under LGPL - original licence link has changed is not relivant.
20375  *
20376  * Fork - LGPL
20377  * <script type="text/javascript">
20378  */
20379 /**
20380  * @class Roo.menu.Item
20381  * @extends Roo.menu.BaseItem
20382  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20383  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20384  * activation and click handling.
20385  * @constructor
20386  * Creates a new Item
20387  * @param {Object} config Configuration options
20388  */
20389 Roo.menu.Item = function(config){
20390     Roo.menu.Item.superclass.constructor.call(this, config);
20391     if(this.menu){
20392         this.menu = Roo.menu.MenuMgr.get(this.menu);
20393     }
20394 };
20395 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20396     
20397     /**
20398      * @cfg {String} text
20399      * The text to show on the menu item.
20400      */
20401     text: '',
20402      /**
20403      * @cfg {String} HTML to render in menu
20404      * The text to show on the menu item (HTML version).
20405      */
20406     html: '',
20407     /**
20408      * @cfg {String} icon
20409      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20410      */
20411     icon: undefined,
20412     /**
20413      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20414      */
20415     itemCls : "x-menu-item",
20416     /**
20417      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20418      */
20419     canActivate : true,
20420     /**
20421      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20422      */
20423     showDelay: 200,
20424     // doc'd in BaseItem
20425     hideDelay: 200,
20426
20427     // private
20428     ctype: "Roo.menu.Item",
20429     
20430     // private
20431     onRender : function(container, position){
20432         var el = document.createElement("a");
20433         el.hideFocus = true;
20434         el.unselectable = "on";
20435         el.href = this.href || "#";
20436         if(this.hrefTarget){
20437             el.target = this.hrefTarget;
20438         }
20439         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20440         
20441         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20442         
20443         el.innerHTML = String.format(
20444                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20445                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20446         this.el = el;
20447         Roo.menu.Item.superclass.onRender.call(this, container, position);
20448     },
20449
20450     /**
20451      * Sets the text to display in this menu item
20452      * @param {String} text The text to display
20453      * @param {Boolean} isHTML true to indicate text is pure html.
20454      */
20455     setText : function(text, isHTML){
20456         if (isHTML) {
20457             this.html = text;
20458         } else {
20459             this.text = text;
20460             this.html = '';
20461         }
20462         if(this.rendered){
20463             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20464      
20465             this.el.update(String.format(
20466                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20467                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20468             this.parentMenu.autoWidth();
20469         }
20470     },
20471
20472     // private
20473     handleClick : function(e){
20474         if(!this.href){ // if no link defined, stop the event automatically
20475             e.stopEvent();
20476         }
20477         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20478     },
20479
20480     // private
20481     activate : function(autoExpand){
20482         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20483             this.focus();
20484             if(autoExpand){
20485                 this.expandMenu();
20486             }
20487         }
20488         return true;
20489     },
20490
20491     // private
20492     shouldDeactivate : function(e){
20493         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20494             if(this.menu && this.menu.isVisible()){
20495                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20496             }
20497             return true;
20498         }
20499         return false;
20500     },
20501
20502     // private
20503     deactivate : function(){
20504         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20505         this.hideMenu();
20506     },
20507
20508     // private
20509     expandMenu : function(autoActivate){
20510         if(!this.disabled && this.menu){
20511             clearTimeout(this.hideTimer);
20512             delete this.hideTimer;
20513             if(!this.menu.isVisible() && !this.showTimer){
20514                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20515             }else if (this.menu.isVisible() && autoActivate){
20516                 this.menu.tryActivate(0, 1);
20517             }
20518         }
20519     },
20520
20521     // private
20522     deferExpand : function(autoActivate){
20523         delete this.showTimer;
20524         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20525         if(autoActivate){
20526             this.menu.tryActivate(0, 1);
20527         }
20528     },
20529
20530     // private
20531     hideMenu : function(){
20532         clearTimeout(this.showTimer);
20533         delete this.showTimer;
20534         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20535             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20536         }
20537     },
20538
20539     // private
20540     deferHide : function(){
20541         delete this.hideTimer;
20542         this.menu.hide();
20543     }
20544 });/*
20545  * Based on:
20546  * Ext JS Library 1.1.1
20547  * Copyright(c) 2006-2007, Ext JS, LLC.
20548  *
20549  * Originally Released Under LGPL - original licence link has changed is not relivant.
20550  *
20551  * Fork - LGPL
20552  * <script type="text/javascript">
20553  */
20554  
20555 /**
20556  * @class Roo.menu.CheckItem
20557  * @extends Roo.menu.Item
20558  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20559  * @constructor
20560  * Creates a new CheckItem
20561  * @param {Object} config Configuration options
20562  */
20563 Roo.menu.CheckItem = function(config){
20564     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20565     this.addEvents({
20566         /**
20567          * @event beforecheckchange
20568          * Fires before the checked value is set, providing an opportunity to cancel if needed
20569          * @param {Roo.menu.CheckItem} this
20570          * @param {Boolean} checked The new checked value that will be set
20571          */
20572         "beforecheckchange" : true,
20573         /**
20574          * @event checkchange
20575          * Fires after the checked value has been set
20576          * @param {Roo.menu.CheckItem} this
20577          * @param {Boolean} checked The checked value that was set
20578          */
20579         "checkchange" : true
20580     });
20581     if(this.checkHandler){
20582         this.on('checkchange', this.checkHandler, this.scope);
20583     }
20584 };
20585 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20586     /**
20587      * @cfg {String} group
20588      * All check items with the same group name will automatically be grouped into a single-select
20589      * radio button group (defaults to '')
20590      */
20591     /**
20592      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20593      */
20594     itemCls : "x-menu-item x-menu-check-item",
20595     /**
20596      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20597      */
20598     groupClass : "x-menu-group-item",
20599
20600     /**
20601      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20602      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20603      * initialized with checked = true will be rendered as checked.
20604      */
20605     checked: false,
20606
20607     // private
20608     ctype: "Roo.menu.CheckItem",
20609
20610     // private
20611     onRender : function(c){
20612         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20613         if(this.group){
20614             this.el.addClass(this.groupClass);
20615         }
20616         Roo.menu.MenuMgr.registerCheckable(this);
20617         if(this.checked){
20618             this.checked = false;
20619             this.setChecked(true, true);
20620         }
20621     },
20622
20623     // private
20624     destroy : function(){
20625         if(this.rendered){
20626             Roo.menu.MenuMgr.unregisterCheckable(this);
20627         }
20628         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20629     },
20630
20631     /**
20632      * Set the checked state of this item
20633      * @param {Boolean} checked The new checked value
20634      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20635      */
20636     setChecked : function(state, suppressEvent){
20637         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20638             if(this.container){
20639                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20640             }
20641             this.checked = state;
20642             if(suppressEvent !== true){
20643                 this.fireEvent("checkchange", this, state);
20644             }
20645         }
20646     },
20647
20648     // private
20649     handleClick : function(e){
20650        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20651            this.setChecked(!this.checked);
20652        }
20653        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20654     }
20655 });/*
20656  * Based on:
20657  * Ext JS Library 1.1.1
20658  * Copyright(c) 2006-2007, Ext JS, LLC.
20659  *
20660  * Originally Released Under LGPL - original licence link has changed is not relivant.
20661  *
20662  * Fork - LGPL
20663  * <script type="text/javascript">
20664  */
20665  
20666 /**
20667  * @class Roo.menu.DateItem
20668  * @extends Roo.menu.Adapter
20669  * A menu item that wraps the {@link Roo.DatPicker} component.
20670  * @constructor
20671  * Creates a new DateItem
20672  * @param {Object} config Configuration options
20673  */
20674 Roo.menu.DateItem = function(config){
20675     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20676     /** The Roo.DatePicker object @type Roo.DatePicker */
20677     this.picker = this.component;
20678     this.addEvents({select: true});
20679     
20680     this.picker.on("render", function(picker){
20681         picker.getEl().swallowEvent("click");
20682         picker.container.addClass("x-menu-date-item");
20683     });
20684
20685     this.picker.on("select", this.onSelect, this);
20686 };
20687
20688 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20689     // private
20690     onSelect : function(picker, date){
20691         this.fireEvent("select", this, date, picker);
20692         Roo.menu.DateItem.superclass.handleClick.call(this);
20693     }
20694 });/*
20695  * Based on:
20696  * Ext JS Library 1.1.1
20697  * Copyright(c) 2006-2007, Ext JS, LLC.
20698  *
20699  * Originally Released Under LGPL - original licence link has changed is not relivant.
20700  *
20701  * Fork - LGPL
20702  * <script type="text/javascript">
20703  */
20704  
20705 /**
20706  * @class Roo.menu.ColorItem
20707  * @extends Roo.menu.Adapter
20708  * A menu item that wraps the {@link Roo.ColorPalette} component.
20709  * @constructor
20710  * Creates a new ColorItem
20711  * @param {Object} config Configuration options
20712  */
20713 Roo.menu.ColorItem = function(config){
20714     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20715     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20716     this.palette = this.component;
20717     this.relayEvents(this.palette, ["select"]);
20718     if(this.selectHandler){
20719         this.on('select', this.selectHandler, this.scope);
20720     }
20721 };
20722 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20723  * Based on:
20724  * Ext JS Library 1.1.1
20725  * Copyright(c) 2006-2007, Ext JS, LLC.
20726  *
20727  * Originally Released Under LGPL - original licence link has changed is not relivant.
20728  *
20729  * Fork - LGPL
20730  * <script type="text/javascript">
20731  */
20732  
20733
20734 /**
20735  * @class Roo.menu.DateMenu
20736  * @extends Roo.menu.Menu
20737  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20738  * @constructor
20739  * Creates a new DateMenu
20740  * @param {Object} config Configuration options
20741  */
20742 Roo.menu.DateMenu = function(config){
20743     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20744     this.plain = true;
20745     var di = new Roo.menu.DateItem(config);
20746     this.add(di);
20747     /**
20748      * The {@link Roo.DatePicker} instance for this DateMenu
20749      * @type DatePicker
20750      */
20751     this.picker = di.picker;
20752     /**
20753      * @event select
20754      * @param {DatePicker} picker
20755      * @param {Date} date
20756      */
20757     this.relayEvents(di, ["select"]);
20758
20759     this.on('beforeshow', function(){
20760         if(this.picker){
20761             this.picker.hideMonthPicker(true);
20762         }
20763     }, this);
20764 };
20765 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20766     cls:'x-date-menu'
20767 });/*
20768  * Based on:
20769  * Ext JS Library 1.1.1
20770  * Copyright(c) 2006-2007, Ext JS, LLC.
20771  *
20772  * Originally Released Under LGPL - original licence link has changed is not relivant.
20773  *
20774  * Fork - LGPL
20775  * <script type="text/javascript">
20776  */
20777  
20778
20779 /**
20780  * @class Roo.menu.ColorMenu
20781  * @extends Roo.menu.Menu
20782  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20783  * @constructor
20784  * Creates a new ColorMenu
20785  * @param {Object} config Configuration options
20786  */
20787 Roo.menu.ColorMenu = function(config){
20788     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20789     this.plain = true;
20790     var ci = new Roo.menu.ColorItem(config);
20791     this.add(ci);
20792     /**
20793      * The {@link Roo.ColorPalette} instance for this ColorMenu
20794      * @type ColorPalette
20795      */
20796     this.palette = ci.palette;
20797     /**
20798      * @event select
20799      * @param {ColorPalette} palette
20800      * @param {String} color
20801      */
20802     this.relayEvents(ci, ["select"]);
20803 };
20804 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20805  * Based on:
20806  * Ext JS Library 1.1.1
20807  * Copyright(c) 2006-2007, Ext JS, LLC.
20808  *
20809  * Originally Released Under LGPL - original licence link has changed is not relivant.
20810  *
20811  * Fork - LGPL
20812  * <script type="text/javascript">
20813  */
20814  
20815 /**
20816  * @class Roo.form.Field
20817  * @extends Roo.BoxComponent
20818  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20819  * @constructor
20820  * Creates a new Field
20821  * @param {Object} config Configuration options
20822  */
20823 Roo.form.Field = function(config){
20824     Roo.form.Field.superclass.constructor.call(this, config);
20825 };
20826
20827 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20828     /**
20829      * @cfg {String} fieldLabel Label to use when rendering a form.
20830      */
20831        /**
20832      * @cfg {String} qtip Mouse over tip
20833      */
20834      
20835     /**
20836      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20837      */
20838     invalidClass : "x-form-invalid",
20839     /**
20840      * @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")
20841      */
20842     invalidText : "The value in this field is invalid",
20843     /**
20844      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20845      */
20846     focusClass : "x-form-focus",
20847     /**
20848      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20849       automatic validation (defaults to "keyup").
20850      */
20851     validationEvent : "keyup",
20852     /**
20853      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20854      */
20855     validateOnBlur : true,
20856     /**
20857      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20858      */
20859     validationDelay : 250,
20860     /**
20861      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20862      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20863      */
20864     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20865     /**
20866      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20867      */
20868     fieldClass : "x-form-field",
20869     /**
20870      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20871      *<pre>
20872 Value         Description
20873 -----------   ----------------------------------------------------------------------
20874 qtip          Display a quick tip when the user hovers over the field
20875 title         Display a default browser title attribute popup
20876 under         Add a block div beneath the field containing the error text
20877 side          Add an error icon to the right of the field with a popup on hover
20878 [element id]  Add the error text directly to the innerHTML of the specified element
20879 </pre>
20880      */
20881     msgTarget : 'qtip',
20882     /**
20883      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20884      */
20885     msgFx : 'normal',
20886
20887     /**
20888      * @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.
20889      */
20890     readOnly : false,
20891
20892     /**
20893      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20894      */
20895     disabled : false,
20896
20897     /**
20898      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20899      */
20900     inputType : undefined,
20901     
20902     /**
20903      * @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).
20904          */
20905         tabIndex : undefined,
20906         
20907     // private
20908     isFormField : true,
20909
20910     // private
20911     hasFocus : false,
20912     /**
20913      * @property {Roo.Element} fieldEl
20914      * Element Containing the rendered Field (with label etc.)
20915      */
20916     /**
20917      * @cfg {Mixed} value A value to initialize this field with.
20918      */
20919     value : undefined,
20920
20921     /**
20922      * @cfg {String} name The field's HTML name attribute.
20923      */
20924     /**
20925      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20926      */
20927
20928         // private ??
20929         initComponent : function(){
20930         Roo.form.Field.superclass.initComponent.call(this);
20931         this.addEvents({
20932             /**
20933              * @event focus
20934              * Fires when this field receives input focus.
20935              * @param {Roo.form.Field} this
20936              */
20937             focus : true,
20938             /**
20939              * @event blur
20940              * Fires when this field loses input focus.
20941              * @param {Roo.form.Field} this
20942              */
20943             blur : true,
20944             /**
20945              * @event specialkey
20946              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20947              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20948              * @param {Roo.form.Field} this
20949              * @param {Roo.EventObject} e The event object
20950              */
20951             specialkey : true,
20952             /**
20953              * @event change
20954              * Fires just before the field blurs if the field value has changed.
20955              * @param {Roo.form.Field} this
20956              * @param {Mixed} newValue The new value
20957              * @param {Mixed} oldValue The original value
20958              */
20959             change : true,
20960             /**
20961              * @event invalid
20962              * Fires after the field has been marked as invalid.
20963              * @param {Roo.form.Field} this
20964              * @param {String} msg The validation message
20965              */
20966             invalid : true,
20967             /**
20968              * @event valid
20969              * Fires after the field has been validated with no errors.
20970              * @param {Roo.form.Field} this
20971              */
20972             valid : true,
20973              /**
20974              * @event keyup
20975              * Fires after the key up
20976              * @param {Roo.form.Field} this
20977              * @param {Roo.EventObject}  e The event Object
20978              */
20979             keyup : true
20980         });
20981     },
20982
20983     /**
20984      * Returns the name attribute of the field if available
20985      * @return {String} name The field name
20986      */
20987     getName: function(){
20988          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20989     },
20990
20991     // private
20992     onRender : function(ct, position){
20993         Roo.form.Field.superclass.onRender.call(this, ct, position);
20994         if(!this.el){
20995             var cfg = this.getAutoCreate();
20996             if(!cfg.name){
20997                 cfg.name = this.name || this.id;
20998             }
20999             if(this.inputType){
21000                 cfg.type = this.inputType;
21001             }
21002             this.el = ct.createChild(cfg, position);
21003         }
21004         var type = this.el.dom.type;
21005         if(type){
21006             if(type == 'password'){
21007                 type = 'text';
21008             }
21009             this.el.addClass('x-form-'+type);
21010         }
21011         if(this.readOnly){
21012             this.el.dom.readOnly = true;
21013         }
21014         if(this.tabIndex !== undefined){
21015             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21016         }
21017
21018         this.el.addClass([this.fieldClass, this.cls]);
21019         this.initValue();
21020     },
21021
21022     /**
21023      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21024      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21025      * @return {Roo.form.Field} this
21026      */
21027     applyTo : function(target){
21028         this.allowDomMove = false;
21029         this.el = Roo.get(target);
21030         this.render(this.el.dom.parentNode);
21031         return this;
21032     },
21033
21034     // private
21035     initValue : function(){
21036         if(this.value !== undefined){
21037             this.setValue(this.value);
21038         }else if(this.el.dom.value.length > 0){
21039             this.setValue(this.el.dom.value);
21040         }
21041     },
21042
21043     /**
21044      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21045      */
21046     isDirty : function() {
21047         if(this.disabled) {
21048             return false;
21049         }
21050         return String(this.getValue()) !== String(this.originalValue);
21051     },
21052
21053     // private
21054     afterRender : function(){
21055         Roo.form.Field.superclass.afterRender.call(this);
21056         this.initEvents();
21057     },
21058
21059     // private
21060     fireKey : function(e){
21061         //Roo.log('field ' + e.getKey());
21062         if(e.isNavKeyPress()){
21063             this.fireEvent("specialkey", this, e);
21064         }
21065     },
21066
21067     /**
21068      * Resets the current field value to the originally loaded value and clears any validation messages
21069      */
21070     reset : function(){
21071         this.setValue(this.originalValue);
21072         this.clearInvalid();
21073     },
21074
21075     // private
21076     initEvents : function(){
21077         // safari killled keypress - so keydown is now used..
21078         this.el.on("keydown" , this.fireKey,  this);
21079         this.el.on("focus", this.onFocus,  this);
21080         this.el.on("blur", this.onBlur,  this);
21081         this.el.relayEvent('keyup', this);
21082
21083         // reference to original value for reset
21084         this.originalValue = this.getValue();
21085     },
21086
21087     // private
21088     onFocus : function(){
21089         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21090             this.el.addClass(this.focusClass);
21091         }
21092         if(!this.hasFocus){
21093             this.hasFocus = true;
21094             this.startValue = this.getValue();
21095             this.fireEvent("focus", this);
21096         }
21097     },
21098
21099     beforeBlur : Roo.emptyFn,
21100
21101     // private
21102     onBlur : function(){
21103         this.beforeBlur();
21104         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21105             this.el.removeClass(this.focusClass);
21106         }
21107         this.hasFocus = false;
21108         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21109             this.validate();
21110         }
21111         var v = this.getValue();
21112         if(String(v) !== String(this.startValue)){
21113             this.fireEvent('change', this, v, this.startValue);
21114         }
21115         this.fireEvent("blur", this);
21116     },
21117
21118     /**
21119      * Returns whether or not the field value is currently valid
21120      * @param {Boolean} preventMark True to disable marking the field invalid
21121      * @return {Boolean} True if the value is valid, else false
21122      */
21123     isValid : function(preventMark){
21124         if(this.disabled){
21125             return true;
21126         }
21127         var restore = this.preventMark;
21128         this.preventMark = preventMark === true;
21129         var v = this.validateValue(this.processValue(this.getRawValue()));
21130         this.preventMark = restore;
21131         return v;
21132     },
21133
21134     /**
21135      * Validates the field value
21136      * @return {Boolean} True if the value is valid, else false
21137      */
21138     validate : function(){
21139         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21140             this.clearInvalid();
21141             return true;
21142         }
21143         return false;
21144     },
21145
21146     processValue : function(value){
21147         return value;
21148     },
21149
21150     // private
21151     // Subclasses should provide the validation implementation by overriding this
21152     validateValue : function(value){
21153         return true;
21154     },
21155
21156     /**
21157      * Mark this field as invalid
21158      * @param {String} msg The validation message
21159      */
21160     markInvalid : function(msg){
21161         if(!this.rendered || this.preventMark){ // not rendered
21162             return;
21163         }
21164         this.el.addClass(this.invalidClass);
21165         msg = msg || this.invalidText;
21166         switch(this.msgTarget){
21167             case 'qtip':
21168                 this.el.dom.qtip = msg;
21169                 this.el.dom.qclass = 'x-form-invalid-tip';
21170                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21171                     Roo.QuickTips.enable();
21172                 }
21173                 break;
21174             case 'title':
21175                 this.el.dom.title = msg;
21176                 break;
21177             case 'under':
21178                 if(!this.errorEl){
21179                     var elp = this.el.findParent('.x-form-element', 5, true);
21180                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21181                     this.errorEl.setWidth(elp.getWidth(true)-20);
21182                 }
21183                 this.errorEl.update(msg);
21184                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21185                 break;
21186             case 'side':
21187                 if(!this.errorIcon){
21188                     var elp = this.el.findParent('.x-form-element', 5, true);
21189                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21190                 }
21191                 this.alignErrorIcon();
21192                 this.errorIcon.dom.qtip = msg;
21193                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21194                 this.errorIcon.show();
21195                 this.on('resize', this.alignErrorIcon, this);
21196                 break;
21197             default:
21198                 var t = Roo.getDom(this.msgTarget);
21199                 t.innerHTML = msg;
21200                 t.style.display = this.msgDisplay;
21201                 break;
21202         }
21203         this.fireEvent('invalid', this, msg);
21204     },
21205
21206     // private
21207     alignErrorIcon : function(){
21208         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21209     },
21210
21211     /**
21212      * Clear any invalid styles/messages for this field
21213      */
21214     clearInvalid : function(){
21215         if(!this.rendered || this.preventMark){ // not rendered
21216             return;
21217         }
21218         this.el.removeClass(this.invalidClass);
21219         switch(this.msgTarget){
21220             case 'qtip':
21221                 this.el.dom.qtip = '';
21222                 break;
21223             case 'title':
21224                 this.el.dom.title = '';
21225                 break;
21226             case 'under':
21227                 if(this.errorEl){
21228                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21229                 }
21230                 break;
21231             case 'side':
21232                 if(this.errorIcon){
21233                     this.errorIcon.dom.qtip = '';
21234                     this.errorIcon.hide();
21235                     this.un('resize', this.alignErrorIcon, this);
21236                 }
21237                 break;
21238             default:
21239                 var t = Roo.getDom(this.msgTarget);
21240                 t.innerHTML = '';
21241                 t.style.display = 'none';
21242                 break;
21243         }
21244         this.fireEvent('valid', this);
21245     },
21246
21247     /**
21248      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21249      * @return {Mixed} value The field value
21250      */
21251     getRawValue : function(){
21252         var v = this.el.getValue();
21253         if(v === this.emptyText){
21254             v = '';
21255         }
21256         return v;
21257     },
21258
21259     /**
21260      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21261      * @return {Mixed} value The field value
21262      */
21263     getValue : function(){
21264         var v = this.el.getValue();
21265         if(v === this.emptyText || v === undefined){
21266             v = '';
21267         }
21268         return v;
21269     },
21270
21271     /**
21272      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21273      * @param {Mixed} value The value to set
21274      */
21275     setRawValue : function(v){
21276         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21277     },
21278
21279     /**
21280      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21281      * @param {Mixed} value The value to set
21282      */
21283     setValue : function(v){
21284         this.value = v;
21285         if(this.rendered){
21286             this.el.dom.value = (v === null || v === undefined ? '' : v);
21287             this.validate();
21288         }
21289     },
21290
21291     adjustSize : function(w, h){
21292         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21293         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21294         return s;
21295     },
21296
21297     adjustWidth : function(tag, w){
21298         tag = tag.toLowerCase();
21299         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21300             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21301                 if(tag == 'input'){
21302                     return w + 2;
21303                 }
21304                 if(tag = 'textarea'){
21305                     return w-2;
21306                 }
21307             }else if(Roo.isOpera){
21308                 if(tag == 'input'){
21309                     return w + 2;
21310                 }
21311                 if(tag = 'textarea'){
21312                     return w-2;
21313                 }
21314             }
21315         }
21316         return w;
21317     }
21318 });
21319
21320
21321 // anything other than normal should be considered experimental
21322 Roo.form.Field.msgFx = {
21323     normal : {
21324         show: function(msgEl, f){
21325             msgEl.setDisplayed('block');
21326         },
21327
21328         hide : function(msgEl, f){
21329             msgEl.setDisplayed(false).update('');
21330         }
21331     },
21332
21333     slide : {
21334         show: function(msgEl, f){
21335             msgEl.slideIn('t', {stopFx:true});
21336         },
21337
21338         hide : function(msgEl, f){
21339             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21340         }
21341     },
21342
21343     slideRight : {
21344         show: function(msgEl, f){
21345             msgEl.fixDisplay();
21346             msgEl.alignTo(f.el, 'tl-tr');
21347             msgEl.slideIn('l', {stopFx:true});
21348         },
21349
21350         hide : function(msgEl, f){
21351             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21352         }
21353     }
21354 };/*
21355  * Based on:
21356  * Ext JS Library 1.1.1
21357  * Copyright(c) 2006-2007, Ext JS, LLC.
21358  *
21359  * Originally Released Under LGPL - original licence link has changed is not relivant.
21360  *
21361  * Fork - LGPL
21362  * <script type="text/javascript">
21363  */
21364  
21365
21366 /**
21367  * @class Roo.form.TextField
21368  * @extends Roo.form.Field
21369  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21370  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21371  * @constructor
21372  * Creates a new TextField
21373  * @param {Object} config Configuration options
21374  */
21375 Roo.form.TextField = function(config){
21376     Roo.form.TextField.superclass.constructor.call(this, config);
21377     this.addEvents({
21378         /**
21379          * @event autosize
21380          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21381          * according to the default logic, but this event provides a hook for the developer to apply additional
21382          * logic at runtime to resize the field if needed.
21383              * @param {Roo.form.Field} this This text field
21384              * @param {Number} width The new field width
21385              */
21386         autosize : true
21387     });
21388 };
21389
21390 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21391     /**
21392      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21393      */
21394     grow : false,
21395     /**
21396      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21397      */
21398     growMin : 30,
21399     /**
21400      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21401      */
21402     growMax : 800,
21403     /**
21404      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21405      */
21406     vtype : null,
21407     /**
21408      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21409      */
21410     maskRe : null,
21411     /**
21412      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21413      */
21414     disableKeyFilter : false,
21415     /**
21416      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21417      */
21418     allowBlank : true,
21419     /**
21420      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21421      */
21422     minLength : 0,
21423     /**
21424      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21425      */
21426     maxLength : Number.MAX_VALUE,
21427     /**
21428      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21429      */
21430     minLengthText : "The minimum length for this field is {0}",
21431     /**
21432      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21433      */
21434     maxLengthText : "The maximum length for this field is {0}",
21435     /**
21436      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21437      */
21438     selectOnFocus : false,
21439     /**
21440      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21441      */
21442     blankText : "This field is required",
21443     /**
21444      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21445      * If available, this function will be called only after the basic validators all return true, and will be passed the
21446      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21447      */
21448     validator : null,
21449     /**
21450      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21451      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21452      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21453      */
21454     regex : null,
21455     /**
21456      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21457      */
21458     regexText : "",
21459     /**
21460      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21461      */
21462     emptyText : null,
21463     /**
21464      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21465      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21466      */
21467     emptyClass : 'x-form-empty-field',
21468
21469     // private
21470     initEvents : function(){
21471         Roo.form.TextField.superclass.initEvents.call(this);
21472         if(this.validationEvent == 'keyup'){
21473             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21474             this.el.on('keyup', this.filterValidation, this);
21475         }
21476         else if(this.validationEvent !== false){
21477             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21478         }
21479         if(this.selectOnFocus || this.emptyText){
21480             this.on("focus", this.preFocus, this);
21481             if(this.emptyText){
21482                 this.on('blur', this.postBlur, this);
21483                 this.applyEmptyText();
21484             }
21485         }
21486         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21487             this.el.on("keypress", this.filterKeys, this);
21488         }
21489         if(this.grow){
21490             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21491             this.el.on("click", this.autoSize,  this);
21492         }
21493     },
21494
21495     processValue : function(value){
21496         if(this.stripCharsRe){
21497             var newValue = value.replace(this.stripCharsRe, '');
21498             if(newValue !== value){
21499                 this.setRawValue(newValue);
21500                 return newValue;
21501             }
21502         }
21503         return value;
21504     },
21505
21506     filterValidation : function(e){
21507         if(!e.isNavKeyPress()){
21508             this.validationTask.delay(this.validationDelay);
21509         }
21510     },
21511
21512     // private
21513     onKeyUp : function(e){
21514         if(!e.isNavKeyPress()){
21515             this.autoSize();
21516         }
21517     },
21518
21519     /**
21520      * Resets the current field value to the originally-loaded value and clears any validation messages.
21521      * Also adds emptyText and emptyClass if the original value was blank.
21522      */
21523     reset : function(){
21524         Roo.form.TextField.superclass.reset.call(this);
21525         this.applyEmptyText();
21526     },
21527
21528     applyEmptyText : function(){
21529         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21530             this.setRawValue(this.emptyText);
21531             this.el.addClass(this.emptyClass);
21532         }
21533     },
21534
21535     // private
21536     preFocus : function(){
21537         if(this.emptyText){
21538             if(this.el.dom.value == this.emptyText){
21539                 this.setRawValue('');
21540             }
21541             this.el.removeClass(this.emptyClass);
21542         }
21543         if(this.selectOnFocus){
21544             this.el.dom.select();
21545         }
21546     },
21547
21548     // private
21549     postBlur : function(){
21550         this.applyEmptyText();
21551     },
21552
21553     // private
21554     filterKeys : function(e){
21555         var k = e.getKey();
21556         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21557             return;
21558         }
21559         var c = e.getCharCode(), cc = String.fromCharCode(c);
21560         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21561             return;
21562         }
21563         if(!this.maskRe.test(cc)){
21564             e.stopEvent();
21565         }
21566     },
21567
21568     setValue : function(v){
21569         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21570             this.el.removeClass(this.emptyClass);
21571         }
21572         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21573         this.applyEmptyText();
21574         this.autoSize();
21575     },
21576
21577     /**
21578      * Validates a value according to the field's validation rules and marks the field as invalid
21579      * if the validation fails
21580      * @param {Mixed} value The value to validate
21581      * @return {Boolean} True if the value is valid, else false
21582      */
21583     validateValue : function(value){
21584         if(value.length < 1 || value === this.emptyText){ // if it's blank
21585              if(this.allowBlank){
21586                 this.clearInvalid();
21587                 return true;
21588              }else{
21589                 this.markInvalid(this.blankText);
21590                 return false;
21591              }
21592         }
21593         if(value.length < this.minLength){
21594             this.markInvalid(String.format(this.minLengthText, this.minLength));
21595             return false;
21596         }
21597         if(value.length > this.maxLength){
21598             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21599             return false;
21600         }
21601         if(this.vtype){
21602             var vt = Roo.form.VTypes;
21603             if(!vt[this.vtype](value, this)){
21604                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21605                 return false;
21606             }
21607         }
21608         if(typeof this.validator == "function"){
21609             var msg = this.validator(value);
21610             if(msg !== true){
21611                 this.markInvalid(msg);
21612                 return false;
21613             }
21614         }
21615         if(this.regex && !this.regex.test(value)){
21616             this.markInvalid(this.regexText);
21617             return false;
21618         }
21619         return true;
21620     },
21621
21622     /**
21623      * Selects text in this field
21624      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21625      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21626      */
21627     selectText : function(start, end){
21628         var v = this.getRawValue();
21629         if(v.length > 0){
21630             start = start === undefined ? 0 : start;
21631             end = end === undefined ? v.length : end;
21632             var d = this.el.dom;
21633             if(d.setSelectionRange){
21634                 d.setSelectionRange(start, end);
21635             }else if(d.createTextRange){
21636                 var range = d.createTextRange();
21637                 range.moveStart("character", start);
21638                 range.moveEnd("character", v.length-end);
21639                 range.select();
21640             }
21641         }
21642     },
21643
21644     /**
21645      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21646      * This only takes effect if grow = true, and fires the autosize event.
21647      */
21648     autoSize : function(){
21649         if(!this.grow || !this.rendered){
21650             return;
21651         }
21652         if(!this.metrics){
21653             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21654         }
21655         var el = this.el;
21656         var v = el.dom.value;
21657         var d = document.createElement('div');
21658         d.appendChild(document.createTextNode(v));
21659         v = d.innerHTML;
21660         d = null;
21661         v += "&#160;";
21662         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21663         this.el.setWidth(w);
21664         this.fireEvent("autosize", this, w);
21665     }
21666 });/*
21667  * Based on:
21668  * Ext JS Library 1.1.1
21669  * Copyright(c) 2006-2007, Ext JS, LLC.
21670  *
21671  * Originally Released Under LGPL - original licence link has changed is not relivant.
21672  *
21673  * Fork - LGPL
21674  * <script type="text/javascript">
21675  */
21676  
21677 /**
21678  * @class Roo.form.Hidden
21679  * @extends Roo.form.TextField
21680  * Simple Hidden element used on forms 
21681  * 
21682  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21683  * 
21684  * @constructor
21685  * Creates a new Hidden form element.
21686  * @param {Object} config Configuration options
21687  */
21688
21689
21690
21691 // easy hidden field...
21692 Roo.form.Hidden = function(config){
21693     Roo.form.Hidden.superclass.constructor.call(this, config);
21694 };
21695   
21696 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21697     fieldLabel:      '',
21698     inputType:      'hidden',
21699     width:          50,
21700     allowBlank:     true,
21701     labelSeparator: '',
21702     hidden:         true,
21703     itemCls :       'x-form-item-display-none'
21704
21705
21706 });
21707
21708
21709 /*
21710  * Based on:
21711  * Ext JS Library 1.1.1
21712  * Copyright(c) 2006-2007, Ext JS, LLC.
21713  *
21714  * Originally Released Under LGPL - original licence link has changed is not relivant.
21715  *
21716  * Fork - LGPL
21717  * <script type="text/javascript">
21718  */
21719  
21720 /**
21721  * @class Roo.form.TriggerField
21722  * @extends Roo.form.TextField
21723  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21724  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21725  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21726  * for which you can provide a custom implementation.  For example:
21727  * <pre><code>
21728 var trigger = new Roo.form.TriggerField();
21729 trigger.onTriggerClick = myTriggerFn;
21730 trigger.applyTo('my-field');
21731 </code></pre>
21732  *
21733  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21734  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21735  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21736  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21737  * @constructor
21738  * Create a new TriggerField.
21739  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21740  * to the base TextField)
21741  */
21742 Roo.form.TriggerField = function(config){
21743     this.mimicing = false;
21744     Roo.form.TriggerField.superclass.constructor.call(this, config);
21745 };
21746
21747 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21748     /**
21749      * @cfg {String} triggerClass A CSS class to apply to the trigger
21750      */
21751     /**
21752      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21753      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21754      */
21755     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21756     /**
21757      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21758      */
21759     hideTrigger:false,
21760
21761     /** @cfg {Boolean} grow @hide */
21762     /** @cfg {Number} growMin @hide */
21763     /** @cfg {Number} growMax @hide */
21764
21765     /**
21766      * @hide 
21767      * @method
21768      */
21769     autoSize: Roo.emptyFn,
21770     // private
21771     monitorTab : true,
21772     // private
21773     deferHeight : true,
21774
21775     
21776     actionMode : 'wrap',
21777     // private
21778     onResize : function(w, h){
21779         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21780         if(typeof w == 'number'){
21781             var x = w - this.trigger.getWidth();
21782             this.el.setWidth(this.adjustWidth('input', x));
21783             this.trigger.setStyle('left', x+'px');
21784         }
21785     },
21786
21787     // private
21788     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21789
21790     // private
21791     getResizeEl : function(){
21792         return this.wrap;
21793     },
21794
21795     // private
21796     getPositionEl : function(){
21797         return this.wrap;
21798     },
21799
21800     // private
21801     alignErrorIcon : function(){
21802         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21803     },
21804
21805     // private
21806     onRender : function(ct, position){
21807         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21808         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21809         this.trigger = this.wrap.createChild(this.triggerConfig ||
21810                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21811         if(this.hideTrigger){
21812             this.trigger.setDisplayed(false);
21813         }
21814         this.initTrigger();
21815         if(!this.width){
21816             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21817         }
21818     },
21819
21820     // private
21821     initTrigger : function(){
21822         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21823         this.trigger.addClassOnOver('x-form-trigger-over');
21824         this.trigger.addClassOnClick('x-form-trigger-click');
21825     },
21826
21827     // private
21828     onDestroy : function(){
21829         if(this.trigger){
21830             this.trigger.removeAllListeners();
21831             this.trigger.remove();
21832         }
21833         if(this.wrap){
21834             this.wrap.remove();
21835         }
21836         Roo.form.TriggerField.superclass.onDestroy.call(this);
21837     },
21838
21839     // private
21840     onFocus : function(){
21841         Roo.form.TriggerField.superclass.onFocus.call(this);
21842         if(!this.mimicing){
21843             this.wrap.addClass('x-trigger-wrap-focus');
21844             this.mimicing = true;
21845             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21846             if(this.monitorTab){
21847                 this.el.on("keydown", this.checkTab, this);
21848             }
21849         }
21850     },
21851
21852     // private
21853     checkTab : function(e){
21854         if(e.getKey() == e.TAB){
21855             this.triggerBlur();
21856         }
21857     },
21858
21859     // private
21860     onBlur : function(){
21861         // do nothing
21862     },
21863
21864     // private
21865     mimicBlur : function(e, t){
21866         if(!this.wrap.contains(t) && this.validateBlur()){
21867             this.triggerBlur();
21868         }
21869     },
21870
21871     // private
21872     triggerBlur : function(){
21873         this.mimicing = false;
21874         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21875         if(this.monitorTab){
21876             this.el.un("keydown", this.checkTab, this);
21877         }
21878         this.wrap.removeClass('x-trigger-wrap-focus');
21879         Roo.form.TriggerField.superclass.onBlur.call(this);
21880     },
21881
21882     // private
21883     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21884     validateBlur : function(e, t){
21885         return true;
21886     },
21887
21888     // private
21889     onDisable : function(){
21890         Roo.form.TriggerField.superclass.onDisable.call(this);
21891         if(this.wrap){
21892             this.wrap.addClass('x-item-disabled');
21893         }
21894     },
21895
21896     // private
21897     onEnable : function(){
21898         Roo.form.TriggerField.superclass.onEnable.call(this);
21899         if(this.wrap){
21900             this.wrap.removeClass('x-item-disabled');
21901         }
21902     },
21903
21904     // private
21905     onShow : function(){
21906         var ae = this.getActionEl();
21907         
21908         if(ae){
21909             ae.dom.style.display = '';
21910             ae.dom.style.visibility = 'visible';
21911         }
21912     },
21913
21914     // private
21915     
21916     onHide : function(){
21917         var ae = this.getActionEl();
21918         ae.dom.style.display = 'none';
21919     },
21920
21921     /**
21922      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21923      * by an implementing function.
21924      * @method
21925      * @param {EventObject} e
21926      */
21927     onTriggerClick : Roo.emptyFn
21928 });
21929
21930 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21931 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21932 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21933 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21934     initComponent : function(){
21935         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21936
21937         this.triggerConfig = {
21938             tag:'span', cls:'x-form-twin-triggers', cn:[
21939             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21940             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21941         ]};
21942     },
21943
21944     getTrigger : function(index){
21945         return this.triggers[index];
21946     },
21947
21948     initTrigger : function(){
21949         var ts = this.trigger.select('.x-form-trigger', true);
21950         this.wrap.setStyle('overflow', 'hidden');
21951         var triggerField = this;
21952         ts.each(function(t, all, index){
21953             t.hide = function(){
21954                 var w = triggerField.wrap.getWidth();
21955                 this.dom.style.display = 'none';
21956                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21957             };
21958             t.show = function(){
21959                 var w = triggerField.wrap.getWidth();
21960                 this.dom.style.display = '';
21961                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21962             };
21963             var triggerIndex = 'Trigger'+(index+1);
21964
21965             if(this['hide'+triggerIndex]){
21966                 t.dom.style.display = 'none';
21967             }
21968             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21969             t.addClassOnOver('x-form-trigger-over');
21970             t.addClassOnClick('x-form-trigger-click');
21971         }, this);
21972         this.triggers = ts.elements;
21973     },
21974
21975     onTrigger1Click : Roo.emptyFn,
21976     onTrigger2Click : Roo.emptyFn
21977 });/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987  
21988 /**
21989  * @class Roo.form.TextArea
21990  * @extends Roo.form.TextField
21991  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21992  * support for auto-sizing.
21993  * @constructor
21994  * Creates a new TextArea
21995  * @param {Object} config Configuration options
21996  */
21997 Roo.form.TextArea = function(config){
21998     Roo.form.TextArea.superclass.constructor.call(this, config);
21999     // these are provided exchanges for backwards compat
22000     // minHeight/maxHeight were replaced by growMin/growMax to be
22001     // compatible with TextField growing config values
22002     if(this.minHeight !== undefined){
22003         this.growMin = this.minHeight;
22004     }
22005     if(this.maxHeight !== undefined){
22006         this.growMax = this.maxHeight;
22007     }
22008 };
22009
22010 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22011     /**
22012      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22013      */
22014     growMin : 60,
22015     /**
22016      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22017      */
22018     growMax: 1000,
22019     /**
22020      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22021      * in the field (equivalent to setting overflow: hidden, defaults to false)
22022      */
22023     preventScrollbars: false,
22024     /**
22025      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22026      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22027      */
22028
22029     // private
22030     onRender : function(ct, position){
22031         if(!this.el){
22032             this.defaultAutoCreate = {
22033                 tag: "textarea",
22034                 style:"width:300px;height:60px;",
22035                 autocomplete: "off"
22036             };
22037         }
22038         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22039         if(this.grow){
22040             this.textSizeEl = Roo.DomHelper.append(document.body, {
22041                 tag: "pre", cls: "x-form-grow-sizer"
22042             });
22043             if(this.preventScrollbars){
22044                 this.el.setStyle("overflow", "hidden");
22045             }
22046             this.el.setHeight(this.growMin);
22047         }
22048     },
22049
22050     onDestroy : function(){
22051         if(this.textSizeEl){
22052             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22053         }
22054         Roo.form.TextArea.superclass.onDestroy.call(this);
22055     },
22056
22057     // private
22058     onKeyUp : function(e){
22059         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22060             this.autoSize();
22061         }
22062     },
22063
22064     /**
22065      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22066      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22067      */
22068     autoSize : function(){
22069         if(!this.grow || !this.textSizeEl){
22070             return;
22071         }
22072         var el = this.el;
22073         var v = el.dom.value;
22074         var ts = this.textSizeEl;
22075
22076         ts.innerHTML = '';
22077         ts.appendChild(document.createTextNode(v));
22078         v = ts.innerHTML;
22079
22080         Roo.fly(ts).setWidth(this.el.getWidth());
22081         if(v.length < 1){
22082             v = "&#160;&#160;";
22083         }else{
22084             if(Roo.isIE){
22085                 v = v.replace(/\n/g, '<p>&#160;</p>');
22086             }
22087             v += "&#160;\n&#160;";
22088         }
22089         ts.innerHTML = v;
22090         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22091         if(h != this.lastHeight){
22092             this.lastHeight = h;
22093             this.el.setHeight(h);
22094             this.fireEvent("autosize", this, h);
22095         }
22096     }
22097 });/*
22098  * Based on:
22099  * Ext JS Library 1.1.1
22100  * Copyright(c) 2006-2007, Ext JS, LLC.
22101  *
22102  * Originally Released Under LGPL - original licence link has changed is not relivant.
22103  *
22104  * Fork - LGPL
22105  * <script type="text/javascript">
22106  */
22107  
22108
22109 /**
22110  * @class Roo.form.NumberField
22111  * @extends Roo.form.TextField
22112  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22113  * @constructor
22114  * Creates a new NumberField
22115  * @param {Object} config Configuration options
22116  */
22117 Roo.form.NumberField = function(config){
22118     Roo.form.NumberField.superclass.constructor.call(this, config);
22119 };
22120
22121 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22122     /**
22123      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22124      */
22125     fieldClass: "x-form-field x-form-num-field",
22126     /**
22127      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22128      */
22129     allowDecimals : true,
22130     /**
22131      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22132      */
22133     decimalSeparator : ".",
22134     /**
22135      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22136      */
22137     decimalPrecision : 2,
22138     /**
22139      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22140      */
22141     allowNegative : true,
22142     /**
22143      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22144      */
22145     minValue : Number.NEGATIVE_INFINITY,
22146     /**
22147      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22148      */
22149     maxValue : Number.MAX_VALUE,
22150     /**
22151      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22152      */
22153     minText : "The minimum value for this field is {0}",
22154     /**
22155      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22156      */
22157     maxText : "The maximum value for this field is {0}",
22158     /**
22159      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22160      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22161      */
22162     nanText : "{0} is not a valid number",
22163
22164     // private
22165     initEvents : function(){
22166         Roo.form.NumberField.superclass.initEvents.call(this);
22167         var allowed = "0123456789";
22168         if(this.allowDecimals){
22169             allowed += this.decimalSeparator;
22170         }
22171         if(this.allowNegative){
22172             allowed += "-";
22173         }
22174         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22175         var keyPress = function(e){
22176             var k = e.getKey();
22177             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22178                 return;
22179             }
22180             var c = e.getCharCode();
22181             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22182                 e.stopEvent();
22183             }
22184         };
22185         this.el.on("keypress", keyPress, this);
22186     },
22187
22188     // private
22189     validateValue : function(value){
22190         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22191             return false;
22192         }
22193         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22194              return true;
22195         }
22196         var num = this.parseValue(value);
22197         if(isNaN(num)){
22198             this.markInvalid(String.format(this.nanText, value));
22199             return false;
22200         }
22201         if(num < this.minValue){
22202             this.markInvalid(String.format(this.minText, this.minValue));
22203             return false;
22204         }
22205         if(num > this.maxValue){
22206             this.markInvalid(String.format(this.maxText, this.maxValue));
22207             return false;
22208         }
22209         return true;
22210     },
22211
22212     getValue : function(){
22213         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22214     },
22215
22216     // private
22217     parseValue : function(value){
22218         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22219         return isNaN(value) ? '' : value;
22220     },
22221
22222     // private
22223     fixPrecision : function(value){
22224         var nan = isNaN(value);
22225         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22226             return nan ? '' : value;
22227         }
22228         return parseFloat(value).toFixed(this.decimalPrecision);
22229     },
22230
22231     setValue : function(v){
22232         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22233     },
22234
22235     // private
22236     decimalPrecisionFcn : function(v){
22237         return Math.floor(v);
22238     },
22239
22240     beforeBlur : function(){
22241         var v = this.parseValue(this.getRawValue());
22242         if(v){
22243             this.setValue(this.fixPrecision(v));
22244         }
22245     }
22246 });/*
22247  * Based on:
22248  * Ext JS Library 1.1.1
22249  * Copyright(c) 2006-2007, Ext JS, LLC.
22250  *
22251  * Originally Released Under LGPL - original licence link has changed is not relivant.
22252  *
22253  * Fork - LGPL
22254  * <script type="text/javascript">
22255  */
22256  
22257 /**
22258  * @class Roo.form.DateField
22259  * @extends Roo.form.TriggerField
22260  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22261 * @constructor
22262 * Create a new DateField
22263 * @param {Object} config
22264  */
22265 Roo.form.DateField = function(config){
22266     Roo.form.DateField.superclass.constructor.call(this, config);
22267     
22268       this.addEvents({
22269          
22270         /**
22271          * @event select
22272          * Fires when a date is selected
22273              * @param {Roo.form.DateField} combo This combo box
22274              * @param {Date} date The date selected
22275              */
22276         'select' : true
22277          
22278     });
22279     
22280     
22281     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22282     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22283     this.ddMatch = null;
22284     if(this.disabledDates){
22285         var dd = this.disabledDates;
22286         var re = "(?:";
22287         for(var i = 0; i < dd.length; i++){
22288             re += dd[i];
22289             if(i != dd.length-1) re += "|";
22290         }
22291         this.ddMatch = new RegExp(re + ")");
22292     }
22293 };
22294
22295 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22296     /**
22297      * @cfg {String} format
22298      * The default date format string which can be overriden for localization support.  The format must be
22299      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22300      */
22301     format : "m/d/y",
22302     /**
22303      * @cfg {String} altFormats
22304      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22305      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22306      */
22307     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22308     /**
22309      * @cfg {Array} disabledDays
22310      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22311      */
22312     disabledDays : null,
22313     /**
22314      * @cfg {String} disabledDaysText
22315      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22316      */
22317     disabledDaysText : "Disabled",
22318     /**
22319      * @cfg {Array} disabledDates
22320      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22321      * expression so they are very powerful. Some examples:
22322      * <ul>
22323      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22324      * <li>["03/08", "09/16"] would disable those days for every year</li>
22325      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22326      * <li>["03/../2006"] would disable every day in March 2006</li>
22327      * <li>["^03"] would disable every day in every March</li>
22328      * </ul>
22329      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22330      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22331      */
22332     disabledDates : null,
22333     /**
22334      * @cfg {String} disabledDatesText
22335      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22336      */
22337     disabledDatesText : "Disabled",
22338     /**
22339      * @cfg {Date/String} minValue
22340      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22341      * valid format (defaults to null).
22342      */
22343     minValue : null,
22344     /**
22345      * @cfg {Date/String} maxValue
22346      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22347      * valid format (defaults to null).
22348      */
22349     maxValue : null,
22350     /**
22351      * @cfg {String} minText
22352      * The error text to display when the date in the cell is before minValue (defaults to
22353      * 'The date in this field must be after {minValue}').
22354      */
22355     minText : "The date in this field must be equal to or after {0}",
22356     /**
22357      * @cfg {String} maxText
22358      * The error text to display when the date in the cell is after maxValue (defaults to
22359      * 'The date in this field must be before {maxValue}').
22360      */
22361     maxText : "The date in this field must be equal to or before {0}",
22362     /**
22363      * @cfg {String} invalidText
22364      * The error text to display when the date in the field is invalid (defaults to
22365      * '{value} is not a valid date - it must be in the format {format}').
22366      */
22367     invalidText : "{0} is not a valid date - it must be in the format {1}",
22368     /**
22369      * @cfg {String} triggerClass
22370      * An additional CSS class used to style the trigger button.  The trigger will always get the
22371      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22372      * which displays a calendar icon).
22373      */
22374     triggerClass : 'x-form-date-trigger',
22375     
22376
22377     /**
22378      * @cfg {bool} useIso
22379      * if enabled, then the date field will use a hidden field to store the 
22380      * real value as iso formated date. default (false)
22381      */ 
22382     useIso : false,
22383     /**
22384      * @cfg {String/Object} autoCreate
22385      * A DomHelper element spec, or true for a default element spec (defaults to
22386      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22387      */ 
22388     // private
22389     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22390     
22391     // private
22392     hiddenField: false,
22393     
22394     onRender : function(ct, position)
22395     {
22396         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22397         if (this.useIso) {
22398             this.el.dom.removeAttribute('name'); 
22399             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22400                     'before', true);
22401             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22402             // prevent input submission
22403             this.hiddenName = this.name;
22404         }
22405             
22406             
22407     },
22408     
22409     // private
22410     validateValue : function(value)
22411     {
22412         value = this.formatDate(value);
22413         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22414             return false;
22415         }
22416         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22417              return true;
22418         }
22419         var svalue = value;
22420         value = this.parseDate(value);
22421         if(!value){
22422             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22423             return false;
22424         }
22425         var time = value.getTime();
22426         if(this.minValue && time < this.minValue.getTime()){
22427             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22428             return false;
22429         }
22430         if(this.maxValue && time > this.maxValue.getTime()){
22431             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22432             return false;
22433         }
22434         if(this.disabledDays){
22435             var day = value.getDay();
22436             for(var i = 0; i < this.disabledDays.length; i++) {
22437                 if(day === this.disabledDays[i]){
22438                     this.markInvalid(this.disabledDaysText);
22439                     return false;
22440                 }
22441             }
22442         }
22443         var fvalue = this.formatDate(value);
22444         if(this.ddMatch && this.ddMatch.test(fvalue)){
22445             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22446             return false;
22447         }
22448         return true;
22449     },
22450
22451     // private
22452     // Provides logic to override the default TriggerField.validateBlur which just returns true
22453     validateBlur : function(){
22454         return !this.menu || !this.menu.isVisible();
22455     },
22456
22457     /**
22458      * Returns the current date value of the date field.
22459      * @return {Date} The date value
22460      */
22461     getValue : function(){
22462         
22463         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22464     },
22465
22466     /**
22467      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22468      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22469      * (the default format used is "m/d/y").
22470      * <br />Usage:
22471      * <pre><code>
22472 //All of these calls set the same date value (May 4, 2006)
22473
22474 //Pass a date object:
22475 var dt = new Date('5/4/06');
22476 dateField.setValue(dt);
22477
22478 //Pass a date string (default format):
22479 dateField.setValue('5/4/06');
22480
22481 //Pass a date string (custom format):
22482 dateField.format = 'Y-m-d';
22483 dateField.setValue('2006-5-4');
22484 </code></pre>
22485      * @param {String/Date} date The date or valid date string
22486      */
22487     setValue : function(date){
22488         if (this.hiddenField) {
22489             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22490         }
22491         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22492     },
22493
22494     // private
22495     parseDate : function(value){
22496         if(!value || value instanceof Date){
22497             return value;
22498         }
22499         var v = Date.parseDate(value, this.format);
22500         if(!v && this.altFormats){
22501             if(!this.altFormatsArray){
22502                 this.altFormatsArray = this.altFormats.split("|");
22503             }
22504             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22505                 v = Date.parseDate(value, this.altFormatsArray[i]);
22506             }
22507         }
22508         return v;
22509     },
22510
22511     // private
22512     formatDate : function(date, fmt){
22513         return (!date || !(date instanceof Date)) ?
22514                date : date.dateFormat(fmt || this.format);
22515     },
22516
22517     // private
22518     menuListeners : {
22519         select: function(m, d){
22520             this.setValue(d);
22521             this.fireEvent('select', this, d);
22522         },
22523         show : function(){ // retain focus styling
22524             this.onFocus();
22525         },
22526         hide : function(){
22527             this.focus.defer(10, this);
22528             var ml = this.menuListeners;
22529             this.menu.un("select", ml.select,  this);
22530             this.menu.un("show", ml.show,  this);
22531             this.menu.un("hide", ml.hide,  this);
22532         }
22533     },
22534
22535     // private
22536     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22537     onTriggerClick : function(){
22538         if(this.disabled){
22539             return;
22540         }
22541         if(this.menu == null){
22542             this.menu = new Roo.menu.DateMenu();
22543         }
22544         Roo.apply(this.menu.picker,  {
22545             showClear: this.allowBlank,
22546             minDate : this.minValue,
22547             maxDate : this.maxValue,
22548             disabledDatesRE : this.ddMatch,
22549             disabledDatesText : this.disabledDatesText,
22550             disabledDays : this.disabledDays,
22551             disabledDaysText : this.disabledDaysText,
22552             format : this.format,
22553             minText : String.format(this.minText, this.formatDate(this.minValue)),
22554             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22555         });
22556         this.menu.on(Roo.apply({}, this.menuListeners, {
22557             scope:this
22558         }));
22559         this.menu.picker.setValue(this.getValue() || new Date());
22560         this.menu.show(this.el, "tl-bl?");
22561     },
22562
22563     beforeBlur : function(){
22564         var v = this.parseDate(this.getRawValue());
22565         if(v){
22566             this.setValue(v);
22567         }
22568     }
22569
22570     /** @cfg {Boolean} grow @hide */
22571     /** @cfg {Number} growMin @hide */
22572     /** @cfg {Number} growMax @hide */
22573     /**
22574      * @hide
22575      * @method autoSize
22576      */
22577 });/*
22578  * Based on:
22579  * Ext JS Library 1.1.1
22580  * Copyright(c) 2006-2007, Ext JS, LLC.
22581  *
22582  * Originally Released Under LGPL - original licence link has changed is not relivant.
22583  *
22584  * Fork - LGPL
22585  * <script type="text/javascript">
22586  */
22587  
22588
22589 /**
22590  * @class Roo.form.ComboBox
22591  * @extends Roo.form.TriggerField
22592  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22593  * @constructor
22594  * Create a new ComboBox.
22595  * @param {Object} config Configuration options
22596  */
22597 Roo.form.ComboBox = function(config){
22598     Roo.form.ComboBox.superclass.constructor.call(this, config);
22599     this.addEvents({
22600         /**
22601          * @event expand
22602          * Fires when the dropdown list is expanded
22603              * @param {Roo.form.ComboBox} combo This combo box
22604              */
22605         'expand' : true,
22606         /**
22607          * @event collapse
22608          * Fires when the dropdown list is collapsed
22609              * @param {Roo.form.ComboBox} combo This combo box
22610              */
22611         'collapse' : true,
22612         /**
22613          * @event beforeselect
22614          * Fires before a list item is selected. Return false to cancel the selection.
22615              * @param {Roo.form.ComboBox} combo This combo box
22616              * @param {Roo.data.Record} record The data record returned from the underlying store
22617              * @param {Number} index The index of the selected item in the dropdown list
22618              */
22619         'beforeselect' : true,
22620         /**
22621          * @event select
22622          * Fires when a list item is selected
22623              * @param {Roo.form.ComboBox} combo This combo box
22624              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22625              * @param {Number} index The index of the selected item in the dropdown list
22626              */
22627         'select' : true,
22628         /**
22629          * @event beforequery
22630          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22631          * The event object passed has these properties:
22632              * @param {Roo.form.ComboBox} combo This combo box
22633              * @param {String} query The query
22634              * @param {Boolean} forceAll true to force "all" query
22635              * @param {Boolean} cancel true to cancel the query
22636              * @param {Object} e The query event object
22637              */
22638         'beforequery': true,
22639          /**
22640          * @event add
22641          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22642              * @param {Roo.form.ComboBox} combo This combo box
22643              */
22644         'add' : true,
22645         /**
22646          * @event edit
22647          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22648              * @param {Roo.form.ComboBox} combo This combo box
22649              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22650              */
22651         'edit' : true
22652         
22653         
22654     });
22655     if(this.transform){
22656         this.allowDomMove = false;
22657         var s = Roo.getDom(this.transform);
22658         if(!this.hiddenName){
22659             this.hiddenName = s.name;
22660         }
22661         if(!this.store){
22662             this.mode = 'local';
22663             var d = [], opts = s.options;
22664             for(var i = 0, len = opts.length;i < len; i++){
22665                 var o = opts[i];
22666                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22667                 if(o.selected) {
22668                     this.value = value;
22669                 }
22670                 d.push([value, o.text]);
22671             }
22672             this.store = new Roo.data.SimpleStore({
22673                 'id': 0,
22674                 fields: ['value', 'text'],
22675                 data : d
22676             });
22677             this.valueField = 'value';
22678             this.displayField = 'text';
22679         }
22680         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22681         if(!this.lazyRender){
22682             this.target = true;
22683             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22684             s.parentNode.removeChild(s); // remove it
22685             this.render(this.el.parentNode);
22686         }else{
22687             s.parentNode.removeChild(s); // remove it
22688         }
22689
22690     }
22691     if (this.store) {
22692         this.store = Roo.factory(this.store, Roo.data);
22693     }
22694     
22695     this.selectedIndex = -1;
22696     if(this.mode == 'local'){
22697         if(config.queryDelay === undefined){
22698             this.queryDelay = 10;
22699         }
22700         if(config.minChars === undefined){
22701             this.minChars = 0;
22702         }
22703     }
22704 };
22705
22706 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22707     /**
22708      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22709      */
22710     /**
22711      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22712      * rendering into an Roo.Editor, defaults to false)
22713      */
22714     /**
22715      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22716      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22717      */
22718     /**
22719      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22720      */
22721     /**
22722      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22723      * the dropdown list (defaults to undefined, with no header element)
22724      */
22725
22726      /**
22727      * @cfg {String/Roo.Template} tpl The template to use to render the output
22728      */
22729      
22730     // private
22731     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22732     /**
22733      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22734      */
22735     listWidth: undefined,
22736     /**
22737      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22738      * mode = 'remote' or 'text' if mode = 'local')
22739      */
22740     displayField: undefined,
22741     /**
22742      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22743      * mode = 'remote' or 'value' if mode = 'local'). 
22744      * Note: use of a valueField requires the user make a selection
22745      * in order for a value to be mapped.
22746      */
22747     valueField: undefined,
22748     /**
22749      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22750      * field's data value (defaults to the underlying DOM element's name)
22751      */
22752     hiddenName: undefined,
22753     /**
22754      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22755      */
22756     listClass: '',
22757     /**
22758      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22759      */
22760     selectedClass: 'x-combo-selected',
22761     /**
22762      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22763      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22764      * which displays a downward arrow icon).
22765      */
22766     triggerClass : 'x-form-arrow-trigger',
22767     /**
22768      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22769      */
22770     shadow:'sides',
22771     /**
22772      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22773      * anchor positions (defaults to 'tl-bl')
22774      */
22775     listAlign: 'tl-bl?',
22776     /**
22777      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22778      */
22779     maxHeight: 300,
22780     /**
22781      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22782      * query specified by the allQuery config option (defaults to 'query')
22783      */
22784     triggerAction: 'query',
22785     /**
22786      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22787      * (defaults to 4, does not apply if editable = false)
22788      */
22789     minChars : 4,
22790     /**
22791      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22792      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22793      */
22794     typeAhead: false,
22795     /**
22796      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22797      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22798      */
22799     queryDelay: 500,
22800     /**
22801      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22802      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22803      */
22804     pageSize: 0,
22805     /**
22806      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22807      * when editable = true (defaults to false)
22808      */
22809     selectOnFocus:false,
22810     /**
22811      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22812      */
22813     queryParam: 'query',
22814     /**
22815      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22816      * when mode = 'remote' (defaults to 'Loading...')
22817      */
22818     loadingText: 'Loading...',
22819     /**
22820      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22821      */
22822     resizable: false,
22823     /**
22824      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22825      */
22826     handleHeight : 8,
22827     /**
22828      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22829      * traditional select (defaults to true)
22830      */
22831     editable: true,
22832     /**
22833      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22834      */
22835     allQuery: '',
22836     /**
22837      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22838      */
22839     mode: 'remote',
22840     /**
22841      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22842      * listWidth has a higher value)
22843      */
22844     minListWidth : 70,
22845     /**
22846      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22847      * allow the user to set arbitrary text into the field (defaults to false)
22848      */
22849     forceSelection:false,
22850     /**
22851      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22852      * if typeAhead = true (defaults to 250)
22853      */
22854     typeAheadDelay : 250,
22855     /**
22856      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22857      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22858      */
22859     valueNotFoundText : undefined,
22860     /**
22861      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22862      */
22863     blockFocus : false,
22864     
22865     /**
22866      * @cfg {Boolean} disableClear Disable showing of clear button.
22867      */
22868     disableClear : false,
22869     /**
22870      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22871      */
22872     alwaysQuery : false,
22873     
22874     //private
22875     addicon : false,
22876     editicon: false,
22877     
22878     
22879     // private
22880     onRender : function(ct, position){
22881         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22882         if(this.hiddenName){
22883             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22884                     'before', true);
22885             this.hiddenField.value =
22886                 this.hiddenValue !== undefined ? this.hiddenValue :
22887                 this.value !== undefined ? this.value : '';
22888
22889             // prevent input submission
22890             this.el.dom.removeAttribute('name');
22891         }
22892         if(Roo.isGecko){
22893             this.el.dom.setAttribute('autocomplete', 'off');
22894         }
22895
22896         var cls = 'x-combo-list';
22897
22898         this.list = new Roo.Layer({
22899             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22900         });
22901
22902         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22903         this.list.setWidth(lw);
22904         this.list.swallowEvent('mousewheel');
22905         this.assetHeight = 0;
22906
22907         if(this.title){
22908             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22909             this.assetHeight += this.header.getHeight();
22910         }
22911
22912         this.innerList = this.list.createChild({cls:cls+'-inner'});
22913         this.innerList.on('mouseover', this.onViewOver, this);
22914         this.innerList.on('mousemove', this.onViewMove, this);
22915         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22916         
22917         if(this.allowBlank && !this.pageSize && !this.disableClear){
22918             this.footer = this.list.createChild({cls:cls+'-ft'});
22919             this.pageTb = new Roo.Toolbar(this.footer);
22920            
22921         }
22922         if(this.pageSize){
22923             this.footer = this.list.createChild({cls:cls+'-ft'});
22924             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22925                     {pageSize: this.pageSize});
22926             
22927         }
22928         
22929         if (this.pageTb && this.allowBlank && !this.disableClear) {
22930             var _this = this;
22931             this.pageTb.add(new Roo.Toolbar.Fill(), {
22932                 cls: 'x-btn-icon x-btn-clear',
22933                 text: '&#160;',
22934                 handler: function()
22935                 {
22936                     _this.collapse();
22937                     _this.clearValue();
22938                     _this.onSelect(false, -1);
22939                 }
22940             });
22941         }
22942         if (this.footer) {
22943             this.assetHeight += this.footer.getHeight();
22944         }
22945         
22946
22947         if(!this.tpl){
22948             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22949         }
22950
22951         this.view = new Roo.View(this.innerList, this.tpl, {
22952             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22953         });
22954
22955         this.view.on('click', this.onViewClick, this);
22956
22957         this.store.on('beforeload', this.onBeforeLoad, this);
22958         this.store.on('load', this.onLoad, this);
22959         this.store.on('loadexception', this.collapse, this);
22960
22961         if(this.resizable){
22962             this.resizer = new Roo.Resizable(this.list,  {
22963                pinned:true, handles:'se'
22964             });
22965             this.resizer.on('resize', function(r, w, h){
22966                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22967                 this.listWidth = w;
22968                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22969                 this.restrictHeight();
22970             }, this);
22971             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22972         }
22973         if(!this.editable){
22974             this.editable = true;
22975             this.setEditable(false);
22976         }  
22977         
22978         
22979         if (typeof(this.events.add.listeners) != 'undefined') {
22980             
22981             this.addicon = this.wrap.createChild(
22982                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22983        
22984             this.addicon.on('click', function(e) {
22985                 this.fireEvent('add', this);
22986             }, this);
22987         }
22988         if (typeof(this.events.edit.listeners) != 'undefined') {
22989             
22990             this.editicon = this.wrap.createChild(
22991                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22992             if (this.addicon) {
22993                 this.editicon.setStyle('margin-left', '40px');
22994             }
22995             this.editicon.on('click', function(e) {
22996                 
22997                 // we fire even  if inothing is selected..
22998                 this.fireEvent('edit', this, this.lastData );
22999                 
23000             }, this);
23001         }
23002         
23003         
23004         
23005     },
23006
23007     // private
23008     initEvents : function(){
23009         Roo.form.ComboBox.superclass.initEvents.call(this);
23010
23011         this.keyNav = new Roo.KeyNav(this.el, {
23012             "up" : function(e){
23013                 this.inKeyMode = true;
23014                 this.selectPrev();
23015             },
23016
23017             "down" : function(e){
23018                 if(!this.isExpanded()){
23019                     this.onTriggerClick();
23020                 }else{
23021                     this.inKeyMode = true;
23022                     this.selectNext();
23023                 }
23024             },
23025
23026             "enter" : function(e){
23027                 this.onViewClick();
23028                 //return true;
23029             },
23030
23031             "esc" : function(e){
23032                 this.collapse();
23033             },
23034
23035             "tab" : function(e){
23036                 this.onViewClick(false);
23037                 return true;
23038             },
23039
23040             scope : this,
23041
23042             doRelay : function(foo, bar, hname){
23043                 if(hname == 'down' || this.scope.isExpanded()){
23044                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23045                 }
23046                 return true;
23047             },
23048
23049             forceKeyDown: true
23050         });
23051         this.queryDelay = Math.max(this.queryDelay || 10,
23052                 this.mode == 'local' ? 10 : 250);
23053         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23054         if(this.typeAhead){
23055             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23056         }
23057         if(this.editable !== false){
23058             this.el.on("keyup", this.onKeyUp, this);
23059         }
23060         if(this.forceSelection){
23061             this.on('blur', this.doForce, this);
23062         }
23063     },
23064
23065     onDestroy : function(){
23066         if(this.view){
23067             this.view.setStore(null);
23068             this.view.el.removeAllListeners();
23069             this.view.el.remove();
23070             this.view.purgeListeners();
23071         }
23072         if(this.list){
23073             this.list.destroy();
23074         }
23075         if(this.store){
23076             this.store.un('beforeload', this.onBeforeLoad, this);
23077             this.store.un('load', this.onLoad, this);
23078             this.store.un('loadexception', this.collapse, this);
23079         }
23080         Roo.form.ComboBox.superclass.onDestroy.call(this);
23081     },
23082
23083     // private
23084     fireKey : function(e){
23085         if(e.isNavKeyPress() && !this.list.isVisible()){
23086             this.fireEvent("specialkey", this, e);
23087         }
23088     },
23089
23090     // private
23091     onResize: function(w, h){
23092         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23093         
23094         if(typeof w != 'number'){
23095             // we do not handle it!?!?
23096             return;
23097         }
23098         var tw = this.trigger.getWidth();
23099         tw += this.addicon ? this.addicon.getWidth() : 0;
23100         tw += this.editicon ? this.editicon.getWidth() : 0;
23101         var x = w - tw;
23102         this.el.setWidth( this.adjustWidth('input', x));
23103             
23104         this.trigger.setStyle('left', x+'px');
23105         
23106         if(this.list && this.listWidth === undefined){
23107             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23108             this.list.setWidth(lw);
23109             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23110         }
23111         
23112     
23113         
23114     },
23115
23116     /**
23117      * Allow or prevent the user from directly editing the field text.  If false is passed,
23118      * the user will only be able to select from the items defined in the dropdown list.  This method
23119      * is the runtime equivalent of setting the 'editable' config option at config time.
23120      * @param {Boolean} value True to allow the user to directly edit the field text
23121      */
23122     setEditable : function(value){
23123         if(value == this.editable){
23124             return;
23125         }
23126         this.editable = value;
23127         if(!value){
23128             this.el.dom.setAttribute('readOnly', true);
23129             this.el.on('mousedown', this.onTriggerClick,  this);
23130             this.el.addClass('x-combo-noedit');
23131         }else{
23132             this.el.dom.setAttribute('readOnly', false);
23133             this.el.un('mousedown', this.onTriggerClick,  this);
23134             this.el.removeClass('x-combo-noedit');
23135         }
23136     },
23137
23138     // private
23139     onBeforeLoad : function(){
23140         if(!this.hasFocus){
23141             return;
23142         }
23143         this.innerList.update(this.loadingText ?
23144                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23145         this.restrictHeight();
23146         this.selectedIndex = -1;
23147     },
23148
23149     // private
23150     onLoad : function(){
23151         if(!this.hasFocus){
23152             return;
23153         }
23154         if(this.store.getCount() > 0){
23155             this.expand();
23156             this.restrictHeight();
23157             if(this.lastQuery == this.allQuery){
23158                 if(this.editable){
23159                     this.el.dom.select();
23160                 }
23161                 if(!this.selectByValue(this.value, true)){
23162                     this.select(0, true);
23163                 }
23164             }else{
23165                 this.selectNext();
23166                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23167                     this.taTask.delay(this.typeAheadDelay);
23168                 }
23169             }
23170         }else{
23171             this.onEmptyResults();
23172         }
23173         //this.el.focus();
23174     },
23175
23176     // private
23177     onTypeAhead : function(){
23178         if(this.store.getCount() > 0){
23179             var r = this.store.getAt(0);
23180             var newValue = r.data[this.displayField];
23181             var len = newValue.length;
23182             var selStart = this.getRawValue().length;
23183             if(selStart != len){
23184                 this.setRawValue(newValue);
23185                 this.selectText(selStart, newValue.length);
23186             }
23187         }
23188     },
23189
23190     // private
23191     onSelect : function(record, index){
23192         if(this.fireEvent('beforeselect', this, record, index) !== false){
23193             this.setFromData(index > -1 ? record.data : false);
23194             this.collapse();
23195             this.fireEvent('select', this, record, index);
23196         }
23197     },
23198
23199     /**
23200      * Returns the currently selected field value or empty string if no value is set.
23201      * @return {String} value The selected value
23202      */
23203     getValue : function(){
23204         if(this.valueField){
23205             return typeof this.value != 'undefined' ? this.value : '';
23206         }else{
23207             return Roo.form.ComboBox.superclass.getValue.call(this);
23208         }
23209     },
23210
23211     /**
23212      * Clears any text/value currently set in the field
23213      */
23214     clearValue : function(){
23215         if(this.hiddenField){
23216             this.hiddenField.value = '';
23217         }
23218         this.value = '';
23219         this.setRawValue('');
23220         this.lastSelectionText = '';
23221         this.applyEmptyText();
23222     },
23223
23224     /**
23225      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23226      * will be displayed in the field.  If the value does not match the data value of an existing item,
23227      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23228      * Otherwise the field will be blank (although the value will still be set).
23229      * @param {String} value The value to match
23230      */
23231     setValue : function(v){
23232         var text = v;
23233         if(this.valueField){
23234             var r = this.findRecord(this.valueField, v);
23235             if(r){
23236                 text = r.data[this.displayField];
23237             }else if(this.valueNotFoundText !== undefined){
23238                 text = this.valueNotFoundText;
23239             }
23240         }
23241         this.lastSelectionText = text;
23242         if(this.hiddenField){
23243             this.hiddenField.value = v;
23244         }
23245         Roo.form.ComboBox.superclass.setValue.call(this, text);
23246         this.value = v;
23247     },
23248     /**
23249      * @property {Object} the last set data for the element
23250      */
23251     
23252     lastData : false,
23253     /**
23254      * Sets the value of the field based on a object which is related to the record format for the store.
23255      * @param {Object} value the value to set as. or false on reset?
23256      */
23257     setFromData : function(o){
23258         var dv = ''; // display value
23259         var vv = ''; // value value..
23260         this.lastData = o;
23261         if (this.displayField) {
23262             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23263         } else {
23264             // this is an error condition!!!
23265             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23266         }
23267         
23268         if(this.valueField){
23269             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23270         }
23271         if(this.hiddenField){
23272             this.hiddenField.value = vv;
23273             
23274             this.lastSelectionText = dv;
23275             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23276             this.value = vv;
23277             return;
23278         }
23279         // no hidden field.. - we store the value in 'value', but still display
23280         // display field!!!!
23281         this.lastSelectionText = dv;
23282         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23283         this.value = vv;
23284         
23285         
23286     },
23287     // private
23288     reset : function(){
23289         // overridden so that last data is reset..
23290         this.setValue(this.originalValue);
23291         this.clearInvalid();
23292         this.lastData = false;
23293     },
23294     // private
23295     findRecord : function(prop, value){
23296         var record;
23297         if(this.store.getCount() > 0){
23298             this.store.each(function(r){
23299                 if(r.data[prop] == value){
23300                     record = r;
23301                     return false;
23302                 }
23303             });
23304         }
23305         return record;
23306     },
23307
23308     // private
23309     onViewMove : function(e, t){
23310         this.inKeyMode = false;
23311     },
23312
23313     // private
23314     onViewOver : function(e, t){
23315         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23316             return;
23317         }
23318         var item = this.view.findItemFromChild(t);
23319         if(item){
23320             var index = this.view.indexOf(item);
23321             this.select(index, false);
23322         }
23323     },
23324
23325     // private
23326     onViewClick : function(doFocus){
23327         var index = this.view.getSelectedIndexes()[0];
23328         var r = this.store.getAt(index);
23329         if(r){
23330             this.onSelect(r, index);
23331         }
23332         if(doFocus !== false && !this.blockFocus){
23333             this.el.focus();
23334         }
23335     },
23336
23337     // private
23338     restrictHeight : function(){
23339         this.innerList.dom.style.height = '';
23340         var inner = this.innerList.dom;
23341         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23342         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23343         this.list.beginUpdate();
23344         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23345         this.list.alignTo(this.el, this.listAlign);
23346         this.list.endUpdate();
23347     },
23348
23349     // private
23350     onEmptyResults : function(){
23351         this.collapse();
23352     },
23353
23354     /**
23355      * Returns true if the dropdown list is expanded, else false.
23356      */
23357     isExpanded : function(){
23358         return this.list.isVisible();
23359     },
23360
23361     /**
23362      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23363      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23364      * @param {String} value The data value of the item to select
23365      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23366      * selected item if it is not currently in view (defaults to true)
23367      * @return {Boolean} True if the value matched an item in the list, else false
23368      */
23369     selectByValue : function(v, scrollIntoView){
23370         if(v !== undefined && v !== null){
23371             var r = this.findRecord(this.valueField || this.displayField, v);
23372             if(r){
23373                 this.select(this.store.indexOf(r), scrollIntoView);
23374                 return true;
23375             }
23376         }
23377         return false;
23378     },
23379
23380     /**
23381      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23382      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23383      * @param {Number} index The zero-based index of the list item to select
23384      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23385      * selected item if it is not currently in view (defaults to true)
23386      */
23387     select : function(index, scrollIntoView){
23388         this.selectedIndex = index;
23389         this.view.select(index);
23390         if(scrollIntoView !== false){
23391             var el = this.view.getNode(index);
23392             if(el){
23393                 this.innerList.scrollChildIntoView(el, false);
23394             }
23395         }
23396     },
23397
23398     // private
23399     selectNext : function(){
23400         var ct = this.store.getCount();
23401         if(ct > 0){
23402             if(this.selectedIndex == -1){
23403                 this.select(0);
23404             }else if(this.selectedIndex < ct-1){
23405                 this.select(this.selectedIndex+1);
23406             }
23407         }
23408     },
23409
23410     // private
23411     selectPrev : function(){
23412         var ct = this.store.getCount();
23413         if(ct > 0){
23414             if(this.selectedIndex == -1){
23415                 this.select(0);
23416             }else if(this.selectedIndex != 0){
23417                 this.select(this.selectedIndex-1);
23418             }
23419         }
23420     },
23421
23422     // private
23423     onKeyUp : function(e){
23424         if(this.editable !== false && !e.isSpecialKey()){
23425             this.lastKey = e.getKey();
23426             this.dqTask.delay(this.queryDelay);
23427         }
23428     },
23429
23430     // private
23431     validateBlur : function(){
23432         return !this.list || !this.list.isVisible();   
23433     },
23434
23435     // private
23436     initQuery : function(){
23437         this.doQuery(this.getRawValue());
23438     },
23439
23440     // private
23441     doForce : function(){
23442         if(this.el.dom.value.length > 0){
23443             this.el.dom.value =
23444                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23445             this.applyEmptyText();
23446         }
23447     },
23448
23449     /**
23450      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23451      * query allowing the query action to be canceled if needed.
23452      * @param {String} query The SQL query to execute
23453      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23454      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23455      * saved in the current store (defaults to false)
23456      */
23457     doQuery : function(q, forceAll){
23458         if(q === undefined || q === null){
23459             q = '';
23460         }
23461         var qe = {
23462             query: q,
23463             forceAll: forceAll,
23464             combo: this,
23465             cancel:false
23466         };
23467         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23468             return false;
23469         }
23470         q = qe.query;
23471         forceAll = qe.forceAll;
23472         if(forceAll === true || (q.length >= this.minChars)){
23473             if(this.lastQuery != q || this.alwaysQuery){
23474                 this.lastQuery = q;
23475                 if(this.mode == 'local'){
23476                     this.selectedIndex = -1;
23477                     if(forceAll){
23478                         this.store.clearFilter();
23479                     }else{
23480                         this.store.filter(this.displayField, q);
23481                     }
23482                     this.onLoad();
23483                 }else{
23484                     this.store.baseParams[this.queryParam] = q;
23485                     this.store.load({
23486                         params: this.getParams(q)
23487                     });
23488                     this.expand();
23489                 }
23490             }else{
23491                 this.selectedIndex = -1;
23492                 this.onLoad();   
23493             }
23494         }
23495     },
23496
23497     // private
23498     getParams : function(q){
23499         var p = {};
23500         //p[this.queryParam] = q;
23501         if(this.pageSize){
23502             p.start = 0;
23503             p.limit = this.pageSize;
23504         }
23505         return p;
23506     },
23507
23508     /**
23509      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23510      */
23511     collapse : function(){
23512         if(!this.isExpanded()){
23513             return;
23514         }
23515         this.list.hide();
23516         Roo.get(document).un('mousedown', this.collapseIf, this);
23517         Roo.get(document).un('mousewheel', this.collapseIf, this);
23518         if (!this.editable) {
23519             Roo.get(document).un('keydown', this.listKeyPress, this);
23520         }
23521         this.fireEvent('collapse', this);
23522     },
23523
23524     // private
23525     collapseIf : function(e){
23526         if(!e.within(this.wrap) && !e.within(this.list)){
23527             this.collapse();
23528         }
23529     },
23530
23531     /**
23532      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23533      */
23534     expand : function(){
23535         if(this.isExpanded() || !this.hasFocus){
23536             return;
23537         }
23538         this.list.alignTo(this.el, this.listAlign);
23539         this.list.show();
23540         Roo.get(document).on('mousedown', this.collapseIf, this);
23541         Roo.get(document).on('mousewheel', this.collapseIf, this);
23542         if (!this.editable) {
23543             Roo.get(document).on('keydown', this.listKeyPress, this);
23544         }
23545         
23546         this.fireEvent('expand', this);
23547     },
23548
23549     // private
23550     // Implements the default empty TriggerField.onTriggerClick function
23551     onTriggerClick : function(){
23552         if(this.disabled){
23553             return;
23554         }
23555         if(this.isExpanded()){
23556             this.collapse();
23557             if (!this.blockFocus) {
23558                 this.el.focus();
23559             }
23560             
23561         }else {
23562             this.hasFocus = true;
23563             if(this.triggerAction == 'all') {
23564                 this.doQuery(this.allQuery, true);
23565             } else {
23566                 this.doQuery(this.getRawValue());
23567             }
23568             if (!this.blockFocus) {
23569                 this.el.focus();
23570             }
23571         }
23572     },
23573     listKeyPress : function(e)
23574     {
23575         //Roo.log('listkeypress');
23576         // scroll to first matching element based on key pres..
23577         if (e.isSpecialKey()) {
23578             return false;
23579         }
23580         var k = String.fromCharCode(e.getKey()).toUpperCase();
23581         //Roo.log(k);
23582         var match  = false;
23583         var csel = this.view.getSelectedNodes();
23584         var cselitem = false;
23585         if (csel.length) {
23586             var ix = this.view.indexOf(csel[0]);
23587             cselitem  = this.store.getAt(ix);
23588             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23589                 cselitem = false;
23590             }
23591             
23592         }
23593         
23594         this.store.each(function(v) { 
23595             if (cselitem) {
23596                 // start at existing selection.
23597                 if (cselitem.id == v.id) {
23598                     cselitem = false;
23599                 }
23600                 return;
23601             }
23602                 
23603             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23604                 match = this.store.indexOf(v);
23605                 return false;
23606             }
23607         }, this);
23608         
23609         if (match === false) {
23610             return true; // no more action?
23611         }
23612         // scroll to?
23613         this.view.select(match);
23614         var sn = Roo.get(this.view.getSelectedNodes()[0])
23615         sn.scrollIntoView(sn.dom.parentNode, false);
23616     }
23617
23618     /** 
23619     * @cfg {Boolean} grow 
23620     * @hide 
23621     */
23622     /** 
23623     * @cfg {Number} growMin 
23624     * @hide 
23625     */
23626     /** 
23627     * @cfg {Number} growMax 
23628     * @hide 
23629     */
23630     /**
23631      * @hide
23632      * @method autoSize
23633      */
23634 });/*
23635  * Based on:
23636  * Ext JS Library 1.1.1
23637  * Copyright(c) 2006-2007, Ext JS, LLC.
23638  *
23639  * Originally Released Under LGPL - original licence link has changed is not relivant.
23640  *
23641  * Fork - LGPL
23642  * <script type="text/javascript">
23643  */
23644 /**
23645  * @class Roo.form.Checkbox
23646  * @extends Roo.form.Field
23647  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23648  * @constructor
23649  * Creates a new Checkbox
23650  * @param {Object} config Configuration options
23651  */
23652 Roo.form.Checkbox = function(config){
23653     Roo.form.Checkbox.superclass.constructor.call(this, config);
23654     this.addEvents({
23655         /**
23656          * @event check
23657          * Fires when the checkbox is checked or unchecked.
23658              * @param {Roo.form.Checkbox} this This checkbox
23659              * @param {Boolean} checked The new checked value
23660              */
23661         check : true
23662     });
23663 };
23664
23665 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23666     /**
23667      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23668      */
23669     focusClass : undefined,
23670     /**
23671      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23672      */
23673     fieldClass: "x-form-field",
23674     /**
23675      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23676      */
23677     checked: false,
23678     /**
23679      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23680      * {tag: "input", type: "checkbox", autocomplete: "off"})
23681      */
23682     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23683     /**
23684      * @cfg {String} boxLabel The text that appears beside the checkbox
23685      */
23686     boxLabel : "",
23687     /**
23688      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23689      */  
23690     inputValue : '1',
23691     /**
23692      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23693      */
23694      valueOff: '0', // value when not checked..
23695
23696     actionMode : 'viewEl', 
23697     //
23698     // private
23699     itemCls : 'x-menu-check-item x-form-item',
23700     groupClass : 'x-menu-group-item',
23701     inputType : 'hidden',
23702     
23703     
23704     inSetChecked: false, // check that we are not calling self...
23705     
23706     inputElement: false, // real input element?
23707     basedOn: false, // ????
23708     
23709     isFormField: true, // not sure where this is needed!!!!
23710
23711     onResize : function(){
23712         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23713         if(!this.boxLabel){
23714             this.el.alignTo(this.wrap, 'c-c');
23715         }
23716     },
23717
23718     initEvents : function(){
23719         Roo.form.Checkbox.superclass.initEvents.call(this);
23720         this.el.on("click", this.onClick,  this);
23721         this.el.on("change", this.onClick,  this);
23722     },
23723
23724
23725     getResizeEl : function(){
23726         return this.wrap;
23727     },
23728
23729     getPositionEl : function(){
23730         return this.wrap;
23731     },
23732
23733     // private
23734     onRender : function(ct, position){
23735         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23736         /*
23737         if(this.inputValue !== undefined){
23738             this.el.dom.value = this.inputValue;
23739         }
23740         */
23741         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23742         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23743         var viewEl = this.wrap.createChild({ 
23744             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23745         this.viewEl = viewEl;   
23746         this.wrap.on('click', this.onClick,  this); 
23747         
23748         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23749         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23750         
23751         
23752         
23753         if(this.boxLabel){
23754             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23755         //    viewEl.on('click', this.onClick,  this); 
23756         }
23757         //if(this.checked){
23758             this.setChecked(this.checked);
23759         //}else{
23760             //this.checked = this.el.dom;
23761         //}
23762
23763     },
23764
23765     // private
23766     initValue : Roo.emptyFn,
23767
23768     /**
23769      * Returns the checked state of the checkbox.
23770      * @return {Boolean} True if checked, else false
23771      */
23772     getValue : function(){
23773         if(this.el){
23774             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23775         }
23776         return this.valueOff;
23777         
23778     },
23779
23780         // private
23781     onClick : function(){ 
23782         this.setChecked(!this.checked);
23783
23784         //if(this.el.dom.checked != this.checked){
23785         //    this.setValue(this.el.dom.checked);
23786        // }
23787     },
23788
23789     /**
23790      * Sets the checked state of the checkbox.
23791      * On is always based on a string comparison between inputValue and the param.
23792      * @param {Boolean/String} value - the value to set 
23793      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23794      */
23795     setValue : function(v,suppressEvent){
23796         
23797         
23798         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23799         //if(this.el && this.el.dom){
23800         //    this.el.dom.checked = this.checked;
23801         //    this.el.dom.defaultChecked = this.checked;
23802         //}
23803         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23804         //this.fireEvent("check", this, this.checked);
23805     },
23806     // private..
23807     setChecked : function(state,suppressEvent)
23808     {
23809         if (this.inSetChecked) {
23810             this.checked = state;
23811             return;
23812         }
23813         
23814     
23815         if(this.wrap){
23816             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23817         }
23818         this.checked = state;
23819         if(suppressEvent !== true){
23820             this.fireEvent('check', this, state);
23821         }
23822         this.inSetChecked = true;
23823         this.el.dom.value = state ? this.inputValue : this.valueOff;
23824         this.inSetChecked = false;
23825         
23826     },
23827     // handle setting of hidden value by some other method!!?!?
23828     setFromHidden: function()
23829     {
23830         if(!this.el){
23831             return;
23832         }
23833         //console.log("SET FROM HIDDEN");
23834         //alert('setFrom hidden');
23835         this.setValue(this.el.dom.value);
23836     },
23837     
23838     onDestroy : function()
23839     {
23840         if(this.viewEl){
23841             Roo.get(this.viewEl).remove();
23842         }
23843          
23844         Roo.form.Checkbox.superclass.onDestroy.call(this);
23845     }
23846
23847 });/*
23848  * Based on:
23849  * Ext JS Library 1.1.1
23850  * Copyright(c) 2006-2007, Ext JS, LLC.
23851  *
23852  * Originally Released Under LGPL - original licence link has changed is not relivant.
23853  *
23854  * Fork - LGPL
23855  * <script type="text/javascript">
23856  */
23857  
23858 /**
23859  * @class Roo.form.Radio
23860  * @extends Roo.form.Checkbox
23861  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23862  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23863  * @constructor
23864  * Creates a new Radio
23865  * @param {Object} config Configuration options
23866  */
23867 Roo.form.Radio = function(){
23868     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23869 };
23870 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23871     inputType: 'radio',
23872
23873     /**
23874      * If this radio is part of a group, it will return the selected value
23875      * @return {String}
23876      */
23877     getGroupValue : function(){
23878         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23879     }
23880 });//<script type="text/javascript">
23881
23882 /*
23883  * Ext JS Library 1.1.1
23884  * Copyright(c) 2006-2007, Ext JS, LLC.
23885  * licensing@extjs.com
23886  * 
23887  * http://www.extjs.com/license
23888  */
23889  
23890  /*
23891   * 
23892   * Known bugs:
23893   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23894   * - IE ? - no idea how much works there.
23895   * 
23896   * 
23897   * 
23898   */
23899  
23900
23901 /**
23902  * @class Ext.form.HtmlEditor
23903  * @extends Ext.form.Field
23904  * Provides a lightweight HTML Editor component.
23905  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23906  * 
23907  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23908  * supported by this editor.</b><br/><br/>
23909  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23910  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23911  */
23912 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23913       /**
23914      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23915      */
23916     toolbars : false,
23917     /**
23918      * @cfg {String} createLinkText The default text for the create link prompt
23919      */
23920     createLinkText : 'Please enter the URL for the link:',
23921     /**
23922      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23923      */
23924     defaultLinkValue : 'http:/'+'/',
23925    
23926     
23927     // id of frame..
23928     frameId: false,
23929     
23930     // private properties
23931     validationEvent : false,
23932     deferHeight: true,
23933     initialized : false,
23934     activated : false,
23935     sourceEditMode : false,
23936     onFocus : Roo.emptyFn,
23937     iframePad:3,
23938     hideMode:'offsets',
23939     defaultAutoCreate : {
23940         tag: "textarea",
23941         style:"width:500px;height:300px;",
23942         autocomplete: "off"
23943     },
23944
23945     // private
23946     initComponent : function(){
23947         this.addEvents({
23948             /**
23949              * @event initialize
23950              * Fires when the editor is fully initialized (including the iframe)
23951              * @param {HtmlEditor} this
23952              */
23953             initialize: true,
23954             /**
23955              * @event activate
23956              * Fires when the editor is first receives the focus. Any insertion must wait
23957              * until after this event.
23958              * @param {HtmlEditor} this
23959              */
23960             activate: true,
23961              /**
23962              * @event beforesync
23963              * Fires before the textarea is updated with content from the editor iframe. Return false
23964              * to cancel the sync.
23965              * @param {HtmlEditor} this
23966              * @param {String} html
23967              */
23968             beforesync: true,
23969              /**
23970              * @event beforepush
23971              * Fires before the iframe editor is updated with content from the textarea. Return false
23972              * to cancel the push.
23973              * @param {HtmlEditor} this
23974              * @param {String} html
23975              */
23976             beforepush: true,
23977              /**
23978              * @event sync
23979              * Fires when the textarea is updated with content from the editor iframe.
23980              * @param {HtmlEditor} this
23981              * @param {String} html
23982              */
23983             sync: true,
23984              /**
23985              * @event push
23986              * Fires when the iframe editor is updated with content from the textarea.
23987              * @param {HtmlEditor} this
23988              * @param {String} html
23989              */
23990             push: true,
23991              /**
23992              * @event editmodechange
23993              * Fires when the editor switches edit modes
23994              * @param {HtmlEditor} this
23995              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23996              */
23997             editmodechange: true,
23998             /**
23999              * @event editorevent
24000              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24001              * @param {HtmlEditor} this
24002              */
24003             editorevent: true
24004         })
24005     },
24006
24007     /**
24008      * Protected method that will not generally be called directly. It
24009      * is called when the editor creates its toolbar. Override this method if you need to
24010      * add custom toolbar buttons.
24011      * @param {HtmlEditor} editor
24012      */
24013     createToolbar : function(editor){
24014         if (!editor.toolbars || !editor.toolbars.length) {
24015             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24016         }
24017         
24018         for (var i =0 ; i < editor.toolbars.length;i++) {
24019             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24020             editor.toolbars[i].init(editor);
24021         }
24022          
24023         
24024     },
24025
24026     /**
24027      * Protected method that will not generally be called directly. It
24028      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24029      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24030      */
24031     getDocMarkup : function(){
24032         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24033     },
24034
24035     // private
24036     onRender : function(ct, position){
24037         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24038         this.el.dom.style.border = '0 none';
24039         this.el.dom.setAttribute('tabIndex', -1);
24040         this.el.addClass('x-hidden');
24041         if(Roo.isIE){ // fix IE 1px bogus margin
24042             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24043         }
24044         this.wrap = this.el.wrap({
24045             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24046         });
24047
24048         this.frameId = Roo.id();
24049         this.createToolbar(this);
24050         
24051         
24052         
24053         
24054       
24055         
24056         var iframe = this.wrap.createChild({
24057             tag: 'iframe',
24058             id: this.frameId,
24059             name: this.frameId,
24060             frameBorder : 'no',
24061             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24062         });
24063         
24064        // console.log(iframe);
24065         //this.wrap.dom.appendChild(iframe);
24066
24067         this.iframe = iframe.dom;
24068
24069          this.assignDocWin();
24070         
24071         this.doc.designMode = 'on';
24072        
24073         this.doc.open();
24074         this.doc.write(this.getDocMarkup());
24075         this.doc.close();
24076
24077         
24078         var task = { // must defer to wait for browser to be ready
24079             run : function(){
24080                 //console.log("run task?" + this.doc.readyState);
24081                 this.assignDocWin();
24082                 if(this.doc.body || this.doc.readyState == 'complete'){
24083                     try {
24084                         this.doc.designMode="on";
24085                     } catch (e) {
24086                         return;
24087                     }
24088                     Roo.TaskMgr.stop(task);
24089                     this.initEditor.defer(10, this);
24090                 }
24091             },
24092             interval : 10,
24093             duration:10000,
24094             scope: this
24095         };
24096         Roo.TaskMgr.start(task);
24097
24098         if(!this.width){
24099             this.setSize(this.el.getSize());
24100         }
24101     },
24102
24103     // private
24104     onResize : function(w, h){
24105         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24106         if(this.el && this.iframe){
24107             if(typeof w == 'number'){
24108                 var aw = w - this.wrap.getFrameWidth('lr');
24109                 this.el.setWidth(this.adjustWidth('textarea', aw));
24110                 this.iframe.style.width = aw + 'px';
24111             }
24112             if(typeof h == 'number'){
24113                 var tbh = 0;
24114                 for (var i =0; i < this.toolbars.length;i++) {
24115                     // fixme - ask toolbars for heights?
24116                     tbh += this.toolbars[i].tb.el.getHeight();
24117                 }
24118                 
24119                 
24120                 
24121                 
24122                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24123                 this.el.setHeight(this.adjustWidth('textarea', ah));
24124                 this.iframe.style.height = ah + 'px';
24125                 if(this.doc){
24126                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24127                 }
24128             }
24129         }
24130     },
24131
24132     /**
24133      * Toggles the editor between standard and source edit mode.
24134      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24135      */
24136     toggleSourceEdit : function(sourceEditMode){
24137         
24138         this.sourceEditMode = sourceEditMode === true;
24139         
24140         if(this.sourceEditMode){
24141           
24142             this.syncValue();
24143             this.iframe.className = 'x-hidden';
24144             this.el.removeClass('x-hidden');
24145             this.el.dom.removeAttribute('tabIndex');
24146             this.el.focus();
24147         }else{
24148              
24149             this.pushValue();
24150             this.iframe.className = '';
24151             this.el.addClass('x-hidden');
24152             this.el.dom.setAttribute('tabIndex', -1);
24153             this.deferFocus();
24154         }
24155         this.setSize(this.wrap.getSize());
24156         this.fireEvent('editmodechange', this, this.sourceEditMode);
24157     },
24158
24159     // private used internally
24160     createLink : function(){
24161         var url = prompt(this.createLinkText, this.defaultLinkValue);
24162         if(url && url != 'http:/'+'/'){
24163             this.relayCmd('createlink', url);
24164         }
24165     },
24166
24167     // private (for BoxComponent)
24168     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24169
24170     // private (for BoxComponent)
24171     getResizeEl : function(){
24172         return this.wrap;
24173     },
24174
24175     // private (for BoxComponent)
24176     getPositionEl : function(){
24177         return this.wrap;
24178     },
24179
24180     // private
24181     initEvents : function(){
24182         this.originalValue = this.getValue();
24183     },
24184
24185     /**
24186      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24187      * @method
24188      */
24189     markInvalid : Roo.emptyFn,
24190     /**
24191      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24192      * @method
24193      */
24194     clearInvalid : Roo.emptyFn,
24195
24196     setValue : function(v){
24197         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24198         this.pushValue();
24199     },
24200
24201     /**
24202      * Protected method that will not generally be called directly. If you need/want
24203      * custom HTML cleanup, this is the method you should override.
24204      * @param {String} html The HTML to be cleaned
24205      * return {String} The cleaned HTML
24206      */
24207     cleanHtml : function(html){
24208         html = String(html);
24209         if(html.length > 5){
24210             if(Roo.isSafari){ // strip safari nonsense
24211                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24212             }
24213         }
24214         if(html == '&nbsp;'){
24215             html = '';
24216         }
24217         return html;
24218     },
24219
24220     /**
24221      * Protected method that will not generally be called directly. Syncs the contents
24222      * of the editor iframe with the textarea.
24223      */
24224     syncValue : function(){
24225         if(this.initialized){
24226             var bd = (this.doc.body || this.doc.documentElement);
24227             this.cleanUpPaste();
24228             var html = bd.innerHTML;
24229             if(Roo.isSafari){
24230                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24231                 var m = bs.match(/text-align:(.*?);/i);
24232                 if(m && m[1]){
24233                     html = '<div style="'+m[0]+'">' + html + '</div>';
24234                 }
24235             }
24236             html = this.cleanHtml(html);
24237             if(this.fireEvent('beforesync', this, html) !== false){
24238                 this.el.dom.value = html;
24239                 this.fireEvent('sync', this, html);
24240             }
24241         }
24242     },
24243
24244     /**
24245      * Protected method that will not generally be called directly. Pushes the value of the textarea
24246      * into the iframe editor.
24247      */
24248     pushValue : function(){
24249         if(this.initialized){
24250             var v = this.el.dom.value;
24251             if(v.length < 1){
24252                 v = '&#160;';
24253             }
24254             
24255             if(this.fireEvent('beforepush', this, v) !== false){
24256                 var d = (this.doc.body || this.doc.documentElement);
24257                 d.innerHTML = v;
24258                 this.cleanUpPaste();
24259                 this.el.dom.value = d.innerHTML;
24260                 this.fireEvent('push', this, v);
24261             }
24262         }
24263     },
24264
24265     // private
24266     deferFocus : function(){
24267         this.focus.defer(10, this);
24268     },
24269
24270     // doc'ed in Field
24271     focus : function(){
24272         if(this.win && !this.sourceEditMode){
24273             this.win.focus();
24274         }else{
24275             this.el.focus();
24276         }
24277     },
24278     
24279     assignDocWin: function()
24280     {
24281         var iframe = this.iframe;
24282         
24283          if(Roo.isIE){
24284             this.doc = iframe.contentWindow.document;
24285             this.win = iframe.contentWindow;
24286         } else {
24287             if (!Roo.get(this.frameId)) {
24288                 return;
24289             }
24290             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24291             this.win = Roo.get(this.frameId).dom.contentWindow;
24292         }
24293     },
24294     
24295     // private
24296     initEditor : function(){
24297         //console.log("INIT EDITOR");
24298         this.assignDocWin();
24299         
24300         
24301         
24302         this.doc.designMode="on";
24303         this.doc.open();
24304         this.doc.write(this.getDocMarkup());
24305         this.doc.close();
24306         
24307         var dbody = (this.doc.body || this.doc.documentElement);
24308         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24309         // this copies styles from the containing element into thsi one..
24310         // not sure why we need all of this..
24311         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24312         ss['background-attachment'] = 'fixed'; // w3c
24313         dbody.bgProperties = 'fixed'; // ie
24314         Roo.DomHelper.applyStyles(dbody, ss);
24315         Roo.EventManager.on(this.doc, {
24316             'mousedown': this.onEditorEvent,
24317             'dblclick': this.onEditorEvent,
24318             'click': this.onEditorEvent,
24319             'keyup': this.onEditorEvent,
24320             buffer:100,
24321             scope: this
24322         });
24323         if(Roo.isGecko){
24324             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24325         }
24326         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24327             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24328         }
24329         this.initialized = true;
24330
24331         this.fireEvent('initialize', this);
24332         this.pushValue();
24333     },
24334
24335     // private
24336     onDestroy : function(){
24337         
24338         
24339         
24340         if(this.rendered){
24341             
24342             for (var i =0; i < this.toolbars.length;i++) {
24343                 // fixme - ask toolbars for heights?
24344                 this.toolbars[i].onDestroy();
24345             }
24346             
24347             this.wrap.dom.innerHTML = '';
24348             this.wrap.remove();
24349         }
24350     },
24351
24352     // private
24353     onFirstFocus : function(){
24354         
24355         this.assignDocWin();
24356         
24357         
24358         this.activated = true;
24359         for (var i =0; i < this.toolbars.length;i++) {
24360             this.toolbars[i].onFirstFocus();
24361         }
24362        
24363         if(Roo.isGecko){ // prevent silly gecko errors
24364             this.win.focus();
24365             var s = this.win.getSelection();
24366             if(!s.focusNode || s.focusNode.nodeType != 3){
24367                 var r = s.getRangeAt(0);
24368                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24369                 r.collapse(true);
24370                 this.deferFocus();
24371             }
24372             try{
24373                 this.execCmd('useCSS', true);
24374                 this.execCmd('styleWithCSS', false);
24375             }catch(e){}
24376         }
24377         this.fireEvent('activate', this);
24378     },
24379
24380     // private
24381     adjustFont: function(btn){
24382         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24383         //if(Roo.isSafari){ // safari
24384         //    adjust *= 2;
24385        // }
24386         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24387         if(Roo.isSafari){ // safari
24388             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24389             v =  (v < 10) ? 10 : v;
24390             v =  (v > 48) ? 48 : v;
24391             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24392             
24393         }
24394         
24395         
24396         v = Math.max(1, v+adjust);
24397         
24398         this.execCmd('FontSize', v  );
24399     },
24400
24401     onEditorEvent : function(e){
24402         this.fireEvent('editorevent', this, e);
24403       //  this.updateToolbar();
24404         this.syncValue();
24405     },
24406
24407     insertTag : function(tg)
24408     {
24409         // could be a bit smarter... -> wrap the current selected tRoo..
24410         
24411         this.execCmd("formatblock",   tg);
24412         
24413     },
24414     
24415     insertText : function(txt)
24416     {
24417         
24418         
24419         range = this.createRange();
24420         range.deleteContents();
24421                //alert(Sender.getAttribute('label'));
24422                
24423         range.insertNode(this.doc.createTextNode(txt));
24424     } ,
24425     
24426     // private
24427     relayBtnCmd : function(btn){
24428         this.relayCmd(btn.cmd);
24429     },
24430
24431     /**
24432      * Executes a Midas editor command on the editor document and performs necessary focus and
24433      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24434      * @param {String} cmd The Midas command
24435      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24436      */
24437     relayCmd : function(cmd, value){
24438         this.win.focus();
24439         this.execCmd(cmd, value);
24440         this.fireEvent('editorevent', this);
24441         //this.updateToolbar();
24442         this.deferFocus();
24443     },
24444
24445     /**
24446      * Executes a Midas editor command directly on the editor document.
24447      * For visual commands, you should use {@link #relayCmd} instead.
24448      * <b>This should only be called after the editor is initialized.</b>
24449      * @param {String} cmd The Midas command
24450      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24451      */
24452     execCmd : function(cmd, value){
24453         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24454         this.syncValue();
24455     },
24456
24457    
24458     /**
24459      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24460      * to insert tRoo.
24461      * @param {String} text
24462      */
24463     insertAtCursor : function(text){
24464         if(!this.activated){
24465             return;
24466         }
24467         if(Roo.isIE){
24468             this.win.focus();
24469             var r = this.doc.selection.createRange();
24470             if(r){
24471                 r.collapse(true);
24472                 r.pasteHTML(text);
24473                 this.syncValue();
24474                 this.deferFocus();
24475             }
24476         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24477             this.win.focus();
24478             this.execCmd('InsertHTML', text);
24479             this.deferFocus();
24480         }
24481     },
24482  // private
24483     mozKeyPress : function(e){
24484         if(e.ctrlKey){
24485             var c = e.getCharCode(), cmd;
24486           
24487             if(c > 0){
24488                 c = String.fromCharCode(c).toLowerCase();
24489                 switch(c){
24490                     case 'b':
24491                         cmd = 'bold';
24492                     break;
24493                     case 'i':
24494                         cmd = 'italic';
24495                     break;
24496                     case 'u':
24497                         cmd = 'underline';
24498                     case 'v':
24499                         this.cleanUpPaste.defer(100, this);
24500                         return;
24501                     break;
24502                 }
24503                 if(cmd){
24504                     this.win.focus();
24505                     this.execCmd(cmd);
24506                     this.deferFocus();
24507                     e.preventDefault();
24508                 }
24509                 
24510             }
24511         }
24512     },
24513
24514     // private
24515     fixKeys : function(){ // load time branching for fastest keydown performance
24516         if(Roo.isIE){
24517             return function(e){
24518                 var k = e.getKey(), r;
24519                 if(k == e.TAB){
24520                     e.stopEvent();
24521                     r = this.doc.selection.createRange();
24522                     if(r){
24523                         r.collapse(true);
24524                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24525                         this.deferFocus();
24526                     }
24527                     return;
24528                 }
24529                 
24530                 if(k == e.ENTER){
24531                     r = this.doc.selection.createRange();
24532                     if(r){
24533                         var target = r.parentElement();
24534                         if(!target || target.tagName.toLowerCase() != 'li'){
24535                             e.stopEvent();
24536                             r.pasteHTML('<br />');
24537                             r.collapse(false);
24538                             r.select();
24539                         }
24540                     }
24541                 }
24542                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24543                     this.cleanUpPaste.defer(100, this);
24544                     return;
24545                 }
24546                 
24547                 
24548             };
24549         }else if(Roo.isOpera){
24550             return function(e){
24551                 var k = e.getKey();
24552                 if(k == e.TAB){
24553                     e.stopEvent();
24554                     this.win.focus();
24555                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24556                     this.deferFocus();
24557                 }
24558                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24559                     this.cleanUpPaste.defer(100, this);
24560                     return;
24561                 }
24562                 
24563             };
24564         }else if(Roo.isSafari){
24565             return function(e){
24566                 var k = e.getKey();
24567                 
24568                 if(k == e.TAB){
24569                     e.stopEvent();
24570                     this.execCmd('InsertText','\t');
24571                     this.deferFocus();
24572                     return;
24573                 }
24574                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24575                     this.cleanUpPaste.defer(100, this);
24576                     return;
24577                 }
24578                 
24579              };
24580         }
24581     }(),
24582     
24583     getAllAncestors: function()
24584     {
24585         var p = this.getSelectedNode();
24586         var a = [];
24587         if (!p) {
24588             a.push(p); // push blank onto stack..
24589             p = this.getParentElement();
24590         }
24591         
24592         
24593         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24594             a.push(p);
24595             p = p.parentNode;
24596         }
24597         a.push(this.doc.body);
24598         return a;
24599     },
24600     lastSel : false,
24601     lastSelNode : false,
24602     
24603     
24604     getSelection : function() 
24605     {
24606         this.assignDocWin();
24607         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24608     },
24609     
24610     getSelectedNode: function() 
24611     {
24612         // this may only work on Gecko!!!
24613         
24614         // should we cache this!!!!
24615         
24616         
24617         
24618          
24619         var range = this.createRange(this.getSelection());
24620         
24621         if (Roo.isIE) {
24622             var parent = range.parentElement();
24623             while (true) {
24624                 var testRange = range.duplicate();
24625                 testRange.moveToElementText(parent);
24626                 if (testRange.inRange(range)) {
24627                     break;
24628                 }
24629                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24630                     break;
24631                 }
24632                 parent = parent.parentElement;
24633             }
24634             return parent;
24635         }
24636         
24637         
24638         var ar = range.endContainer.childNodes;
24639         if (!ar.length) {
24640             ar = range.commonAncestorContainer.childNodes;
24641             //alert(ar.length);
24642         }
24643         var nodes = [];
24644         var other_nodes = [];
24645         var has_other_nodes = false;
24646         for (var i=0;i<ar.length;i++) {
24647             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24648                 continue;
24649             }
24650             // fullly contained node.
24651             
24652             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24653                 nodes.push(ar[i]);
24654                 continue;
24655             }
24656             
24657             // probably selected..
24658             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24659                 other_nodes.push(ar[i]);
24660                 continue;
24661             }
24662             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24663                 continue;
24664             }
24665             
24666             
24667             has_other_nodes = true;
24668         }
24669         if (!nodes.length && other_nodes.length) {
24670             nodes= other_nodes;
24671         }
24672         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24673             return false;
24674         }
24675         
24676         return nodes[0];
24677     },
24678     createRange: function(sel)
24679     {
24680         // this has strange effects when using with 
24681         // top toolbar - not sure if it's a great idea.
24682         //this.editor.contentWindow.focus();
24683         if (typeof sel != "undefined") {
24684             try {
24685                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24686             } catch(e) {
24687                 return this.doc.createRange();
24688             }
24689         } else {
24690             return this.doc.createRange();
24691         }
24692     },
24693     getParentElement: function()
24694     {
24695         
24696         this.assignDocWin();
24697         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24698         
24699         var range = this.createRange(sel);
24700          
24701         try {
24702             var p = range.commonAncestorContainer;
24703             while (p.nodeType == 3) { // text node
24704                 p = p.parentNode;
24705             }
24706             return p;
24707         } catch (e) {
24708             return null;
24709         }
24710     
24711     },
24712     
24713     
24714     
24715     // BC Hacks - cause I cant work out what i was trying to do..
24716     rangeIntersectsNode : function(range, node)
24717     {
24718         var nodeRange = node.ownerDocument.createRange();
24719         try {
24720             nodeRange.selectNode(node);
24721         }
24722         catch (e) {
24723             nodeRange.selectNodeContents(node);
24724         }
24725
24726         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24727                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24728     },
24729     rangeCompareNode : function(range, node) {
24730         var nodeRange = node.ownerDocument.createRange();
24731         try {
24732             nodeRange.selectNode(node);
24733         } catch (e) {
24734             nodeRange.selectNodeContents(node);
24735         }
24736         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24737         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24738
24739         if (nodeIsBefore && !nodeIsAfter)
24740             return 0;
24741         if (!nodeIsBefore && nodeIsAfter)
24742             return 1;
24743         if (nodeIsBefore && nodeIsAfter)
24744             return 2;
24745
24746         return 3;
24747     },
24748
24749     // private? - in a new class?
24750     cleanUpPaste :  function()
24751     {
24752         // cleans up the whole document..
24753       //  console.log('cleanuppaste');
24754         this.cleanUpChildren(this.doc.body);
24755         
24756         
24757     },
24758     cleanUpChildren : function (n)
24759     {
24760         if (!n.childNodes.length) {
24761             return;
24762         }
24763         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24764            this.cleanUpChild(n.childNodes[i]);
24765         }
24766     },
24767     
24768     
24769         
24770     
24771     cleanUpChild : function (node)
24772     {
24773         //console.log(node);
24774         if (node.nodeName == "#text") {
24775             // clean up silly Windows -- stuff?
24776             return; 
24777         }
24778         if (node.nodeName == "#comment") {
24779             node.parentNode.removeChild(node);
24780             // clean up silly Windows -- stuff?
24781             return; 
24782         }
24783         
24784         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24785             // remove node.
24786             node.parentNode.removeChild(node);
24787             return;
24788             
24789         }
24790         if (!node.attributes || !node.attributes.length) {
24791             this.cleanUpChildren(node);
24792             return;
24793         }
24794         
24795         function cleanAttr(n,v)
24796         {
24797             
24798             if (v.match(/^\./) || v.match(/^\//)) {
24799                 return;
24800             }
24801             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24802                 return;
24803             }
24804             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24805             node.removeAttribute(n);
24806             
24807         }
24808         
24809         function cleanStyle(n,v)
24810         {
24811             if (v.match(/expression/)) { //XSS?? should we even bother..
24812                 node.removeAttribute(n);
24813                 return;
24814             }
24815             
24816             
24817             var parts = v.split(/;/);
24818             Roo.each(parts, function(p) {
24819                 p = p.replace(/\s+/g,'');
24820                 if (!p.length) {
24821                     return;
24822                 }
24823                 var l = p.split(':').shift().replace(/\s+/g,'');
24824                 
24825                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24826                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24827                     node.removeAttribute(n);
24828                     return false;
24829                 }
24830             });
24831             
24832             
24833         }
24834         
24835         
24836         for (var i = node.attributes.length-1; i > -1 ; i--) {
24837             var a = node.attributes[i];
24838             //console.log(a);
24839             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24840                 node.removeAttribute(a.name);
24841                 return;
24842             }
24843             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24844                 cleanAttr(a.name,a.value); // fixme..
24845                 return;
24846             }
24847             if (a.name == 'style') {
24848                 cleanStyle(a.name,a.value);
24849             }
24850             /// clean up MS crap..
24851             if (a.name == 'class') {
24852                 if (a.value.match(/^Mso/)) {
24853                     node.className = '';
24854                 }
24855             }
24856             
24857             // style cleanup!?
24858             // class cleanup?
24859             
24860         }
24861         
24862         
24863         this.cleanUpChildren(node);
24864         
24865         
24866     }
24867     
24868     
24869     // hide stuff that is not compatible
24870     /**
24871      * @event blur
24872      * @hide
24873      */
24874     /**
24875      * @event change
24876      * @hide
24877      */
24878     /**
24879      * @event focus
24880      * @hide
24881      */
24882     /**
24883      * @event specialkey
24884      * @hide
24885      */
24886     /**
24887      * @cfg {String} fieldClass @hide
24888      */
24889     /**
24890      * @cfg {String} focusClass @hide
24891      */
24892     /**
24893      * @cfg {String} autoCreate @hide
24894      */
24895     /**
24896      * @cfg {String} inputType @hide
24897      */
24898     /**
24899      * @cfg {String} invalidClass @hide
24900      */
24901     /**
24902      * @cfg {String} invalidText @hide
24903      */
24904     /**
24905      * @cfg {String} msgFx @hide
24906      */
24907     /**
24908      * @cfg {String} validateOnBlur @hide
24909      */
24910 });
24911
24912 Roo.form.HtmlEditor.white = [
24913         'area', 'br', 'img', 'input', 'hr', 'wbr',
24914         
24915        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24916        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24917        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24918        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24919        'table',   'ul',         'xmp', 
24920        
24921        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24922       'thead',   'tr', 
24923      
24924       'dir', 'menu', 'ol', 'ul', 'dl',
24925        
24926       'embed',  'object'
24927 ];
24928
24929
24930 Roo.form.HtmlEditor.black = [
24931     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24932         'applet', // 
24933         'base',   'basefont', 'bgsound', 'blink',  'body', 
24934         'frame',  'frameset', 'head',    'html',   'ilayer', 
24935         'iframe', 'layer',  'link',     'meta',    'object',   
24936         'script', 'style' ,'title',  'xml' // clean later..
24937 ];
24938 Roo.form.HtmlEditor.clean = [
24939     'script', 'style', 'title', 'xml'
24940 ];
24941
24942 // attributes..
24943
24944 Roo.form.HtmlEditor.ablack = [
24945     'on'
24946 ];
24947     
24948 Roo.form.HtmlEditor.aclean = [ 
24949     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24950 ];
24951
24952 // protocols..
24953 Roo.form.HtmlEditor.pwhite= [
24954         'http',  'https',  'mailto'
24955 ];
24956
24957 Roo.form.HtmlEditor.cwhite= [
24958         'text-align',
24959         'font-size'
24960 ];
24961
24962 // <script type="text/javascript">
24963 /*
24964  * Based on
24965  * Ext JS Library 1.1.1
24966  * Copyright(c) 2006-2007, Ext JS, LLC.
24967  *  
24968  
24969  */
24970
24971 /**
24972  * @class Roo.form.HtmlEditorToolbar1
24973  * Basic Toolbar
24974  * 
24975  * Usage:
24976  *
24977  new Roo.form.HtmlEditor({
24978     ....
24979     toolbars : [
24980         new Roo.form.HtmlEditorToolbar1({
24981             disable : { fonts: 1 , format: 1, ..., ... , ...],
24982             btns : [ .... ]
24983         })
24984     }
24985      
24986  * 
24987  * @cfg {Object} disable List of elements to disable..
24988  * @cfg {Array} btns List of additional buttons.
24989  * 
24990  * 
24991  * NEEDS Extra CSS? 
24992  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24993  */
24994  
24995 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24996 {
24997     
24998     Roo.apply(this, config);
24999     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25000     // dont call parent... till later.
25001 }
25002
25003 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25004     
25005     tb: false,
25006     
25007     rendered: false,
25008     
25009     editor : false,
25010     /**
25011      * @cfg {Object} disable  List of toolbar elements to disable
25012          
25013      */
25014     disable : false,
25015       /**
25016      * @cfg {Array} fontFamilies An array of available font families
25017      */
25018     fontFamilies : [
25019         'Arial',
25020         'Courier New',
25021         'Tahoma',
25022         'Times New Roman',
25023         'Verdana'
25024     ],
25025     
25026     specialChars : [
25027            "&#169;",
25028           "&#174;",     
25029           "&#8482;",    
25030           "&#163;" ,    
25031          // "&#8212;",    
25032           "&#8230;",    
25033           "&#247;" ,    
25034         //  "&#225;" ,     ?? a acute?
25035            "&#8364;"    , //Euro
25036        //   "&#8220;"    ,
25037         //  "&#8221;"    ,
25038         //  "&#8226;"    ,
25039           "&#176;"  //   , // degrees
25040
25041          // "&#233;"     , // e ecute
25042          // "&#250;"     , // u ecute?
25043     ],
25044     inputElements : [ 
25045             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25046             "input:submit", "input:button", "select", "textarea", "label" ],
25047     formats : [
25048         ["p"] ,  
25049         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25050         ["pre"],[ "code"], 
25051         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25052     ],
25053      /**
25054      * @cfg {String} defaultFont default font to use.
25055      */
25056     defaultFont: 'tahoma',
25057    
25058     fontSelect : false,
25059     
25060     
25061     formatCombo : false,
25062     
25063     init : function(editor)
25064     {
25065         this.editor = editor;
25066         
25067         
25068         var fid = editor.frameId;
25069         var etb = this;
25070         function btn(id, toggle, handler){
25071             var xid = fid + '-'+ id ;
25072             return {
25073                 id : xid,
25074                 cmd : id,
25075                 cls : 'x-btn-icon x-edit-'+id,
25076                 enableToggle:toggle !== false,
25077                 scope: editor, // was editor...
25078                 handler:handler||editor.relayBtnCmd,
25079                 clickEvent:'mousedown',
25080                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25081                 tabIndex:-1
25082             };
25083         }
25084         
25085         
25086         
25087         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25088         this.tb = tb;
25089          // stop form submits
25090         tb.el.on('click', function(e){
25091             e.preventDefault(); // what does this do?
25092         });
25093
25094         if(!this.disable.font && !Roo.isSafari){
25095             /* why no safari for fonts
25096             editor.fontSelect = tb.el.createChild({
25097                 tag:'select',
25098                 tabIndex: -1,
25099                 cls:'x-font-select',
25100                 html: editor.createFontOptions()
25101             });
25102             editor.fontSelect.on('change', function(){
25103                 var font = editor.fontSelect.dom.value;
25104                 editor.relayCmd('fontname', font);
25105                 editor.deferFocus();
25106             }, editor);
25107             tb.add(
25108                 editor.fontSelect.dom,
25109                 '-'
25110             );
25111             */
25112         };
25113         if(!this.disable.formats){
25114             this.formatCombo = new Roo.form.ComboBox({
25115                 store: new Roo.data.SimpleStore({
25116                     id : 'tag',
25117                     fields: ['tag'],
25118                     data : this.formats // from states.js
25119                 }),
25120                 blockFocus : true,
25121                 //autoCreate : {tag: "div",  size: "20"},
25122                 displayField:'tag',
25123                 typeAhead: false,
25124                 mode: 'local',
25125                 editable : false,
25126                 triggerAction: 'all',
25127                 emptyText:'Add tag',
25128                 selectOnFocus:true,
25129                 width:135,
25130                 listeners : {
25131                     'select': function(c, r, i) {
25132                         editor.insertTag(r.get('tag'));
25133                         editor.focus();
25134                     }
25135                 }
25136
25137             });
25138             tb.addField(this.formatCombo);
25139             
25140         }
25141         
25142         if(!this.disable.format){
25143             tb.add(
25144                 btn('bold'),
25145                 btn('italic'),
25146                 btn('underline')
25147             );
25148         };
25149         if(!this.disable.fontSize){
25150             tb.add(
25151                 '-',
25152                 
25153                 
25154                 btn('increasefontsize', false, editor.adjustFont),
25155                 btn('decreasefontsize', false, editor.adjustFont)
25156             );
25157         };
25158         
25159         
25160         if(this.disable.colors){
25161             tb.add(
25162                 '-', {
25163                     id:editor.frameId +'-forecolor',
25164                     cls:'x-btn-icon x-edit-forecolor',
25165                     clickEvent:'mousedown',
25166                     tooltip: this.buttonTips['forecolor'] || undefined,
25167                     tabIndex:-1,
25168                     menu : new Roo.menu.ColorMenu({
25169                         allowReselect: true,
25170                         focus: Roo.emptyFn,
25171                         value:'000000',
25172                         plain:true,
25173                         selectHandler: function(cp, color){
25174                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25175                             editor.deferFocus();
25176                         },
25177                         scope: editor,
25178                         clickEvent:'mousedown'
25179                     })
25180                 }, {
25181                     id:editor.frameId +'backcolor',
25182                     cls:'x-btn-icon x-edit-backcolor',
25183                     clickEvent:'mousedown',
25184                     tooltip: this.buttonTips['backcolor'] || undefined,
25185                     tabIndex:-1,
25186                     menu : new Roo.menu.ColorMenu({
25187                         focus: Roo.emptyFn,
25188                         value:'FFFFFF',
25189                         plain:true,
25190                         allowReselect: true,
25191                         selectHandler: function(cp, color){
25192                             if(Roo.isGecko){
25193                                 editor.execCmd('useCSS', false);
25194                                 editor.execCmd('hilitecolor', color);
25195                                 editor.execCmd('useCSS', true);
25196                                 editor.deferFocus();
25197                             }else{
25198                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25199                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25200                                 editor.deferFocus();
25201                             }
25202                         },
25203                         scope:editor,
25204                         clickEvent:'mousedown'
25205                     })
25206                 }
25207             );
25208         };
25209         // now add all the items...
25210         
25211
25212         if(!this.disable.alignments){
25213             tb.add(
25214                 '-',
25215                 btn('justifyleft'),
25216                 btn('justifycenter'),
25217                 btn('justifyright')
25218             );
25219         };
25220
25221         //if(!Roo.isSafari){
25222             if(!this.disable.links){
25223                 tb.add(
25224                     '-',
25225                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25226                 );
25227             };
25228
25229             if(!this.disable.lists){
25230                 tb.add(
25231                     '-',
25232                     btn('insertorderedlist'),
25233                     btn('insertunorderedlist')
25234                 );
25235             }
25236             if(!this.disable.sourceEdit){
25237                 tb.add(
25238                     '-',
25239                     btn('sourceedit', true, function(btn){
25240                         this.toggleSourceEdit(btn.pressed);
25241                     })
25242                 );
25243             }
25244         //}
25245         
25246         var smenu = { };
25247         // special menu.. - needs to be tidied up..
25248         if (!this.disable.special) {
25249             smenu = {
25250                 text: "&#169;",
25251                 cls: 'x-edit-none',
25252                 menu : {
25253                     items : []
25254                    }
25255             };
25256             for (var i =0; i < this.specialChars.length; i++) {
25257                 smenu.menu.items.push({
25258                     
25259                     html: this.specialChars[i],
25260                     handler: function(a,b) {
25261                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25262                         
25263                     },
25264                     tabIndex:-1
25265                 });
25266             }
25267             
25268             
25269             tb.add(smenu);
25270             
25271             
25272         }
25273         if (this.btns) {
25274             for(var i =0; i< this.btns.length;i++) {
25275                 var b = this.btns[i];
25276                 b.cls =  'x-edit-none';
25277                 b.scope = editor;
25278                 tb.add(b);
25279             }
25280         
25281         }
25282         
25283         
25284         
25285         // disable everything...
25286         
25287         this.tb.items.each(function(item){
25288            if(item.id != editor.frameId+ '-sourceedit'){
25289                 item.disable();
25290             }
25291         });
25292         this.rendered = true;
25293         
25294         // the all the btns;
25295         editor.on('editorevent', this.updateToolbar, this);
25296         // other toolbars need to implement this..
25297         //editor.on('editmodechange', this.updateToolbar, this);
25298     },
25299     
25300     
25301     
25302     /**
25303      * Protected method that will not generally be called directly. It triggers
25304      * a toolbar update by reading the markup state of the current selection in the editor.
25305      */
25306     updateToolbar: function(){
25307
25308         if(!this.editor.activated){
25309             this.editor.onFirstFocus();
25310             return;
25311         }
25312
25313         var btns = this.tb.items.map, 
25314             doc = this.editor.doc,
25315             frameId = this.editor.frameId;
25316
25317         if(!this.disable.font && !Roo.isSafari){
25318             /*
25319             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25320             if(name != this.fontSelect.dom.value){
25321                 this.fontSelect.dom.value = name;
25322             }
25323             */
25324         }
25325         if(!this.disable.format){
25326             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25327             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25328             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25329         }
25330         if(!this.disable.alignments){
25331             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25332             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25333             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25334         }
25335         if(!Roo.isSafari && !this.disable.lists){
25336             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25337             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25338         }
25339         
25340         var ans = this.editor.getAllAncestors();
25341         if (this.formatCombo) {
25342             
25343             
25344             var store = this.formatCombo.store;
25345             this.formatCombo.setValue("");
25346             for (var i =0; i < ans.length;i++) {
25347                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25348                     // select it..
25349                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25350                     break;
25351                 }
25352             }
25353         }
25354         
25355         
25356         
25357         // hides menus... - so this cant be on a menu...
25358         Roo.menu.MenuMgr.hideAll();
25359
25360         //this.editorsyncValue();
25361     },
25362    
25363     
25364     createFontOptions : function(){
25365         var buf = [], fs = this.fontFamilies, ff, lc;
25366         for(var i = 0, len = fs.length; i< len; i++){
25367             ff = fs[i];
25368             lc = ff.toLowerCase();
25369             buf.push(
25370                 '<option value="',lc,'" style="font-family:',ff,';"',
25371                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25372                     ff,
25373                 '</option>'
25374             );
25375         }
25376         return buf.join('');
25377     },
25378     
25379     toggleSourceEdit : function(sourceEditMode){
25380         if(sourceEditMode === undefined){
25381             sourceEditMode = !this.sourceEditMode;
25382         }
25383         this.sourceEditMode = sourceEditMode === true;
25384         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25385         // just toggle the button?
25386         if(btn.pressed !== this.editor.sourceEditMode){
25387             btn.toggle(this.editor.sourceEditMode);
25388             return;
25389         }
25390         
25391         if(this.sourceEditMode){
25392             this.tb.items.each(function(item){
25393                 if(item.cmd != 'sourceedit'){
25394                     item.disable();
25395                 }
25396             });
25397           
25398         }else{
25399             if(this.initialized){
25400                 this.tb.items.each(function(item){
25401                     item.enable();
25402                 });
25403             }
25404             
25405         }
25406         // tell the editor that it's been pressed..
25407         this.editor.toggleSourceEdit(sourceEditMode);
25408        
25409     },
25410      /**
25411      * Object collection of toolbar tooltips for the buttons in the editor. The key
25412      * is the command id associated with that button and the value is a valid QuickTips object.
25413      * For example:
25414 <pre><code>
25415 {
25416     bold : {
25417         title: 'Bold (Ctrl+B)',
25418         text: 'Make the selected text bold.',
25419         cls: 'x-html-editor-tip'
25420     },
25421     italic : {
25422         title: 'Italic (Ctrl+I)',
25423         text: 'Make the selected text italic.',
25424         cls: 'x-html-editor-tip'
25425     },
25426     ...
25427 </code></pre>
25428     * @type Object
25429      */
25430     buttonTips : {
25431         bold : {
25432             title: 'Bold (Ctrl+B)',
25433             text: 'Make the selected text bold.',
25434             cls: 'x-html-editor-tip'
25435         },
25436         italic : {
25437             title: 'Italic (Ctrl+I)',
25438             text: 'Make the selected text italic.',
25439             cls: 'x-html-editor-tip'
25440         },
25441         underline : {
25442             title: 'Underline (Ctrl+U)',
25443             text: 'Underline the selected text.',
25444             cls: 'x-html-editor-tip'
25445         },
25446         increasefontsize : {
25447             title: 'Grow Text',
25448             text: 'Increase the font size.',
25449             cls: 'x-html-editor-tip'
25450         },
25451         decreasefontsize : {
25452             title: 'Shrink Text',
25453             text: 'Decrease the font size.',
25454             cls: 'x-html-editor-tip'
25455         },
25456         backcolor : {
25457             title: 'Text Highlight Color',
25458             text: 'Change the background color of the selected text.',
25459             cls: 'x-html-editor-tip'
25460         },
25461         forecolor : {
25462             title: 'Font Color',
25463             text: 'Change the color of the selected text.',
25464             cls: 'x-html-editor-tip'
25465         },
25466         justifyleft : {
25467             title: 'Align Text Left',
25468             text: 'Align text to the left.',
25469             cls: 'x-html-editor-tip'
25470         },
25471         justifycenter : {
25472             title: 'Center Text',
25473             text: 'Center text in the editor.',
25474             cls: 'x-html-editor-tip'
25475         },
25476         justifyright : {
25477             title: 'Align Text Right',
25478             text: 'Align text to the right.',
25479             cls: 'x-html-editor-tip'
25480         },
25481         insertunorderedlist : {
25482             title: 'Bullet List',
25483             text: 'Start a bulleted list.',
25484             cls: 'x-html-editor-tip'
25485         },
25486         insertorderedlist : {
25487             title: 'Numbered List',
25488             text: 'Start a numbered list.',
25489             cls: 'x-html-editor-tip'
25490         },
25491         createlink : {
25492             title: 'Hyperlink',
25493             text: 'Make the selected text a hyperlink.',
25494             cls: 'x-html-editor-tip'
25495         },
25496         sourceedit : {
25497             title: 'Source Edit',
25498             text: 'Switch to source editing mode.',
25499             cls: 'x-html-editor-tip'
25500         }
25501     },
25502     // private
25503     onDestroy : function(){
25504         if(this.rendered){
25505             
25506             this.tb.items.each(function(item){
25507                 if(item.menu){
25508                     item.menu.removeAll();
25509                     if(item.menu.el){
25510                         item.menu.el.destroy();
25511                     }
25512                 }
25513                 item.destroy();
25514             });
25515              
25516         }
25517     },
25518     onFirstFocus: function() {
25519         this.tb.items.each(function(item){
25520            item.enable();
25521         });
25522     }
25523 });
25524
25525
25526
25527
25528 // <script type="text/javascript">
25529 /*
25530  * Based on
25531  * Ext JS Library 1.1.1
25532  * Copyright(c) 2006-2007, Ext JS, LLC.
25533  *  
25534  
25535  */
25536
25537  
25538 /**
25539  * @class Roo.form.HtmlEditor.ToolbarContext
25540  * Context Toolbar
25541  * 
25542  * Usage:
25543  *
25544  new Roo.form.HtmlEditor({
25545     ....
25546     toolbars : [
25547         new Roo.form.HtmlEditor.ToolbarStandard(),
25548         new Roo.form.HtmlEditor.ToolbarContext()
25549         })
25550     }
25551      
25552  * 
25553  * @config : {Object} disable List of elements to disable.. (not done yet.)
25554  * 
25555  * 
25556  */
25557
25558 Roo.form.HtmlEditor.ToolbarContext = function(config)
25559 {
25560     
25561     Roo.apply(this, config);
25562     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25563     // dont call parent... till later.
25564 }
25565 Roo.form.HtmlEditor.ToolbarContext.types = {
25566     'IMG' : {
25567         width : {
25568             title: "Width",
25569             width: 40
25570         },
25571         height:  {
25572             title: "Height",
25573             width: 40
25574         },
25575         align: {
25576             title: "Align",
25577             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25578             width : 80
25579             
25580         },
25581         border: {
25582             title: "Border",
25583             width: 40
25584         },
25585         alt: {
25586             title: "Alt",
25587             width: 120
25588         },
25589         src : {
25590             title: "Src",
25591             width: 220
25592         }
25593         
25594     },
25595     'A' : {
25596         name : {
25597             title: "Name",
25598             width: 50
25599         },
25600         href:  {
25601             title: "Href",
25602             width: 220
25603         } // border?
25604         
25605     },
25606     'TABLE' : {
25607         rows : {
25608             title: "Rows",
25609             width: 20
25610         },
25611         cols : {
25612             title: "Cols",
25613             width: 20
25614         },
25615         width : {
25616             title: "Width",
25617             width: 40
25618         },
25619         height : {
25620             title: "Height",
25621             width: 40
25622         },
25623         border : {
25624             title: "Border",
25625             width: 20
25626         }
25627     },
25628     'TD' : {
25629         width : {
25630             title: "Width",
25631             width: 40
25632         },
25633         height : {
25634             title: "Height",
25635             width: 40
25636         },   
25637         align: {
25638             title: "Align",
25639             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25640             width: 40
25641         },
25642         valign: {
25643             title: "Valign",
25644             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25645             width: 40
25646         },
25647         colspan: {
25648             title: "Colspan",
25649             width: 20
25650             
25651         }
25652     },
25653     'INPUT' : {
25654         name : {
25655             title: "name",
25656             width: 120
25657         },
25658         value : {
25659             title: "Value",
25660             width: 120
25661         },
25662         width : {
25663             title: "Width",
25664             width: 40
25665         }
25666     },
25667     'LABEL' : {
25668         'for' : {
25669             title: "For",
25670             width: 120
25671         }
25672     },
25673     'TEXTAREA' : {
25674           name : {
25675             title: "name",
25676             width: 120
25677         },
25678         rows : {
25679             title: "Rows",
25680             width: 20
25681         },
25682         cols : {
25683             title: "Cols",
25684             width: 20
25685         }
25686     },
25687     'SELECT' : {
25688         name : {
25689             title: "name",
25690             width: 120
25691         },
25692         selectoptions : {
25693             title: "Options",
25694             width: 200
25695         }
25696     },
25697     'BODY' : {
25698         title : {
25699             title: "title",
25700             width: 120,
25701             disabled : true
25702         }
25703     }
25704 };
25705
25706
25707
25708 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25709     
25710     tb: false,
25711     
25712     rendered: false,
25713     
25714     editor : false,
25715     /**
25716      * @cfg {Object} disable  List of toolbar elements to disable
25717          
25718      */
25719     disable : false,
25720     
25721     
25722     
25723     toolbars : false,
25724     
25725     init : function(editor)
25726     {
25727         this.editor = editor;
25728         
25729         
25730         var fid = editor.frameId;
25731         var etb = this;
25732         function btn(id, toggle, handler){
25733             var xid = fid + '-'+ id ;
25734             return {
25735                 id : xid,
25736                 cmd : id,
25737                 cls : 'x-btn-icon x-edit-'+id,
25738                 enableToggle:toggle !== false,
25739                 scope: editor, // was editor...
25740                 handler:handler||editor.relayBtnCmd,
25741                 clickEvent:'mousedown',
25742                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25743                 tabIndex:-1
25744             };
25745         }
25746         // create a new element.
25747         var wdiv = editor.wrap.createChild({
25748                 tag: 'div'
25749             }, editor.wrap.dom.firstChild.nextSibling, true);
25750         
25751         // can we do this more than once??
25752         
25753          // stop form submits
25754       
25755  
25756         // disable everything...
25757         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25758         this.toolbars = {};
25759            
25760         for (var i in  ty) {
25761           
25762             this.toolbars[i] = this.buildToolbar(ty[i],i);
25763         }
25764         this.tb = this.toolbars.BODY;
25765         this.tb.el.show();
25766         
25767          
25768         this.rendered = true;
25769         
25770         // the all the btns;
25771         editor.on('editorevent', this.updateToolbar, this);
25772         // other toolbars need to implement this..
25773         //editor.on('editmodechange', this.updateToolbar, this);
25774     },
25775     
25776     
25777     
25778     /**
25779      * Protected method that will not generally be called directly. It triggers
25780      * a toolbar update by reading the markup state of the current selection in the editor.
25781      */
25782     updateToolbar: function(){
25783
25784         if(!this.editor.activated){
25785             this.editor.onFirstFocus();
25786             return;
25787         }
25788
25789         
25790         var ans = this.editor.getAllAncestors();
25791         
25792         // pick
25793         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25794         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25795         sel = sel ? sel : this.editor.doc.body;
25796         sel = sel.tagName.length ? sel : this.editor.doc.body;
25797         var tn = sel.tagName.toUpperCase();
25798         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25799         tn = sel.tagName.toUpperCase();
25800         if (this.tb.name  == tn) {
25801             return; // no change
25802         }
25803         this.tb.el.hide();
25804         ///console.log("show: " + tn);
25805         this.tb =  this.toolbars[tn];
25806         this.tb.el.show();
25807         this.tb.fields.each(function(e) {
25808             e.setValue(sel.getAttribute(e.name));
25809         });
25810         this.tb.selectedNode = sel;
25811         
25812         
25813         Roo.menu.MenuMgr.hideAll();
25814
25815         //this.editorsyncValue();
25816     },
25817    
25818        
25819     // private
25820     onDestroy : function(){
25821         if(this.rendered){
25822             
25823             this.tb.items.each(function(item){
25824                 if(item.menu){
25825                     item.menu.removeAll();
25826                     if(item.menu.el){
25827                         item.menu.el.destroy();
25828                     }
25829                 }
25830                 item.destroy();
25831             });
25832              
25833         }
25834     },
25835     onFirstFocus: function() {
25836         // need to do this for all the toolbars..
25837         this.tb.items.each(function(item){
25838            item.enable();
25839         });
25840     },
25841     buildToolbar: function(tlist, nm)
25842     {
25843         var editor = this.editor;
25844          // create a new element.
25845         var wdiv = editor.wrap.createChild({
25846                 tag: 'div'
25847             }, editor.wrap.dom.firstChild.nextSibling, true);
25848         
25849        
25850         var tb = new Roo.Toolbar(wdiv);
25851         tb.add(nm+ ":&nbsp;");
25852         for (var i in tlist) {
25853             var item = tlist[i];
25854             tb.add(item.title + ":&nbsp;");
25855             if (item.opts) {
25856                 // fixme
25857                 
25858               
25859                 tb.addField( new Roo.form.ComboBox({
25860                     store: new Roo.data.SimpleStore({
25861                         id : 'val',
25862                         fields: ['val'],
25863                         data : item.opts // from states.js
25864                     }),
25865                     name : i,
25866                     displayField:'val',
25867                     typeAhead: false,
25868                     mode: 'local',
25869                     editable : false,
25870                     triggerAction: 'all',
25871                     emptyText:'Select',
25872                     selectOnFocus:true,
25873                     width: item.width ? item.width  : 130,
25874                     listeners : {
25875                         'select': function(c, r, i) {
25876                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25877                         }
25878                     }
25879
25880                 }));
25881                 continue;
25882                     
25883                 
25884                 
25885                 
25886                 
25887                 tb.addField( new Roo.form.TextField({
25888                     name: i,
25889                     width: 100,
25890                     //allowBlank:false,
25891                     value: ''
25892                 }));
25893                 continue;
25894             }
25895             tb.addField( new Roo.form.TextField({
25896                 name: i,
25897                 width: item.width,
25898                 //allowBlank:true,
25899                 value: '',
25900                 listeners: {
25901                     'change' : function(f, nv, ov) {
25902                         tb.selectedNode.setAttribute(f.name, nv);
25903                     }
25904                 }
25905             }));
25906              
25907         }
25908         tb.el.on('click', function(e){
25909             e.preventDefault(); // what does this do?
25910         });
25911         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25912         tb.el.hide();
25913         tb.name = nm;
25914         // dont need to disable them... as they will get hidden
25915         return tb;
25916          
25917         
25918     }
25919     
25920     
25921     
25922     
25923 });
25924
25925
25926
25927
25928
25929 /*
25930  * Based on:
25931  * Ext JS Library 1.1.1
25932  * Copyright(c) 2006-2007, Ext JS, LLC.
25933  *
25934  * Originally Released Under LGPL - original licence link has changed is not relivant.
25935  *
25936  * Fork - LGPL
25937  * <script type="text/javascript">
25938  */
25939  
25940 /**
25941  * @class Roo.form.BasicForm
25942  * @extends Roo.util.Observable
25943  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25944  * @constructor
25945  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25946  * @param {Object} config Configuration options
25947  */
25948 Roo.form.BasicForm = function(el, config){
25949     this.allItems = [];
25950     this.childForms = [];
25951     Roo.apply(this, config);
25952     /*
25953      * The Roo.form.Field items in this form.
25954      * @type MixedCollection
25955      */
25956      
25957      
25958     this.items = new Roo.util.MixedCollection(false, function(o){
25959         return o.id || (o.id = Roo.id());
25960     });
25961     this.addEvents({
25962         /**
25963          * @event beforeaction
25964          * Fires before any action is performed. Return false to cancel the action.
25965          * @param {Form} this
25966          * @param {Action} action The action to be performed
25967          */
25968         beforeaction: true,
25969         /**
25970          * @event actionfailed
25971          * Fires when an action fails.
25972          * @param {Form} this
25973          * @param {Action} action The action that failed
25974          */
25975         actionfailed : true,
25976         /**
25977          * @event actioncomplete
25978          * Fires when an action is completed.
25979          * @param {Form} this
25980          * @param {Action} action The action that completed
25981          */
25982         actioncomplete : true
25983     });
25984     if(el){
25985         this.initEl(el);
25986     }
25987     Roo.form.BasicForm.superclass.constructor.call(this);
25988 };
25989
25990 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25991     /**
25992      * @cfg {String} method
25993      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25994      */
25995     /**
25996      * @cfg {DataReader} reader
25997      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25998      * This is optional as there is built-in support for processing JSON.
25999      */
26000     /**
26001      * @cfg {DataReader} errorReader
26002      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26003      * This is completely optional as there is built-in support for processing JSON.
26004      */
26005     /**
26006      * @cfg {String} url
26007      * The URL to use for form actions if one isn't supplied in the action options.
26008      */
26009     /**
26010      * @cfg {Boolean} fileUpload
26011      * Set to true if this form is a file upload.
26012      */
26013      
26014     /**
26015      * @cfg {Object} baseParams
26016      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26017      */
26018      /**
26019      
26020     /**
26021      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26022      */
26023     timeout: 30,
26024
26025     // private
26026     activeAction : null,
26027
26028     /**
26029      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26030      * or setValues() data instead of when the form was first created.
26031      */
26032     trackResetOnLoad : false,
26033     
26034     
26035     /**
26036      * childForms - used for multi-tab forms
26037      * @type {Array}
26038      */
26039     childForms : false,
26040     
26041     /**
26042      * allItems - full list of fields.
26043      * @type {Array}
26044      */
26045     allItems : false,
26046     
26047     /**
26048      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26049      * element by passing it or its id or mask the form itself by passing in true.
26050      * @type Mixed
26051      */
26052     waitMsgTarget : false,
26053
26054     // private
26055     initEl : function(el){
26056         this.el = Roo.get(el);
26057         this.id = this.el.id || Roo.id();
26058         this.el.on('submit', this.onSubmit, this);
26059         this.el.addClass('x-form');
26060     },
26061
26062     // private
26063     onSubmit : function(e){
26064         e.stopEvent();
26065     },
26066
26067     /**
26068      * Returns true if client-side validation on the form is successful.
26069      * @return Boolean
26070      */
26071     isValid : function(){
26072         var valid = true;
26073         this.items.each(function(f){
26074            if(!f.validate()){
26075                valid = false;
26076            }
26077         });
26078         return valid;
26079     },
26080
26081     /**
26082      * Returns true if any fields in this form have changed since their original load.
26083      * @return Boolean
26084      */
26085     isDirty : function(){
26086         var dirty = false;
26087         this.items.each(function(f){
26088            if(f.isDirty()){
26089                dirty = true;
26090                return false;
26091            }
26092         });
26093         return dirty;
26094     },
26095
26096     /**
26097      * Performs a predefined action (submit or load) or custom actions you define on this form.
26098      * @param {String} actionName The name of the action type
26099      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26100      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26101      * accept other config options):
26102      * <pre>
26103 Property          Type             Description
26104 ----------------  ---------------  ----------------------------------------------------------------------------------
26105 url               String           The url for the action (defaults to the form's url)
26106 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26107 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26108 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26109                                    validate the form on the client (defaults to false)
26110      * </pre>
26111      * @return {BasicForm} this
26112      */
26113     doAction : function(action, options){
26114         if(typeof action == 'string'){
26115             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26116         }
26117         if(this.fireEvent('beforeaction', this, action) !== false){
26118             this.beforeAction(action);
26119             action.run.defer(100, action);
26120         }
26121         return this;
26122     },
26123
26124     /**
26125      * Shortcut to do a submit action.
26126      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26127      * @return {BasicForm} this
26128      */
26129     submit : function(options){
26130         this.doAction('submit', options);
26131         return this;
26132     },
26133
26134     /**
26135      * Shortcut to do a load action.
26136      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26137      * @return {BasicForm} this
26138      */
26139     load : function(options){
26140         this.doAction('load', options);
26141         return this;
26142     },
26143
26144     /**
26145      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26146      * @param {Record} record The record to edit
26147      * @return {BasicForm} this
26148      */
26149     updateRecord : function(record){
26150         record.beginEdit();
26151         var fs = record.fields;
26152         fs.each(function(f){
26153             var field = this.findField(f.name);
26154             if(field){
26155                 record.set(f.name, field.getValue());
26156             }
26157         }, this);
26158         record.endEdit();
26159         return this;
26160     },
26161
26162     /**
26163      * Loads an Roo.data.Record into this form.
26164      * @param {Record} record The record to load
26165      * @return {BasicForm} this
26166      */
26167     loadRecord : function(record){
26168         this.setValues(record.data);
26169         return this;
26170     },
26171
26172     // private
26173     beforeAction : function(action){
26174         var o = action.options;
26175         
26176        
26177         if(this.waitMsgTarget === true){
26178             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26179         }else if(this.waitMsgTarget){
26180             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26181             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26182         }else {
26183             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26184         }
26185          
26186     },
26187
26188     // private
26189     afterAction : function(action, success){
26190         this.activeAction = null;
26191         var o = action.options;
26192         
26193         if(this.waitMsgTarget === true){
26194             this.el.unmask();
26195         }else if(this.waitMsgTarget){
26196             this.waitMsgTarget.unmask();
26197         }else{
26198             Roo.MessageBox.updateProgress(1);
26199             Roo.MessageBox.hide();
26200         }
26201          
26202         if(success){
26203             if(o.reset){
26204                 this.reset();
26205             }
26206             Roo.callback(o.success, o.scope, [this, action]);
26207             this.fireEvent('actioncomplete', this, action);
26208             
26209         }else{
26210             Roo.callback(o.failure, o.scope, [this, action]);
26211             // show an error message if no failed handler is set..
26212             if (!this.hasListener('actionfailed')) {
26213                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26214             }
26215             
26216             this.fireEvent('actionfailed', this, action);
26217         }
26218         
26219     },
26220
26221     /**
26222      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26223      * @param {String} id The value to search for
26224      * @return Field
26225      */
26226     findField : function(id){
26227         var field = this.items.get(id);
26228         if(!field){
26229             this.items.each(function(f){
26230                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26231                     field = f;
26232                     return false;
26233                 }
26234             });
26235         }
26236         return field || null;
26237     },
26238
26239     /**
26240      * Add a secondary form to this one, 
26241      * Used to provide tabbed forms. One form is primary, with hidden values 
26242      * which mirror the elements from the other forms.
26243      * 
26244      * @param {Roo.form.Form} form to add.
26245      * 
26246      */
26247     addForm : function(form)
26248     {
26249        
26250         if (this.childForms.indexOf(form) > -1) {
26251             // already added..
26252             return;
26253         }
26254         this.childForms.push(form);
26255         var n = '';
26256         Roo.each(form.allItems, function (fe) {
26257             
26258             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26259             if (this.findField(n)) { // already added..
26260                 return;
26261             }
26262             var add = new Roo.form.Hidden({
26263                 name : n
26264             });
26265             add.render(this.el);
26266             
26267             this.add( add );
26268         }, this);
26269         
26270     },
26271     /**
26272      * Mark fields in this form invalid in bulk.
26273      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26274      * @return {BasicForm} this
26275      */
26276     markInvalid : function(errors){
26277         if(errors instanceof Array){
26278             for(var i = 0, len = errors.length; i < len; i++){
26279                 var fieldError = errors[i];
26280                 var f = this.findField(fieldError.id);
26281                 if(f){
26282                     f.markInvalid(fieldError.msg);
26283                 }
26284             }
26285         }else{
26286             var field, id;
26287             for(id in errors){
26288                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26289                     field.markInvalid(errors[id]);
26290                 }
26291             }
26292         }
26293         Roo.each(this.childForms || [], function (f) {
26294             f.markInvalid(errors);
26295         });
26296         
26297         return this;
26298     },
26299
26300     /**
26301      * Set values for fields in this form in bulk.
26302      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26303      * @return {BasicForm} this
26304      */
26305     setValues : function(values){
26306         if(values instanceof Array){ // array of objects
26307             for(var i = 0, len = values.length; i < len; i++){
26308                 var v = values[i];
26309                 var f = this.findField(v.id);
26310                 if(f){
26311                     f.setValue(v.value);
26312                     if(this.trackResetOnLoad){
26313                         f.originalValue = f.getValue();
26314                     }
26315                 }
26316             }
26317         }else{ // object hash
26318             var field, id;
26319             for(id in values){
26320                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26321                     
26322                     if (field.setFromData && 
26323                         field.valueField && 
26324                         field.displayField &&
26325                         // combos' with local stores can 
26326                         // be queried via setValue()
26327                         // to set their value..
26328                         (field.store && !field.store.isLocal)
26329                         ) {
26330                         // it's a combo
26331                         var sd = { };
26332                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26333                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26334                         field.setFromData(sd);
26335                         
26336                     } else {
26337                         field.setValue(values[id]);
26338                     }
26339                     
26340                     
26341                     if(this.trackResetOnLoad){
26342                         field.originalValue = field.getValue();
26343                     }
26344                 }
26345             }
26346         }
26347          
26348         Roo.each(this.childForms || [], function (f) {
26349             f.setValues(values);
26350         });
26351                 
26352         return this;
26353     },
26354
26355     /**
26356      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26357      * they are returned as an array.
26358      * @param {Boolean} asString
26359      * @return {Object}
26360      */
26361     getValues : function(asString){
26362         if (this.childForms) {
26363             // copy values from the child forms
26364             Roo.each(this.childForms, function (f) {
26365                 this.setValues(f.getValues());
26366             }, this);
26367         }
26368         
26369         
26370         
26371         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26372         if(asString === true){
26373             return fs;
26374         }
26375         return Roo.urlDecode(fs);
26376     },
26377     
26378     /**
26379      * Returns the fields in this form as an object with key/value pairs. 
26380      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26381      * @return {Object}
26382      */
26383     getFieldValues : function()
26384     {
26385         if (this.childForms) {
26386             // copy values from the child forms
26387             Roo.each(this.childForms, function (f) {
26388                 this.setValues(f.getValues());
26389             }, this);
26390         }
26391         
26392         var ret = {};
26393         this.items.each(function(f){
26394             if (!f.getName()) {
26395                 return;
26396             }
26397             var v = f.getValue();
26398             if ((typeof(v) == 'object') && f.getRawValue) {
26399                 v = f.getRawValue() ; // dates..
26400             }
26401             ret[f.getName()] = v;
26402         });
26403         
26404         return ret;
26405     },
26406
26407     /**
26408      * Clears all invalid messages in this form.
26409      * @return {BasicForm} this
26410      */
26411     clearInvalid : function(){
26412         this.items.each(function(f){
26413            f.clearInvalid();
26414         });
26415         
26416         Roo.each(this.childForms || [], function (f) {
26417             f.clearInvalid();
26418         });
26419         
26420         
26421         return this;
26422     },
26423
26424     /**
26425      * Resets this form.
26426      * @return {BasicForm} this
26427      */
26428     reset : function(){
26429         this.items.each(function(f){
26430             f.reset();
26431         });
26432         
26433         Roo.each(this.childForms || [], function (f) {
26434             f.reset();
26435         });
26436        
26437         
26438         return this;
26439     },
26440
26441     /**
26442      * Add Roo.form components to this form.
26443      * @param {Field} field1
26444      * @param {Field} field2 (optional)
26445      * @param {Field} etc (optional)
26446      * @return {BasicForm} this
26447      */
26448     add : function(){
26449         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26450         return this;
26451     },
26452
26453
26454     /**
26455      * Removes a field from the items collection (does NOT remove its markup).
26456      * @param {Field} field
26457      * @return {BasicForm} this
26458      */
26459     remove : function(field){
26460         this.items.remove(field);
26461         return this;
26462     },
26463
26464     /**
26465      * Looks at the fields in this form, checks them for an id attribute,
26466      * and calls applyTo on the existing dom element with that id.
26467      * @return {BasicForm} this
26468      */
26469     render : function(){
26470         this.items.each(function(f){
26471             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26472                 f.applyTo(f.id);
26473             }
26474         });
26475         return this;
26476     },
26477
26478     /**
26479      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26480      * @param {Object} values
26481      * @return {BasicForm} this
26482      */
26483     applyToFields : function(o){
26484         this.items.each(function(f){
26485            Roo.apply(f, o);
26486         });
26487         return this;
26488     },
26489
26490     /**
26491      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26492      * @param {Object} values
26493      * @return {BasicForm} this
26494      */
26495     applyIfToFields : function(o){
26496         this.items.each(function(f){
26497            Roo.applyIf(f, o);
26498         });
26499         return this;
26500     }
26501 });
26502
26503 // back compat
26504 Roo.BasicForm = Roo.form.BasicForm;/*
26505  * Based on:
26506  * Ext JS Library 1.1.1
26507  * Copyright(c) 2006-2007, Ext JS, LLC.
26508  *
26509  * Originally Released Under LGPL - original licence link has changed is not relivant.
26510  *
26511  * Fork - LGPL
26512  * <script type="text/javascript">
26513  */
26514
26515 /**
26516  * @class Roo.form.Form
26517  * @extends Roo.form.BasicForm
26518  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26519  * @constructor
26520  * @param {Object} config Configuration options
26521  */
26522 Roo.form.Form = function(config){
26523     var xitems =  [];
26524     if (config.items) {
26525         xitems = config.items;
26526         delete config.items;
26527     }
26528    
26529     
26530     Roo.form.Form.superclass.constructor.call(this, null, config);
26531     this.url = this.url || this.action;
26532     if(!this.root){
26533         this.root = new Roo.form.Layout(Roo.applyIf({
26534             id: Roo.id()
26535         }, config));
26536     }
26537     this.active = this.root;
26538     /**
26539      * Array of all the buttons that have been added to this form via {@link addButton}
26540      * @type Array
26541      */
26542     this.buttons = [];
26543     this.allItems = [];
26544     this.addEvents({
26545         /**
26546          * @event clientvalidation
26547          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26548          * @param {Form} this
26549          * @param {Boolean} valid true if the form has passed client-side validation
26550          */
26551         clientvalidation: true,
26552         /**
26553          * @event rendered
26554          * Fires when the form is rendered
26555          * @param {Roo.form.Form} form
26556          */
26557         rendered : true
26558     });
26559     
26560     if (this.progressUrl) {
26561             // push a hidden field onto the list of fields..
26562             this.addxtype( {
26563                     xns: Roo.form, 
26564                     xtype : 'Hidden', 
26565                     name : 'UPLOAD_IDENTIFIER' 
26566             });
26567         }
26568         
26569     
26570     Roo.each(xitems, this.addxtype, this);
26571     
26572     
26573     
26574 };
26575
26576 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26577     /**
26578      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26579      */
26580     /**
26581      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26582      */
26583     /**
26584      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26585      */
26586     buttonAlign:'center',
26587
26588     /**
26589      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26590      */
26591     minButtonWidth:75,
26592
26593     /**
26594      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26595      * This property cascades to child containers if not set.
26596      */
26597     labelAlign:'left',
26598
26599     /**
26600      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26601      * fires a looping event with that state. This is required to bind buttons to the valid
26602      * state using the config value formBind:true on the button.
26603      */
26604     monitorValid : false,
26605
26606     /**
26607      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26608      */
26609     monitorPoll : 200,
26610     
26611     /**
26612      * @cfg {String} progressUrl - Url to return progress data 
26613      */
26614     
26615     progressUrl : false,
26616   
26617     /**
26618      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26619      * fields are added and the column is closed. If no fields are passed the column remains open
26620      * until end() is called.
26621      * @param {Object} config The config to pass to the column
26622      * @param {Field} field1 (optional)
26623      * @param {Field} field2 (optional)
26624      * @param {Field} etc (optional)
26625      * @return Column The column container object
26626      */
26627     column : function(c){
26628         var col = new Roo.form.Column(c);
26629         this.start(col);
26630         if(arguments.length > 1){ // duplicate code required because of Opera
26631             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26632             this.end();
26633         }
26634         return col;
26635     },
26636
26637     /**
26638      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26639      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26640      * until end() is called.
26641      * @param {Object} config The config to pass to the fieldset
26642      * @param {Field} field1 (optional)
26643      * @param {Field} field2 (optional)
26644      * @param {Field} etc (optional)
26645      * @return FieldSet The fieldset container object
26646      */
26647     fieldset : function(c){
26648         var fs = new Roo.form.FieldSet(c);
26649         this.start(fs);
26650         if(arguments.length > 1){ // duplicate code required because of Opera
26651             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26652             this.end();
26653         }
26654         return fs;
26655     },
26656
26657     /**
26658      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26659      * fields are added and the container is closed. If no fields are passed the container remains open
26660      * until end() is called.
26661      * @param {Object} config The config to pass to the Layout
26662      * @param {Field} field1 (optional)
26663      * @param {Field} field2 (optional)
26664      * @param {Field} etc (optional)
26665      * @return Layout The container object
26666      */
26667     container : function(c){
26668         var l = new Roo.form.Layout(c);
26669         this.start(l);
26670         if(arguments.length > 1){ // duplicate code required because of Opera
26671             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26672             this.end();
26673         }
26674         return l;
26675     },
26676
26677     /**
26678      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26679      * @param {Object} container A Roo.form.Layout or subclass of Layout
26680      * @return {Form} this
26681      */
26682     start : function(c){
26683         // cascade label info
26684         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26685         this.active.stack.push(c);
26686         c.ownerCt = this.active;
26687         this.active = c;
26688         return this;
26689     },
26690
26691     /**
26692      * Closes the current open container
26693      * @return {Form} this
26694      */
26695     end : function(){
26696         if(this.active == this.root){
26697             return this;
26698         }
26699         this.active = this.active.ownerCt;
26700         return this;
26701     },
26702
26703     /**
26704      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26705      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26706      * as the label of the field.
26707      * @param {Field} field1
26708      * @param {Field} field2 (optional)
26709      * @param {Field} etc. (optional)
26710      * @return {Form} this
26711      */
26712     add : function(){
26713         this.active.stack.push.apply(this.active.stack, arguments);
26714         this.allItems.push.apply(this.allItems,arguments);
26715         var r = [];
26716         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26717             if(a[i].isFormField){
26718                 r.push(a[i]);
26719             }
26720         }
26721         if(r.length > 0){
26722             Roo.form.Form.superclass.add.apply(this, r);
26723         }
26724         return this;
26725     },
26726     
26727
26728     
26729     
26730     
26731      /**
26732      * Find any element that has been added to a form, using it's ID or name
26733      * This can include framesets, columns etc. along with regular fields..
26734      * @param {String} id - id or name to find.
26735      
26736      * @return {Element} e - or false if nothing found.
26737      */
26738     findbyId : function(id)
26739     {
26740         var ret = false;
26741         if (!id) {
26742             return ret;
26743         }
26744         Roo.each(this.allItems, function(f){
26745             if (f.id == id || f.name == id ){
26746                 ret = f;
26747                 return false;
26748             }
26749         });
26750         return ret;
26751     },
26752
26753     
26754     
26755     /**
26756      * Render this form into the passed container. This should only be called once!
26757      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26758      * @return {Form} this
26759      */
26760     render : function(ct)
26761     {
26762         
26763         
26764         
26765         ct = Roo.get(ct);
26766         var o = this.autoCreate || {
26767             tag: 'form',
26768             method : this.method || 'POST',
26769             id : this.id || Roo.id()
26770         };
26771         this.initEl(ct.createChild(o));
26772
26773         this.root.render(this.el);
26774         
26775        
26776              
26777         this.items.each(function(f){
26778             f.render('x-form-el-'+f.id);
26779         });
26780
26781         if(this.buttons.length > 0){
26782             // tables are required to maintain order and for correct IE layout
26783             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26784                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26785                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26786             }}, null, true);
26787             var tr = tb.getElementsByTagName('tr')[0];
26788             for(var i = 0, len = this.buttons.length; i < len; i++) {
26789                 var b = this.buttons[i];
26790                 var td = document.createElement('td');
26791                 td.className = 'x-form-btn-td';
26792                 b.render(tr.appendChild(td));
26793             }
26794         }
26795         if(this.monitorValid){ // initialize after render
26796             this.startMonitoring();
26797         }
26798         this.fireEvent('rendered', this);
26799         return this;
26800     },
26801
26802     /**
26803      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26804      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26805      * object or a valid Roo.DomHelper element config
26806      * @param {Function} handler The function called when the button is clicked
26807      * @param {Object} scope (optional) The scope of the handler function
26808      * @return {Roo.Button}
26809      */
26810     addButton : function(config, handler, scope){
26811         var bc = {
26812             handler: handler,
26813             scope: scope,
26814             minWidth: this.minButtonWidth,
26815             hideParent:true
26816         };
26817         if(typeof config == "string"){
26818             bc.text = config;
26819         }else{
26820             Roo.apply(bc, config);
26821         }
26822         var btn = new Roo.Button(null, bc);
26823         this.buttons.push(btn);
26824         return btn;
26825     },
26826
26827      /**
26828      * Adds a series of form elements (using the xtype property as the factory method.
26829      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26830      * @param {Object} config 
26831      */
26832     
26833     addxtype : function()
26834     {
26835         var ar = Array.prototype.slice.call(arguments, 0);
26836         var ret = false;
26837         for(var i = 0; i < ar.length; i++) {
26838             if (!ar[i]) {
26839                 continue; // skip -- if this happends something invalid got sent, we 
26840                 // should ignore it, as basically that interface element will not show up
26841                 // and that should be pretty obvious!!
26842             }
26843             
26844             if (Roo.form[ar[i].xtype]) {
26845                 ar[i].form = this;
26846                 var fe = Roo.factory(ar[i], Roo.form);
26847                 if (!ret) {
26848                     ret = fe;
26849                 }
26850                 fe.form = this;
26851                 if (fe.store) {
26852                     fe.store.form = this;
26853                 }
26854                 if (fe.isLayout) {  
26855                          
26856                     this.start(fe);
26857                     this.allItems.push(fe);
26858                     if (fe.items && fe.addxtype) {
26859                         fe.addxtype.apply(fe, fe.items);
26860                         delete fe.items;
26861                     }
26862                      this.end();
26863                     continue;
26864                 }
26865                 
26866                 
26867                  
26868                 this.add(fe);
26869               //  console.log('adding ' + ar[i].xtype);
26870             }
26871             if (ar[i].xtype == 'Button') {  
26872                 //console.log('adding button');
26873                 //console.log(ar[i]);
26874                 this.addButton(ar[i]);
26875                 this.allItems.push(fe);
26876                 continue;
26877             }
26878             
26879             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26880                 alert('end is not supported on xtype any more, use items');
26881             //    this.end();
26882             //    //console.log('adding end');
26883             }
26884             
26885         }
26886         return ret;
26887     },
26888     
26889     /**
26890      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26891      * option "monitorValid"
26892      */
26893     startMonitoring : function(){
26894         if(!this.bound){
26895             this.bound = true;
26896             Roo.TaskMgr.start({
26897                 run : this.bindHandler,
26898                 interval : this.monitorPoll || 200,
26899                 scope: this
26900             });
26901         }
26902     },
26903
26904     /**
26905      * Stops monitoring of the valid state of this form
26906      */
26907     stopMonitoring : function(){
26908         this.bound = false;
26909     },
26910
26911     // private
26912     bindHandler : function(){
26913         if(!this.bound){
26914             return false; // stops binding
26915         }
26916         var valid = true;
26917         this.items.each(function(f){
26918             if(!f.isValid(true)){
26919                 valid = false;
26920                 return false;
26921             }
26922         });
26923         for(var i = 0, len = this.buttons.length; i < len; i++){
26924             var btn = this.buttons[i];
26925             if(btn.formBind === true && btn.disabled === valid){
26926                 btn.setDisabled(!valid);
26927             }
26928         }
26929         this.fireEvent('clientvalidation', this, valid);
26930     }
26931     
26932     
26933     
26934     
26935     
26936     
26937     
26938     
26939 });
26940
26941
26942 // back compat
26943 Roo.Form = Roo.form.Form;
26944 /*
26945  * Based on:
26946  * Ext JS Library 1.1.1
26947  * Copyright(c) 2006-2007, Ext JS, LLC.
26948  *
26949  * Originally Released Under LGPL - original licence link has changed is not relivant.
26950  *
26951  * Fork - LGPL
26952  * <script type="text/javascript">
26953  */
26954  
26955  /**
26956  * @class Roo.form.Action
26957  * Internal Class used to handle form actions
26958  * @constructor
26959  * @param {Roo.form.BasicForm} el The form element or its id
26960  * @param {Object} config Configuration options
26961  */
26962  
26963  
26964 // define the action interface
26965 Roo.form.Action = function(form, options){
26966     this.form = form;
26967     this.options = options || {};
26968 };
26969 /**
26970  * Client Validation Failed
26971  * @const 
26972  */
26973 Roo.form.Action.CLIENT_INVALID = 'client';
26974 /**
26975  * Server Validation Failed
26976  * @const 
26977  */
26978  Roo.form.Action.SERVER_INVALID = 'server';
26979  /**
26980  * Connect to Server Failed
26981  * @const 
26982  */
26983 Roo.form.Action.CONNECT_FAILURE = 'connect';
26984 /**
26985  * Reading Data from Server Failed
26986  * @const 
26987  */
26988 Roo.form.Action.LOAD_FAILURE = 'load';
26989
26990 Roo.form.Action.prototype = {
26991     type : 'default',
26992     failureType : undefined,
26993     response : undefined,
26994     result : undefined,
26995
26996     // interface method
26997     run : function(options){
26998
26999     },
27000
27001     // interface method
27002     success : function(response){
27003
27004     },
27005
27006     // interface method
27007     handleResponse : function(response){
27008
27009     },
27010
27011     // default connection failure
27012     failure : function(response){
27013         
27014         this.response = response;
27015         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27016         this.form.afterAction(this, false);
27017     },
27018
27019     processResponse : function(response){
27020         this.response = response;
27021         if(!response.responseText){
27022             return true;
27023         }
27024         this.result = this.handleResponse(response);
27025         return this.result;
27026     },
27027
27028     // utility functions used internally
27029     getUrl : function(appendParams){
27030         var url = this.options.url || this.form.url || this.form.el.dom.action;
27031         if(appendParams){
27032             var p = this.getParams();
27033             if(p){
27034                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27035             }
27036         }
27037         return url;
27038     },
27039
27040     getMethod : function(){
27041         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27042     },
27043
27044     getParams : function(){
27045         var bp = this.form.baseParams;
27046         var p = this.options.params;
27047         if(p){
27048             if(typeof p == "object"){
27049                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27050             }else if(typeof p == 'string' && bp){
27051                 p += '&' + Roo.urlEncode(bp);
27052             }
27053         }else if(bp){
27054             p = Roo.urlEncode(bp);
27055         }
27056         return p;
27057     },
27058
27059     createCallback : function(){
27060         return {
27061             success: this.success,
27062             failure: this.failure,
27063             scope: this,
27064             timeout: (this.form.timeout*1000),
27065             upload: this.form.fileUpload ? this.success : undefined
27066         };
27067     }
27068 };
27069
27070 Roo.form.Action.Submit = function(form, options){
27071     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27072 };
27073
27074 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27075     type : 'submit',
27076
27077     haveProgress : false,
27078     uploadComplete : false,
27079     
27080     // uploadProgress indicator.
27081     uploadProgress : function()
27082     {
27083         if (!this.form.progressUrl) {
27084             return;
27085         }
27086         
27087         if (!this.haveProgress) {
27088             Roo.MessageBox.progress("Uploading", "Uploading");
27089         }
27090         if (this.uploadComplete) {
27091            Roo.MessageBox.hide();
27092            return;
27093         }
27094         
27095         this.haveProgress = true;
27096    
27097         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27098         
27099         var c = new Roo.data.Connection();
27100         c.request({
27101             url : this.form.progressUrl,
27102             params: {
27103                 id : uid
27104             },
27105             method: 'GET',
27106             success : function(req){
27107                //console.log(data);
27108                 var rdata = false;
27109                 var edata;
27110                 try  {
27111                    rdata = Roo.decode(req.responseText)
27112                 } catch (e) {
27113                     Roo.log("Invalid data from server..");
27114                     Roo.log(edata);
27115                     return;
27116                 }
27117                 if (!rdata || !rdata.success) {
27118                     Roo.log(rdata);
27119                     return;
27120                 }
27121                 var data = rdata.data;
27122                 
27123                 if (this.uploadComplete) {
27124                    Roo.MessageBox.hide();
27125                    return;
27126                 }
27127                    
27128                 if (data){
27129                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27130                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27131                     );
27132                 }
27133                 this.uploadProgress.defer(2000,this);
27134             },
27135        
27136             failure: function(data) {
27137                 Roo.log('progress url failed ');
27138                 Roo.log(data);
27139             },
27140             scope : this
27141         });
27142            
27143     },
27144     
27145     
27146     run : function()
27147     {
27148         // run get Values on the form, so it syncs any secondary forms.
27149         this.form.getValues();
27150         
27151         var o = this.options;
27152         var method = this.getMethod();
27153         var isPost = method == 'POST';
27154         if(o.clientValidation === false || this.form.isValid()){
27155             
27156             if (this.form.progressUrl) {
27157                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27158                     (new Date() * 1) + '' + Math.random());
27159                     
27160             } 
27161             
27162             
27163             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27164                 form:this.form.el.dom,
27165                 url:this.getUrl(!isPost),
27166                 method: method,
27167                 params:isPost ? this.getParams() : null,
27168                 isUpload: this.form.fileUpload
27169             }));
27170             
27171             this.uploadProgress();
27172
27173         }else if (o.clientValidation !== false){ // client validation failed
27174             this.failureType = Roo.form.Action.CLIENT_INVALID;
27175             this.form.afterAction(this, false);
27176         }
27177     },
27178
27179     success : function(response)
27180     {
27181         this.uploadComplete= true;
27182         if (this.haveProgress) {
27183             Roo.MessageBox.hide();
27184         }
27185         
27186         
27187         var result = this.processResponse(response);
27188         if(result === true || result.success){
27189             this.form.afterAction(this, true);
27190             return;
27191         }
27192         if(result.errors){
27193             this.form.markInvalid(result.errors);
27194             this.failureType = Roo.form.Action.SERVER_INVALID;
27195         }
27196         this.form.afterAction(this, false);
27197     },
27198     failure : function(response)
27199     {
27200         this.uploadComplete= true;
27201         if (this.haveProgress) {
27202             Roo.MessageBox.hide();
27203         }
27204         
27205         
27206         this.response = response;
27207         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27208         this.form.afterAction(this, false);
27209     },
27210     
27211     handleResponse : function(response){
27212         if(this.form.errorReader){
27213             var rs = this.form.errorReader.read(response);
27214             var errors = [];
27215             if(rs.records){
27216                 for(var i = 0, len = rs.records.length; i < len; i++) {
27217                     var r = rs.records[i];
27218                     errors[i] = r.data;
27219                 }
27220             }
27221             if(errors.length < 1){
27222                 errors = null;
27223             }
27224             return {
27225                 success : rs.success,
27226                 errors : errors
27227             };
27228         }
27229         var ret = false;
27230         try {
27231             ret = Roo.decode(response.responseText);
27232         } catch (e) {
27233             ret = {
27234                 success: false,
27235                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27236                 errors : []
27237             };
27238         }
27239         return ret;
27240         
27241     }
27242 });
27243
27244
27245 Roo.form.Action.Load = function(form, options){
27246     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27247     this.reader = this.form.reader;
27248 };
27249
27250 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27251     type : 'load',
27252
27253     run : function(){
27254         
27255         Roo.Ajax.request(Roo.apply(
27256                 this.createCallback(), {
27257                     method:this.getMethod(),
27258                     url:this.getUrl(false),
27259                     params:this.getParams()
27260         }));
27261     },
27262
27263     success : function(response){
27264         
27265         var result = this.processResponse(response);
27266         if(result === true || !result.success || !result.data){
27267             this.failureType = Roo.form.Action.LOAD_FAILURE;
27268             this.form.afterAction(this, false);
27269             return;
27270         }
27271         this.form.clearInvalid();
27272         this.form.setValues(result.data);
27273         this.form.afterAction(this, true);
27274     },
27275
27276     handleResponse : function(response){
27277         if(this.form.reader){
27278             var rs = this.form.reader.read(response);
27279             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27280             return {
27281                 success : rs.success,
27282                 data : data
27283             };
27284         }
27285         return Roo.decode(response.responseText);
27286     }
27287 });
27288
27289 Roo.form.Action.ACTION_TYPES = {
27290     'load' : Roo.form.Action.Load,
27291     'submit' : Roo.form.Action.Submit
27292 };/*
27293  * Based on:
27294  * Ext JS Library 1.1.1
27295  * Copyright(c) 2006-2007, Ext JS, LLC.
27296  *
27297  * Originally Released Under LGPL - original licence link has changed is not relivant.
27298  *
27299  * Fork - LGPL
27300  * <script type="text/javascript">
27301  */
27302  
27303 /**
27304  * @class Roo.form.Layout
27305  * @extends Roo.Component
27306  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27307  * @constructor
27308  * @param {Object} config Configuration options
27309  */
27310 Roo.form.Layout = function(config){
27311     var xitems = [];
27312     if (config.items) {
27313         xitems = config.items;
27314         delete config.items;
27315     }
27316     Roo.form.Layout.superclass.constructor.call(this, config);
27317     this.stack = [];
27318     Roo.each(xitems, this.addxtype, this);
27319      
27320 };
27321
27322 Roo.extend(Roo.form.Layout, Roo.Component, {
27323     /**
27324      * @cfg {String/Object} autoCreate
27325      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27326      */
27327     /**
27328      * @cfg {String/Object/Function} style
27329      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27330      * a function which returns such a specification.
27331      */
27332     /**
27333      * @cfg {String} labelAlign
27334      * Valid values are "left," "top" and "right" (defaults to "left")
27335      */
27336     /**
27337      * @cfg {Number} labelWidth
27338      * Fixed width in pixels of all field labels (defaults to undefined)
27339      */
27340     /**
27341      * @cfg {Boolean} clear
27342      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27343      */
27344     clear : true,
27345     /**
27346      * @cfg {String} labelSeparator
27347      * The separator to use after field labels (defaults to ':')
27348      */
27349     labelSeparator : ':',
27350     /**
27351      * @cfg {Boolean} hideLabels
27352      * True to suppress the display of field labels in this layout (defaults to false)
27353      */
27354     hideLabels : false,
27355
27356     // private
27357     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27358     
27359     isLayout : true,
27360     
27361     // private
27362     onRender : function(ct, position){
27363         if(this.el){ // from markup
27364             this.el = Roo.get(this.el);
27365         }else {  // generate
27366             var cfg = this.getAutoCreate();
27367             this.el = ct.createChild(cfg, position);
27368         }
27369         if(this.style){
27370             this.el.applyStyles(this.style);
27371         }
27372         if(this.labelAlign){
27373             this.el.addClass('x-form-label-'+this.labelAlign);
27374         }
27375         if(this.hideLabels){
27376             this.labelStyle = "display:none";
27377             this.elementStyle = "padding-left:0;";
27378         }else{
27379             if(typeof this.labelWidth == 'number'){
27380                 this.labelStyle = "width:"+this.labelWidth+"px;";
27381                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27382             }
27383             if(this.labelAlign == 'top'){
27384                 this.labelStyle = "width:auto;";
27385                 this.elementStyle = "padding-left:0;";
27386             }
27387         }
27388         var stack = this.stack;
27389         var slen = stack.length;
27390         if(slen > 0){
27391             if(!this.fieldTpl){
27392                 var t = new Roo.Template(
27393                     '<div class="x-form-item {5}">',
27394                         '<label for="{0}" style="{2}">{1}{4}</label>',
27395                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27396                         '</div>',
27397                     '</div><div class="x-form-clear-left"></div>'
27398                 );
27399                 t.disableFormats = true;
27400                 t.compile();
27401                 Roo.form.Layout.prototype.fieldTpl = t;
27402             }
27403             for(var i = 0; i < slen; i++) {
27404                 if(stack[i].isFormField){
27405                     this.renderField(stack[i]);
27406                 }else{
27407                     this.renderComponent(stack[i]);
27408                 }
27409             }
27410         }
27411         if(this.clear){
27412             this.el.createChild({cls:'x-form-clear'});
27413         }
27414     },
27415
27416     // private
27417     renderField : function(f){
27418         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27419                f.id, //0
27420                f.fieldLabel, //1
27421                f.labelStyle||this.labelStyle||'', //2
27422                this.elementStyle||'', //3
27423                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27424                f.itemCls||this.itemCls||''  //5
27425        ], true).getPrevSibling());
27426     },
27427
27428     // private
27429     renderComponent : function(c){
27430         c.render(c.isLayout ? this.el : this.el.createChild());    
27431     },
27432     /**
27433      * Adds a object form elements (using the xtype property as the factory method.)
27434      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27435      * @param {Object} config 
27436      */
27437     addxtype : function(o)
27438     {
27439         // create the lement.
27440         o.form = this.form;
27441         var fe = Roo.factory(o, Roo.form);
27442         this.form.allItems.push(fe);
27443         this.stack.push(fe);
27444         
27445         if (fe.isFormField) {
27446             this.form.items.add(fe);
27447         }
27448          
27449         return fe;
27450     }
27451 });
27452
27453 /**
27454  * @class Roo.form.Column
27455  * @extends Roo.form.Layout
27456  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27457  * @constructor
27458  * @param {Object} config Configuration options
27459  */
27460 Roo.form.Column = function(config){
27461     Roo.form.Column.superclass.constructor.call(this, config);
27462 };
27463
27464 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27465     /**
27466      * @cfg {Number/String} width
27467      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27468      */
27469     /**
27470      * @cfg {String/Object} autoCreate
27471      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27472      */
27473
27474     // private
27475     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27476
27477     // private
27478     onRender : function(ct, position){
27479         Roo.form.Column.superclass.onRender.call(this, ct, position);
27480         if(this.width){
27481             this.el.setWidth(this.width);
27482         }
27483     }
27484 });
27485
27486
27487 /**
27488  * @class Roo.form.Row
27489  * @extends Roo.form.Layout
27490  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27491  * @constructor
27492  * @param {Object} config Configuration options
27493  */
27494
27495  
27496 Roo.form.Row = function(config){
27497     Roo.form.Row.superclass.constructor.call(this, config);
27498 };
27499  
27500 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27501       /**
27502      * @cfg {Number/String} width
27503      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27504      */
27505     /**
27506      * @cfg {Number/String} height
27507      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27508      */
27509     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27510     
27511     padWidth : 20,
27512     // private
27513     onRender : function(ct, position){
27514         //console.log('row render');
27515         if(!this.rowTpl){
27516             var t = new Roo.Template(
27517                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27518                     '<label for="{0}" style="{2}">{1}{4}</label>',
27519                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27520                     '</div>',
27521                 '</div>'
27522             );
27523             t.disableFormats = true;
27524             t.compile();
27525             Roo.form.Layout.prototype.rowTpl = t;
27526         }
27527         this.fieldTpl = this.rowTpl;
27528         
27529         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27530         var labelWidth = 100;
27531         
27532         if ((this.labelAlign != 'top')) {
27533             if (typeof this.labelWidth == 'number') {
27534                 labelWidth = this.labelWidth
27535             }
27536             this.padWidth =  20 + labelWidth;
27537             
27538         }
27539         
27540         Roo.form.Column.superclass.onRender.call(this, ct, position);
27541         if(this.width){
27542             this.el.setWidth(this.width);
27543         }
27544         if(this.height){
27545             this.el.setHeight(this.height);
27546         }
27547     },
27548     
27549     // private
27550     renderField : function(f){
27551         f.fieldEl = this.fieldTpl.append(this.el, [
27552                f.id, f.fieldLabel,
27553                f.labelStyle||this.labelStyle||'',
27554                this.elementStyle||'',
27555                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27556                f.itemCls||this.itemCls||'',
27557                f.width ? f.width + this.padWidth : 160 + this.padWidth
27558        ],true);
27559     }
27560 });
27561  
27562
27563 /**
27564  * @class Roo.form.FieldSet
27565  * @extends Roo.form.Layout
27566  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27567  * @constructor
27568  * @param {Object} config Configuration options
27569  */
27570 Roo.form.FieldSet = function(config){
27571     Roo.form.FieldSet.superclass.constructor.call(this, config);
27572 };
27573
27574 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27575     /**
27576      * @cfg {String} legend
27577      * The text to display as the legend for the FieldSet (defaults to '')
27578      */
27579     /**
27580      * @cfg {String/Object} autoCreate
27581      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27582      */
27583
27584     // private
27585     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27586
27587     // private
27588     onRender : function(ct, position){
27589         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27590         if(this.legend){
27591             this.setLegend(this.legend);
27592         }
27593     },
27594
27595     // private
27596     setLegend : function(text){
27597         if(this.rendered){
27598             this.el.child('legend').update(text);
27599         }
27600     }
27601 });/*
27602  * Based on:
27603  * Ext JS Library 1.1.1
27604  * Copyright(c) 2006-2007, Ext JS, LLC.
27605  *
27606  * Originally Released Under LGPL - original licence link has changed is not relivant.
27607  *
27608  * Fork - LGPL
27609  * <script type="text/javascript">
27610  */
27611 /**
27612  * @class Roo.form.VTypes
27613  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27614  * @singleton
27615  */
27616 Roo.form.VTypes = function(){
27617     // closure these in so they are only created once.
27618     var alpha = /^[a-zA-Z_]+$/;
27619     var alphanum = /^[a-zA-Z0-9_]+$/;
27620     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27621     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27622
27623     // All these messages and functions are configurable
27624     return {
27625         /**
27626          * The function used to validate email addresses
27627          * @param {String} value The email address
27628          */
27629         'email' : function(v){
27630             return email.test(v);
27631         },
27632         /**
27633          * The error text to display when the email validation function returns false
27634          * @type String
27635          */
27636         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27637         /**
27638          * The keystroke filter mask to be applied on email input
27639          * @type RegExp
27640          */
27641         'emailMask' : /[a-z0-9_\.\-@]/i,
27642
27643         /**
27644          * The function used to validate URLs
27645          * @param {String} value The URL
27646          */
27647         'url' : function(v){
27648             return url.test(v);
27649         },
27650         /**
27651          * The error text to display when the url validation function returns false
27652          * @type String
27653          */
27654         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27655         
27656         /**
27657          * The function used to validate alpha values
27658          * @param {String} value The value
27659          */
27660         'alpha' : function(v){
27661             return alpha.test(v);
27662         },
27663         /**
27664          * The error text to display when the alpha validation function returns false
27665          * @type String
27666          */
27667         'alphaText' : 'This field should only contain letters and _',
27668         /**
27669          * The keystroke filter mask to be applied on alpha input
27670          * @type RegExp
27671          */
27672         'alphaMask' : /[a-z_]/i,
27673
27674         /**
27675          * The function used to validate alphanumeric values
27676          * @param {String} value The value
27677          */
27678         'alphanum' : function(v){
27679             return alphanum.test(v);
27680         },
27681         /**
27682          * The error text to display when the alphanumeric validation function returns false
27683          * @type String
27684          */
27685         'alphanumText' : 'This field should only contain letters, numbers and _',
27686         /**
27687          * The keystroke filter mask to be applied on alphanumeric input
27688          * @type RegExp
27689          */
27690         'alphanumMask' : /[a-z0-9_]/i
27691     };
27692 }();//<script type="text/javascript">
27693
27694 /**
27695  * @class Roo.form.FCKeditor
27696  * @extends Roo.form.TextArea
27697  * Wrapper around the FCKEditor http://www.fckeditor.net
27698  * @constructor
27699  * Creates a new FCKeditor
27700  * @param {Object} config Configuration options
27701  */
27702 Roo.form.FCKeditor = function(config){
27703     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27704     this.addEvents({
27705          /**
27706          * @event editorinit
27707          * Fired when the editor is initialized - you can add extra handlers here..
27708          * @param {FCKeditor} this
27709          * @param {Object} the FCK object.
27710          */
27711         editorinit : true
27712     });
27713     
27714     
27715 };
27716 Roo.form.FCKeditor.editors = { };
27717 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27718 {
27719     //defaultAutoCreate : {
27720     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27721     //},
27722     // private
27723     /**
27724      * @cfg {Object} fck options - see fck manual for details.
27725      */
27726     fckconfig : false,
27727     
27728     /**
27729      * @cfg {Object} fck toolbar set (Basic or Default)
27730      */
27731     toolbarSet : 'Basic',
27732     /**
27733      * @cfg {Object} fck BasePath
27734      */ 
27735     basePath : '/fckeditor/',
27736     
27737     
27738     frame : false,
27739     
27740     value : '',
27741     
27742    
27743     onRender : function(ct, position)
27744     {
27745         if(!this.el){
27746             this.defaultAutoCreate = {
27747                 tag: "textarea",
27748                 style:"width:300px;height:60px;",
27749                 autocomplete: "off"
27750             };
27751         }
27752         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27753         /*
27754         if(this.grow){
27755             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27756             if(this.preventScrollbars){
27757                 this.el.setStyle("overflow", "hidden");
27758             }
27759             this.el.setHeight(this.growMin);
27760         }
27761         */
27762         //console.log('onrender' + this.getId() );
27763         Roo.form.FCKeditor.editors[this.getId()] = this;
27764          
27765
27766         this.replaceTextarea() ;
27767         
27768     },
27769     
27770     getEditor : function() {
27771         return this.fckEditor;
27772     },
27773     /**
27774      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27775      * @param {Mixed} value The value to set
27776      */
27777     
27778     
27779     setValue : function(value)
27780     {
27781         //console.log('setValue: ' + value);
27782         
27783         if(typeof(value) == 'undefined') { // not sure why this is happending...
27784             return;
27785         }
27786         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27787         
27788         //if(!this.el || !this.getEditor()) {
27789         //    this.value = value;
27790             //this.setValue.defer(100,this,[value]);    
27791         //    return;
27792         //} 
27793         
27794         if(!this.getEditor()) {
27795             return;
27796         }
27797         
27798         this.getEditor().SetData(value);
27799         
27800         //
27801
27802     },
27803
27804     /**
27805      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27806      * @return {Mixed} value The field value
27807      */
27808     getValue : function()
27809     {
27810         
27811         if (this.frame && this.frame.dom.style.display == 'none') {
27812             return Roo.form.FCKeditor.superclass.getValue.call(this);
27813         }
27814         
27815         if(!this.el || !this.getEditor()) {
27816            
27817            // this.getValue.defer(100,this); 
27818             return this.value;
27819         }
27820        
27821         
27822         var value=this.getEditor().GetData();
27823         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27824         return Roo.form.FCKeditor.superclass.getValue.call(this);
27825         
27826
27827     },
27828
27829     /**
27830      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27831      * @return {Mixed} value The field value
27832      */
27833     getRawValue : function()
27834     {
27835         if (this.frame && this.frame.dom.style.display == 'none') {
27836             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27837         }
27838         
27839         if(!this.el || !this.getEditor()) {
27840             //this.getRawValue.defer(100,this); 
27841             return this.value;
27842             return;
27843         }
27844         
27845         
27846         
27847         var value=this.getEditor().GetData();
27848         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27849         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27850          
27851     },
27852     
27853     setSize : function(w,h) {
27854         
27855         
27856         
27857         //if (this.frame && this.frame.dom.style.display == 'none') {
27858         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27859         //    return;
27860         //}
27861         //if(!this.el || !this.getEditor()) {
27862         //    this.setSize.defer(100,this, [w,h]); 
27863         //    return;
27864         //}
27865         
27866         
27867         
27868         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27869         
27870         this.frame.dom.setAttribute('width', w);
27871         this.frame.dom.setAttribute('height', h);
27872         this.frame.setSize(w,h);
27873         
27874     },
27875     
27876     toggleSourceEdit : function(value) {
27877         
27878       
27879          
27880         this.el.dom.style.display = value ? '' : 'none';
27881         this.frame.dom.style.display = value ?  'none' : '';
27882         
27883     },
27884     
27885     
27886     focus: function(tag)
27887     {
27888         if (this.frame.dom.style.display == 'none') {
27889             return Roo.form.FCKeditor.superclass.focus.call(this);
27890         }
27891         if(!this.el || !this.getEditor()) {
27892             this.focus.defer(100,this, [tag]); 
27893             return;
27894         }
27895         
27896         
27897         
27898         
27899         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27900         this.getEditor().Focus();
27901         if (tgs.length) {
27902             if (!this.getEditor().Selection.GetSelection()) {
27903                 this.focus.defer(100,this, [tag]); 
27904                 return;
27905             }
27906             
27907             
27908             var r = this.getEditor().EditorDocument.createRange();
27909             r.setStart(tgs[0],0);
27910             r.setEnd(tgs[0],0);
27911             this.getEditor().Selection.GetSelection().removeAllRanges();
27912             this.getEditor().Selection.GetSelection().addRange(r);
27913             this.getEditor().Focus();
27914         }
27915         
27916     },
27917     
27918     
27919     
27920     replaceTextarea : function()
27921     {
27922         if ( document.getElementById( this.getId() + '___Frame' ) )
27923             return ;
27924         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27925         //{
27926             // We must check the elements firstly using the Id and then the name.
27927         var oTextarea = document.getElementById( this.getId() );
27928         
27929         var colElementsByName = document.getElementsByName( this.getId() ) ;
27930          
27931         oTextarea.style.display = 'none' ;
27932
27933         if ( oTextarea.tabIndex ) {            
27934             this.TabIndex = oTextarea.tabIndex ;
27935         }
27936         
27937         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27938         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27939         this.frame = Roo.get(this.getId() + '___Frame')
27940     },
27941     
27942     _getConfigHtml : function()
27943     {
27944         var sConfig = '' ;
27945
27946         for ( var o in this.fckconfig ) {
27947             sConfig += sConfig.length > 0  ? '&amp;' : '';
27948             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27949         }
27950
27951         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27952     },
27953     
27954     
27955     _getIFrameHtml : function()
27956     {
27957         var sFile = 'fckeditor.html' ;
27958         /* no idea what this is about..
27959         try
27960         {
27961             if ( (/fcksource=true/i).test( window.top.location.search ) )
27962                 sFile = 'fckeditor.original.html' ;
27963         }
27964         catch (e) { 
27965         */
27966
27967         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27968         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27969         
27970         
27971         var html = '<iframe id="' + this.getId() +
27972             '___Frame" src="' + sLink +
27973             '" width="' + this.width +
27974             '" height="' + this.height + '"' +
27975             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27976             ' frameborder="0" scrolling="no"></iframe>' ;
27977
27978         return html ;
27979     },
27980     
27981     _insertHtmlBefore : function( html, element )
27982     {
27983         if ( element.insertAdjacentHTML )       {
27984             // IE
27985             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27986         } else { // Gecko
27987             var oRange = document.createRange() ;
27988             oRange.setStartBefore( element ) ;
27989             var oFragment = oRange.createContextualFragment( html );
27990             element.parentNode.insertBefore( oFragment, element ) ;
27991         }
27992     }
27993     
27994     
27995   
27996     
27997     
27998     
27999     
28000
28001 });
28002
28003 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28004
28005 function FCKeditor_OnComplete(editorInstance){
28006     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28007     f.fckEditor = editorInstance;
28008     //console.log("loaded");
28009     f.fireEvent('editorinit', f, editorInstance);
28010
28011   
28012
28013  
28014
28015
28016
28017
28018
28019
28020
28021
28022
28023
28024
28025
28026
28027
28028
28029 //<script type="text/javascript">
28030 /**
28031  * @class Roo.form.GridField
28032  * @extends Roo.form.Field
28033  * Embed a grid (or editable grid into a form)
28034  * STATUS ALPHA
28035  * 
28036  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28037  * it needs 
28038  * xgrid.store = Roo.data.Store
28039  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28040  * xgrid.store.reader = Roo.data.JsonReader 
28041  * 
28042  * 
28043  * @constructor
28044  * Creates a new GridField
28045  * @param {Object} config Configuration options
28046  */
28047 Roo.form.GridField = function(config){
28048     Roo.form.GridField.superclass.constructor.call(this, config);
28049      
28050 };
28051
28052 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28053     /**
28054      * @cfg {Number} width  - used to restrict width of grid..
28055      */
28056     width : 100,
28057     /**
28058      * @cfg {Number} height - used to restrict height of grid..
28059      */
28060     height : 50,
28061      /**
28062      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28063          * 
28064          *}
28065      */
28066     xgrid : false, 
28067     /**
28068      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28069      * {tag: "input", type: "checkbox", autocomplete: "off"})
28070      */
28071    // defaultAutoCreate : { tag: 'div' },
28072     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28073     /**
28074      * @cfg {String} addTitle Text to include for adding a title.
28075      */
28076     addTitle : false,
28077     //
28078     onResize : function(){
28079         Roo.form.Field.superclass.onResize.apply(this, arguments);
28080     },
28081
28082     initEvents : function(){
28083         // Roo.form.Checkbox.superclass.initEvents.call(this);
28084         // has no events...
28085        
28086     },
28087
28088
28089     getResizeEl : function(){
28090         return this.wrap;
28091     },
28092
28093     getPositionEl : function(){
28094         return this.wrap;
28095     },
28096
28097     // private
28098     onRender : function(ct, position){
28099         
28100         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28101         var style = this.style;
28102         delete this.style;
28103         
28104         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28105         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28106         this.viewEl = this.wrap.createChild({ tag: 'div' });
28107         if (style) {
28108             this.viewEl.applyStyles(style);
28109         }
28110         if (this.width) {
28111             this.viewEl.setWidth(this.width);
28112         }
28113         if (this.height) {
28114             this.viewEl.setHeight(this.height);
28115         }
28116         //if(this.inputValue !== undefined){
28117         //this.setValue(this.value);
28118         
28119         
28120         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28121         
28122         
28123         this.grid.render();
28124         this.grid.getDataSource().on('remove', this.refreshValue, this);
28125         this.grid.getDataSource().on('update', this.refreshValue, this);
28126         this.grid.on('afteredit', this.refreshValue, this);
28127  
28128     },
28129      
28130     
28131     /**
28132      * Sets the value of the item. 
28133      * @param {String} either an object  or a string..
28134      */
28135     setValue : function(v){
28136         //this.value = v;
28137         v = v || []; // empty set..
28138         // this does not seem smart - it really only affects memoryproxy grids..
28139         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28140             var ds = this.grid.getDataSource();
28141             // assumes a json reader..
28142             var data = {}
28143             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28144             ds.loadData( data);
28145         }
28146         Roo.form.GridField.superclass.setValue.call(this, v);
28147         this.refreshValue();
28148         // should load data in the grid really....
28149     },
28150     
28151     // private
28152     refreshValue: function() {
28153          var val = [];
28154         this.grid.getDataSource().each(function(r) {
28155             val.push(r.data);
28156         });
28157         this.el.dom.value = Roo.encode(val);
28158     }
28159     
28160      
28161     
28162     
28163 });/*
28164  * Based on:
28165  * Ext JS Library 1.1.1
28166  * Copyright(c) 2006-2007, Ext JS, LLC.
28167  *
28168  * Originally Released Under LGPL - original licence link has changed is not relivant.
28169  *
28170  * Fork - LGPL
28171  * <script type="text/javascript">
28172  */
28173 /**
28174  * @class Roo.form.DisplayField
28175  * @extends Roo.form.Field
28176  * A generic Field to display non-editable data.
28177  * @constructor
28178  * Creates a new Display Field item.
28179  * @param {Object} config Configuration options
28180  */
28181 Roo.form.DisplayField = function(config){
28182     Roo.form.DisplayField.superclass.constructor.call(this, config);
28183     
28184 };
28185
28186 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28187     inputType:      'hidden',
28188     allowBlank:     true,
28189     readOnly:         true,
28190     
28191  
28192     /**
28193      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28194      */
28195     focusClass : undefined,
28196     /**
28197      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28198      */
28199     fieldClass: 'x-form-field',
28200     
28201      /**
28202      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28203      */
28204     valueRenderer: undefined,
28205     
28206     width: 100,
28207     /**
28208      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28209      * {tag: "input", type: "checkbox", autocomplete: "off"})
28210      */
28211      
28212  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28213
28214     onResize : function(){
28215         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28216         
28217     },
28218
28219     initEvents : function(){
28220         // Roo.form.Checkbox.superclass.initEvents.call(this);
28221         // has no events...
28222        
28223     },
28224
28225
28226     getResizeEl : function(){
28227         return this.wrap;
28228     },
28229
28230     getPositionEl : function(){
28231         return this.wrap;
28232     },
28233
28234     // private
28235     onRender : function(ct, position){
28236         
28237         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28238         //if(this.inputValue !== undefined){
28239         this.wrap = this.el.wrap();
28240         
28241         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28242         
28243         if (this.bodyStyle) {
28244             this.viewEl.applyStyles(this.bodyStyle);
28245         }
28246         //this.viewEl.setStyle('padding', '2px');
28247         
28248         this.setValue(this.value);
28249         
28250     },
28251 /*
28252     // private
28253     initValue : Roo.emptyFn,
28254
28255   */
28256
28257         // private
28258     onClick : function(){
28259         
28260     },
28261
28262     /**
28263      * Sets the checked state of the checkbox.
28264      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28265      */
28266     setValue : function(v){
28267         this.value = v;
28268         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28269         // this might be called before we have a dom element..
28270         if (!this.viewEl) {
28271             return;
28272         }
28273         this.viewEl.dom.innerHTML = html;
28274         Roo.form.DisplayField.superclass.setValue.call(this, v);
28275
28276     }
28277 });/*
28278  * 
28279  * Licence- LGPL
28280  * 
28281  */
28282
28283 /**
28284  * @class Roo.form.DayPicker
28285  * @extends Roo.form.Field
28286  * A Day picker show [M] [T] [W] ....
28287  * @constructor
28288  * Creates a new Day Picker
28289  * @param {Object} config Configuration options
28290  */
28291 Roo.form.DayPicker= function(config){
28292     Roo.form.DayPicker.superclass.constructor.call(this, config);
28293      
28294 };
28295
28296 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28297     /**
28298      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28299      */
28300     focusClass : undefined,
28301     /**
28302      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28303      */
28304     fieldClass: "x-form-field",
28305    
28306     /**
28307      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28308      * {tag: "input", type: "checkbox", autocomplete: "off"})
28309      */
28310     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28311     
28312    
28313     actionMode : 'viewEl', 
28314     //
28315     // private
28316  
28317     inputType : 'hidden',
28318     
28319      
28320     inputElement: false, // real input element?
28321     basedOn: false, // ????
28322     
28323     isFormField: true, // not sure where this is needed!!!!
28324
28325     onResize : function(){
28326         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28327         if(!this.boxLabel){
28328             this.el.alignTo(this.wrap, 'c-c');
28329         }
28330     },
28331
28332     initEvents : function(){
28333         Roo.form.Checkbox.superclass.initEvents.call(this);
28334         this.el.on("click", this.onClick,  this);
28335         this.el.on("change", this.onClick,  this);
28336     },
28337
28338
28339     getResizeEl : function(){
28340         return this.wrap;
28341     },
28342
28343     getPositionEl : function(){
28344         return this.wrap;
28345     },
28346
28347     
28348     // private
28349     onRender : function(ct, position){
28350         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28351        
28352         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28353         
28354         var r1 = '<table><tr>';
28355         var r2 = '<tr class="x-form-daypick-icons">';
28356         for (var i=0; i < 7; i++) {
28357             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28358             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28359         }
28360         
28361         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28362         viewEl.select('img').on('click', this.onClick, this);
28363         this.viewEl = viewEl;   
28364         
28365         
28366         // this will not work on Chrome!!!
28367         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28368         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28369         
28370         
28371           
28372
28373     },
28374
28375     // private
28376     initValue : Roo.emptyFn,
28377
28378     /**
28379      * Returns the checked state of the checkbox.
28380      * @return {Boolean} True if checked, else false
28381      */
28382     getValue : function(){
28383         return this.el.dom.value;
28384         
28385     },
28386
28387         // private
28388     onClick : function(e){ 
28389         //this.setChecked(!this.checked);
28390         Roo.get(e.target).toggleClass('x-menu-item-checked');
28391         this.refreshValue();
28392         //if(this.el.dom.checked != this.checked){
28393         //    this.setValue(this.el.dom.checked);
28394        // }
28395     },
28396     
28397     // private
28398     refreshValue : function()
28399     {
28400         var val = '';
28401         this.viewEl.select('img',true).each(function(e,i,n)  {
28402             val += e.is(".x-menu-item-checked") ? String(n) : '';
28403         });
28404         this.setValue(val, true);
28405     },
28406
28407     /**
28408      * Sets the checked state of the checkbox.
28409      * On is always based on a string comparison between inputValue and the param.
28410      * @param {Boolean/String} value - the value to set 
28411      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28412      */
28413     setValue : function(v,suppressEvent){
28414         if (!this.el.dom) {
28415             return;
28416         }
28417         var old = this.el.dom.value ;
28418         this.el.dom.value = v;
28419         if (suppressEvent) {
28420             return ;
28421         }
28422          
28423         // update display..
28424         this.viewEl.select('img',true).each(function(e,i,n)  {
28425             
28426             var on = e.is(".x-menu-item-checked");
28427             var newv = v.indexOf(String(n)) > -1;
28428             if (on != newv) {
28429                 e.toggleClass('x-menu-item-checked');
28430             }
28431             
28432         });
28433         
28434         
28435         this.fireEvent('change', this, v, old);
28436         
28437         
28438     },
28439    
28440     // handle setting of hidden value by some other method!!?!?
28441     setFromHidden: function()
28442     {
28443         if(!this.el){
28444             return;
28445         }
28446         //console.log("SET FROM HIDDEN");
28447         //alert('setFrom hidden');
28448         this.setValue(this.el.dom.value);
28449     },
28450     
28451     onDestroy : function()
28452     {
28453         if(this.viewEl){
28454             Roo.get(this.viewEl).remove();
28455         }
28456          
28457         Roo.form.DayPicker.superclass.onDestroy.call(this);
28458     }
28459
28460 });//<script type="text/javasscript">
28461  
28462
28463 /**
28464  * @class Roo.DDView
28465  * A DnD enabled version of Roo.View.
28466  * @param {Element/String} container The Element in which to create the View.
28467  * @param {String} tpl The template string used to create the markup for each element of the View
28468  * @param {Object} config The configuration properties. These include all the config options of
28469  * {@link Roo.View} plus some specific to this class.<br>
28470  * <p>
28471  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28472  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28473  * <p>
28474  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28475 .x-view-drag-insert-above {
28476         border-top:1px dotted #3366cc;
28477 }
28478 .x-view-drag-insert-below {
28479         border-bottom:1px dotted #3366cc;
28480 }
28481 </code></pre>
28482  * 
28483  */
28484  
28485 Roo.DDView = function(container, tpl, config) {
28486     Roo.DDView.superclass.constructor.apply(this, arguments);
28487     this.getEl().setStyle("outline", "0px none");
28488     this.getEl().unselectable();
28489     if (this.dragGroup) {
28490                 this.setDraggable(this.dragGroup.split(","));
28491     }
28492     if (this.dropGroup) {
28493                 this.setDroppable(this.dropGroup.split(","));
28494     }
28495     if (this.deletable) {
28496         this.setDeletable();
28497     }
28498     this.isDirtyFlag = false;
28499         this.addEvents({
28500                 "drop" : true
28501         });
28502 };
28503
28504 Roo.extend(Roo.DDView, Roo.View, {
28505 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28506 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28507 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28508 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28509
28510         isFormField: true,
28511
28512         reset: Roo.emptyFn,
28513         
28514         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28515
28516         validate: function() {
28517                 return true;
28518         },
28519         
28520         destroy: function() {
28521                 this.purgeListeners();
28522                 this.getEl.removeAllListeners();
28523                 this.getEl().remove();
28524                 if (this.dragZone) {
28525                         if (this.dragZone.destroy) {
28526                                 this.dragZone.destroy();
28527                         }
28528                 }
28529                 if (this.dropZone) {
28530                         if (this.dropZone.destroy) {
28531                                 this.dropZone.destroy();
28532                         }
28533                 }
28534         },
28535
28536 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28537         getName: function() {
28538                 return this.name;
28539         },
28540
28541 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28542         setValue: function(v) {
28543                 if (!this.store) {
28544                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28545                 }
28546                 var data = {};
28547                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28548                 this.store.proxy = new Roo.data.MemoryProxy(data);
28549                 this.store.load();
28550         },
28551
28552 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28553         getValue: function() {
28554                 var result = '(';
28555                 this.store.each(function(rec) {
28556                         result += rec.id + ',';
28557                 });
28558                 return result.substr(0, result.length - 1) + ')';
28559         },
28560         
28561         getIds: function() {
28562                 var i = 0, result = new Array(this.store.getCount());
28563                 this.store.each(function(rec) {
28564                         result[i++] = rec.id;
28565                 });
28566                 return result;
28567         },
28568         
28569         isDirty: function() {
28570                 return this.isDirtyFlag;
28571         },
28572
28573 /**
28574  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28575  *      whole Element becomes the target, and this causes the drop gesture to append.
28576  */
28577     getTargetFromEvent : function(e) {
28578                 var target = e.getTarget();
28579                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28580                 target = target.parentNode;
28581                 }
28582                 if (!target) {
28583                         target = this.el.dom.lastChild || this.el.dom;
28584                 }
28585                 return target;
28586     },
28587
28588 /**
28589  *      Create the drag data which consists of an object which has the property "ddel" as
28590  *      the drag proxy element. 
28591  */
28592     getDragData : function(e) {
28593         var target = this.findItemFromChild(e.getTarget());
28594                 if(target) {
28595                         this.handleSelection(e);
28596                         var selNodes = this.getSelectedNodes();
28597             var dragData = {
28598                 source: this,
28599                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28600                 nodes: selNodes,
28601                 records: []
28602                         };
28603                         var selectedIndices = this.getSelectedIndexes();
28604                         for (var i = 0; i < selectedIndices.length; i++) {
28605                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28606                         }
28607                         if (selNodes.length == 1) {
28608                                 dragData.ddel = target.cloneNode(true); // the div element
28609                         } else {
28610                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28611                                 div.className = 'multi-proxy';
28612                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28613                                         div.appendChild(selNodes[i].cloneNode(true));
28614                                 }
28615                                 dragData.ddel = div;
28616                         }
28617             //console.log(dragData)
28618             //console.log(dragData.ddel.innerHTML)
28619                         return dragData;
28620                 }
28621         //console.log('nodragData')
28622                 return false;
28623     },
28624     
28625 /**     Specify to which ddGroup items in this DDView may be dragged. */
28626     setDraggable: function(ddGroup) {
28627         if (ddGroup instanceof Array) {
28628                 Roo.each(ddGroup, this.setDraggable, this);
28629                 return;
28630         }
28631         if (this.dragZone) {
28632                 this.dragZone.addToGroup(ddGroup);
28633         } else {
28634                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28635                                 containerScroll: true,
28636                                 ddGroup: ddGroup 
28637
28638                         });
28639 //                      Draggability implies selection. DragZone's mousedown selects the element.
28640                         if (!this.multiSelect) { this.singleSelect = true; }
28641
28642 //                      Wire the DragZone's handlers up to methods in *this*
28643                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28644                 }
28645     },
28646
28647 /**     Specify from which ddGroup this DDView accepts drops. */
28648     setDroppable: function(ddGroup) {
28649         if (ddGroup instanceof Array) {
28650                 Roo.each(ddGroup, this.setDroppable, this);
28651                 return;
28652         }
28653         if (this.dropZone) {
28654                 this.dropZone.addToGroup(ddGroup);
28655         } else {
28656                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28657                                 containerScroll: true,
28658                                 ddGroup: ddGroup
28659                         });
28660
28661 //                      Wire the DropZone's handlers up to methods in *this*
28662                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28663                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28664                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28665                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28666                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28667                 }
28668     },
28669
28670 /**     Decide whether to drop above or below a View node. */
28671     getDropPoint : function(e, n, dd){
28672         if (n == this.el.dom) { return "above"; }
28673                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28674                 var c = t + (b - t) / 2;
28675                 var y = Roo.lib.Event.getPageY(e);
28676                 if(y <= c) {
28677                         return "above";
28678                 }else{
28679                         return "below";
28680                 }
28681     },
28682
28683     onNodeEnter : function(n, dd, e, data){
28684                 return false;
28685     },
28686     
28687     onNodeOver : function(n, dd, e, data){
28688                 var pt = this.getDropPoint(e, n, dd);
28689                 // set the insert point style on the target node
28690                 var dragElClass = this.dropNotAllowed;
28691                 if (pt) {
28692                         var targetElClass;
28693                         if (pt == "above"){
28694                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28695                                 targetElClass = "x-view-drag-insert-above";
28696                         } else {
28697                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28698                                 targetElClass = "x-view-drag-insert-below";
28699                         }
28700                         if (this.lastInsertClass != targetElClass){
28701                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28702                                 this.lastInsertClass = targetElClass;
28703                         }
28704                 }
28705                 return dragElClass;
28706         },
28707
28708     onNodeOut : function(n, dd, e, data){
28709                 this.removeDropIndicators(n);
28710     },
28711
28712     onNodeDrop : function(n, dd, e, data){
28713         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28714                 return false;
28715         }
28716         var pt = this.getDropPoint(e, n, dd);
28717                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28718                 if (pt == "below") { insertAt++; }
28719                 for (var i = 0; i < data.records.length; i++) {
28720                         var r = data.records[i];
28721                         var dup = this.store.getById(r.id);
28722                         if (dup && (dd != this.dragZone)) {
28723                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28724                         } else {
28725                                 if (data.copy) {
28726                                         this.store.insert(insertAt++, r.copy());
28727                                 } else {
28728                                         data.source.isDirtyFlag = true;
28729                                         r.store.remove(r);
28730                                         this.store.insert(insertAt++, r);
28731                                 }
28732                                 this.isDirtyFlag = true;
28733                         }
28734                 }
28735                 this.dragZone.cachedTarget = null;
28736                 return true;
28737     },
28738
28739     removeDropIndicators : function(n){
28740                 if(n){
28741                         Roo.fly(n).removeClass([
28742                                 "x-view-drag-insert-above",
28743                                 "x-view-drag-insert-below"]);
28744                         this.lastInsertClass = "_noclass";
28745                 }
28746     },
28747
28748 /**
28749  *      Utility method. Add a delete option to the DDView's context menu.
28750  *      @param {String} imageUrl The URL of the "delete" icon image.
28751  */
28752         setDeletable: function(imageUrl) {
28753                 if (!this.singleSelect && !this.multiSelect) {
28754                         this.singleSelect = true;
28755                 }
28756                 var c = this.getContextMenu();
28757                 this.contextMenu.on("itemclick", function(item) {
28758                         switch (item.id) {
28759                                 case "delete":
28760                                         this.remove(this.getSelectedIndexes());
28761                                         break;
28762                         }
28763                 }, this);
28764                 this.contextMenu.add({
28765                         icon: imageUrl,
28766                         id: "delete",
28767                         text: 'Delete'
28768                 });
28769         },
28770         
28771 /**     Return the context menu for this DDView. */
28772         getContextMenu: function() {
28773                 if (!this.contextMenu) {
28774 //                      Create the View's context menu
28775                         this.contextMenu = new Roo.menu.Menu({
28776                                 id: this.id + "-contextmenu"
28777                         });
28778                         this.el.on("contextmenu", this.showContextMenu, this);
28779                 }
28780                 return this.contextMenu;
28781         },
28782         
28783         disableContextMenu: function() {
28784                 if (this.contextMenu) {
28785                         this.el.un("contextmenu", this.showContextMenu, this);
28786                 }
28787         },
28788
28789         showContextMenu: function(e, item) {
28790         item = this.findItemFromChild(e.getTarget());
28791                 if (item) {
28792                         e.stopEvent();
28793                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28794                         this.contextMenu.showAt(e.getXY());
28795             }
28796     },
28797
28798 /**
28799  *      Remove {@link Roo.data.Record}s at the specified indices.
28800  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28801  */
28802     remove: function(selectedIndices) {
28803                 selectedIndices = [].concat(selectedIndices);
28804                 for (var i = 0; i < selectedIndices.length; i++) {
28805                         var rec = this.store.getAt(selectedIndices[i]);
28806                         this.store.remove(rec);
28807                 }
28808     },
28809
28810 /**
28811  *      Double click fires the event, but also, if this is draggable, and there is only one other
28812  *      related DropZone, it transfers the selected node.
28813  */
28814     onDblClick : function(e){
28815         var item = this.findItemFromChild(e.getTarget());
28816         if(item){
28817             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28818                 return false;
28819             }
28820             if (this.dragGroup) {
28821                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28822                     while (targets.indexOf(this.dropZone) > -1) {
28823                             targets.remove(this.dropZone);
28824                                 }
28825                     if (targets.length == 1) {
28826                                         this.dragZone.cachedTarget = null;
28827                         var el = Roo.get(targets[0].getEl());
28828                         var box = el.getBox(true);
28829                         targets[0].onNodeDrop(el.dom, {
28830                                 target: el.dom,
28831                                 xy: [box.x, box.y + box.height - 1]
28832                         }, null, this.getDragData(e));
28833                     }
28834                 }
28835         }
28836     },
28837     
28838     handleSelection: function(e) {
28839                 this.dragZone.cachedTarget = null;
28840         var item = this.findItemFromChild(e.getTarget());
28841         if (!item) {
28842                 this.clearSelections(true);
28843                 return;
28844         }
28845                 if (item && (this.multiSelect || this.singleSelect)){
28846                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28847                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28848                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28849                                 this.unselect(item);
28850                         } else {
28851                                 this.select(item, this.multiSelect && e.ctrlKey);
28852                                 this.lastSelection = item;
28853                         }
28854                 }
28855     },
28856
28857     onItemClick : function(item, index, e){
28858                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28859                         return false;
28860                 }
28861                 return true;
28862     },
28863
28864     unselect : function(nodeInfo, suppressEvent){
28865                 var node = this.getNode(nodeInfo);
28866                 if(node && this.isSelected(node)){
28867                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28868                                 Roo.fly(node).removeClass(this.selectedClass);
28869                                 this.selections.remove(node);
28870                                 if(!suppressEvent){
28871                                         this.fireEvent("selectionchange", this, this.selections);
28872                                 }
28873                         }
28874                 }
28875     }
28876 });
28877 /*
28878  * Based on:
28879  * Ext JS Library 1.1.1
28880  * Copyright(c) 2006-2007, Ext JS, LLC.
28881  *
28882  * Originally Released Under LGPL - original licence link has changed is not relivant.
28883  *
28884  * Fork - LGPL
28885  * <script type="text/javascript">
28886  */
28887  
28888 /**
28889  * @class Roo.LayoutManager
28890  * @extends Roo.util.Observable
28891  * Base class for layout managers.
28892  */
28893 Roo.LayoutManager = function(container, config){
28894     Roo.LayoutManager.superclass.constructor.call(this);
28895     this.el = Roo.get(container);
28896     // ie scrollbar fix
28897     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28898         document.body.scroll = "no";
28899     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28900         this.el.position('relative');
28901     }
28902     this.id = this.el.id;
28903     this.el.addClass("x-layout-container");
28904     /** false to disable window resize monitoring @type Boolean */
28905     this.monitorWindowResize = true;
28906     this.regions = {};
28907     this.addEvents({
28908         /**
28909          * @event layout
28910          * Fires when a layout is performed. 
28911          * @param {Roo.LayoutManager} this
28912          */
28913         "layout" : true,
28914         /**
28915          * @event regionresized
28916          * Fires when the user resizes a region. 
28917          * @param {Roo.LayoutRegion} region The resized region
28918          * @param {Number} newSize The new size (width for east/west, height for north/south)
28919          */
28920         "regionresized" : true,
28921         /**
28922          * @event regioncollapsed
28923          * Fires when a region is collapsed. 
28924          * @param {Roo.LayoutRegion} region The collapsed region
28925          */
28926         "regioncollapsed" : true,
28927         /**
28928          * @event regionexpanded
28929          * Fires when a region is expanded.  
28930          * @param {Roo.LayoutRegion} region The expanded region
28931          */
28932         "regionexpanded" : true
28933     });
28934     this.updating = false;
28935     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28936 };
28937
28938 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28939     /**
28940      * Returns true if this layout is currently being updated
28941      * @return {Boolean}
28942      */
28943     isUpdating : function(){
28944         return this.updating; 
28945     },
28946     
28947     /**
28948      * Suspend the LayoutManager from doing auto-layouts while
28949      * making multiple add or remove calls
28950      */
28951     beginUpdate : function(){
28952         this.updating = true;    
28953     },
28954     
28955     /**
28956      * Restore auto-layouts and optionally disable the manager from performing a layout
28957      * @param {Boolean} noLayout true to disable a layout update 
28958      */
28959     endUpdate : function(noLayout){
28960         this.updating = false;
28961         if(!noLayout){
28962             this.layout();
28963         }    
28964     },
28965     
28966     layout: function(){
28967         
28968     },
28969     
28970     onRegionResized : function(region, newSize){
28971         this.fireEvent("regionresized", region, newSize);
28972         this.layout();
28973     },
28974     
28975     onRegionCollapsed : function(region){
28976         this.fireEvent("regioncollapsed", region);
28977     },
28978     
28979     onRegionExpanded : function(region){
28980         this.fireEvent("regionexpanded", region);
28981     },
28982         
28983     /**
28984      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28985      * performs box-model adjustments.
28986      * @return {Object} The size as an object {width: (the width), height: (the height)}
28987      */
28988     getViewSize : function(){
28989         var size;
28990         if(this.el.dom != document.body){
28991             size = this.el.getSize();
28992         }else{
28993             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28994         }
28995         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28996         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28997         return size;
28998     },
28999     
29000     /**
29001      * Returns the Element this layout is bound to.
29002      * @return {Roo.Element}
29003      */
29004     getEl : function(){
29005         return this.el;
29006     },
29007     
29008     /**
29009      * Returns the specified region.
29010      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29011      * @return {Roo.LayoutRegion}
29012      */
29013     getRegion : function(target){
29014         return this.regions[target.toLowerCase()];
29015     },
29016     
29017     onWindowResize : function(){
29018         if(this.monitorWindowResize){
29019             this.layout();
29020         }
29021     }
29022 });/*
29023  * Based on:
29024  * Ext JS Library 1.1.1
29025  * Copyright(c) 2006-2007, Ext JS, LLC.
29026  *
29027  * Originally Released Under LGPL - original licence link has changed is not relivant.
29028  *
29029  * Fork - LGPL
29030  * <script type="text/javascript">
29031  */
29032 /**
29033  * @class Roo.BorderLayout
29034  * @extends Roo.LayoutManager
29035  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29036  * please see: <br><br>
29037  * <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>
29038  * <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>
29039  * Example:
29040  <pre><code>
29041  var layout = new Roo.BorderLayout(document.body, {
29042     north: {
29043         initialSize: 25,
29044         titlebar: false
29045     },
29046     west: {
29047         split:true,
29048         initialSize: 200,
29049         minSize: 175,
29050         maxSize: 400,
29051         titlebar: true,
29052         collapsible: true
29053     },
29054     east: {
29055         split:true,
29056         initialSize: 202,
29057         minSize: 175,
29058         maxSize: 400,
29059         titlebar: true,
29060         collapsible: true
29061     },
29062     south: {
29063         split:true,
29064         initialSize: 100,
29065         minSize: 100,
29066         maxSize: 200,
29067         titlebar: true,
29068         collapsible: true
29069     },
29070     center: {
29071         titlebar: true,
29072         autoScroll:true,
29073         resizeTabs: true,
29074         minTabWidth: 50,
29075         preferredTabWidth: 150
29076     }
29077 });
29078
29079 // shorthand
29080 var CP = Roo.ContentPanel;
29081
29082 layout.beginUpdate();
29083 layout.add("north", new CP("north", "North"));
29084 layout.add("south", new CP("south", {title: "South", closable: true}));
29085 layout.add("west", new CP("west", {title: "West"}));
29086 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29087 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29088 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29089 layout.getRegion("center").showPanel("center1");
29090 layout.endUpdate();
29091 </code></pre>
29092
29093 <b>The container the layout is rendered into can be either the body element or any other element.
29094 If it is not the body element, the container needs to either be an absolute positioned element,
29095 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29096 the container size if it is not the body element.</b>
29097
29098 * @constructor
29099 * Create a new BorderLayout
29100 * @param {String/HTMLElement/Element} container The container this layout is bound to
29101 * @param {Object} config Configuration options
29102  */
29103 Roo.BorderLayout = function(container, config){
29104     config = config || {};
29105     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29106     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29107     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29108         var target = this.factory.validRegions[i];
29109         if(config[target]){
29110             this.addRegion(target, config[target]);
29111         }
29112     }
29113 };
29114
29115 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29116     /**
29117      * Creates and adds a new region if it doesn't already exist.
29118      * @param {String} target The target region key (north, south, east, west or center).
29119      * @param {Object} config The regions config object
29120      * @return {BorderLayoutRegion} The new region
29121      */
29122     addRegion : function(target, config){
29123         if(!this.regions[target]){
29124             var r = this.factory.create(target, this, config);
29125             this.bindRegion(target, r);
29126         }
29127         return this.regions[target];
29128     },
29129
29130     // private (kinda)
29131     bindRegion : function(name, r){
29132         this.regions[name] = r;
29133         r.on("visibilitychange", this.layout, this);
29134         r.on("paneladded", this.layout, this);
29135         r.on("panelremoved", this.layout, this);
29136         r.on("invalidated", this.layout, this);
29137         r.on("resized", this.onRegionResized, this);
29138         r.on("collapsed", this.onRegionCollapsed, this);
29139         r.on("expanded", this.onRegionExpanded, this);
29140     },
29141
29142     /**
29143      * Performs a layout update.
29144      */
29145     layout : function(){
29146         if(this.updating) return;
29147         var size = this.getViewSize();
29148         var w = size.width;
29149         var h = size.height;
29150         var centerW = w;
29151         var centerH = h;
29152         var centerY = 0;
29153         var centerX = 0;
29154         //var x = 0, y = 0;
29155
29156         var rs = this.regions;
29157         var north = rs["north"];
29158         var south = rs["south"]; 
29159         var west = rs["west"];
29160         var east = rs["east"];
29161         var center = rs["center"];
29162         //if(this.hideOnLayout){ // not supported anymore
29163             //c.el.setStyle("display", "none");
29164         //}
29165         if(north && north.isVisible()){
29166             var b = north.getBox();
29167             var m = north.getMargins();
29168             b.width = w - (m.left+m.right);
29169             b.x = m.left;
29170             b.y = m.top;
29171             centerY = b.height + b.y + m.bottom;
29172             centerH -= centerY;
29173             north.updateBox(this.safeBox(b));
29174         }
29175         if(south && south.isVisible()){
29176             var b = south.getBox();
29177             var m = south.getMargins();
29178             b.width = w - (m.left+m.right);
29179             b.x = m.left;
29180             var totalHeight = (b.height + m.top + m.bottom);
29181             b.y = h - totalHeight + m.top;
29182             centerH -= totalHeight;
29183             south.updateBox(this.safeBox(b));
29184         }
29185         if(west && west.isVisible()){
29186             var b = west.getBox();
29187             var m = west.getMargins();
29188             b.height = centerH - (m.top+m.bottom);
29189             b.x = m.left;
29190             b.y = centerY + m.top;
29191             var totalWidth = (b.width + m.left + m.right);
29192             centerX += totalWidth;
29193             centerW -= totalWidth;
29194             west.updateBox(this.safeBox(b));
29195         }
29196         if(east && east.isVisible()){
29197             var b = east.getBox();
29198             var m = east.getMargins();
29199             b.height = centerH - (m.top+m.bottom);
29200             var totalWidth = (b.width + m.left + m.right);
29201             b.x = w - totalWidth + m.left;
29202             b.y = centerY + m.top;
29203             centerW -= totalWidth;
29204             east.updateBox(this.safeBox(b));
29205         }
29206         if(center){
29207             var m = center.getMargins();
29208             var centerBox = {
29209                 x: centerX + m.left,
29210                 y: centerY + m.top,
29211                 width: centerW - (m.left+m.right),
29212                 height: centerH - (m.top+m.bottom)
29213             };
29214             //if(this.hideOnLayout){
29215                 //center.el.setStyle("display", "block");
29216             //}
29217             center.updateBox(this.safeBox(centerBox));
29218         }
29219         this.el.repaint();
29220         this.fireEvent("layout", this);
29221     },
29222
29223     // private
29224     safeBox : function(box){
29225         box.width = Math.max(0, box.width);
29226         box.height = Math.max(0, box.height);
29227         return box;
29228     },
29229
29230     /**
29231      * Adds a ContentPanel (or subclass) to this layout.
29232      * @param {String} target The target region key (north, south, east, west or center).
29233      * @param {Roo.ContentPanel} panel The panel to add
29234      * @return {Roo.ContentPanel} The added panel
29235      */
29236     add : function(target, panel){
29237          
29238         target = target.toLowerCase();
29239         return this.regions[target].add(panel);
29240     },
29241
29242     /**
29243      * Remove a ContentPanel (or subclass) to this layout.
29244      * @param {String} target The target region key (north, south, east, west or center).
29245      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29246      * @return {Roo.ContentPanel} The removed panel
29247      */
29248     remove : function(target, panel){
29249         target = target.toLowerCase();
29250         return this.regions[target].remove(panel);
29251     },
29252
29253     /**
29254      * Searches all regions for a panel with the specified id
29255      * @param {String} panelId
29256      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29257      */
29258     findPanel : function(panelId){
29259         var rs = this.regions;
29260         for(var target in rs){
29261             if(typeof rs[target] != "function"){
29262                 var p = rs[target].getPanel(panelId);
29263                 if(p){
29264                     return p;
29265                 }
29266             }
29267         }
29268         return null;
29269     },
29270
29271     /**
29272      * Searches all regions for a panel with the specified id and activates (shows) it.
29273      * @param {String/ContentPanel} panelId The panels id or the panel itself
29274      * @return {Roo.ContentPanel} The shown panel or null
29275      */
29276     showPanel : function(panelId) {
29277       var rs = this.regions;
29278       for(var target in rs){
29279          var r = rs[target];
29280          if(typeof r != "function"){
29281             if(r.hasPanel(panelId)){
29282                return r.showPanel(panelId);
29283             }
29284          }
29285       }
29286       return null;
29287    },
29288
29289    /**
29290      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29291      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29292      */
29293     restoreState : function(provider){
29294         if(!provider){
29295             provider = Roo.state.Manager;
29296         }
29297         var sm = new Roo.LayoutStateManager();
29298         sm.init(this, provider);
29299     },
29300
29301     /**
29302      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29303      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29304      * a valid ContentPanel config object.  Example:
29305      * <pre><code>
29306 // Create the main layout
29307 var layout = new Roo.BorderLayout('main-ct', {
29308     west: {
29309         split:true,
29310         minSize: 175,
29311         titlebar: true
29312     },
29313     center: {
29314         title:'Components'
29315     }
29316 }, 'main-ct');
29317
29318 // Create and add multiple ContentPanels at once via configs
29319 layout.batchAdd({
29320    west: {
29321        id: 'source-files',
29322        autoCreate:true,
29323        title:'Ext Source Files',
29324        autoScroll:true,
29325        fitToFrame:true
29326    },
29327    center : {
29328        el: cview,
29329        autoScroll:true,
29330        fitToFrame:true,
29331        toolbar: tb,
29332        resizeEl:'cbody'
29333    }
29334 });
29335 </code></pre>
29336      * @param {Object} regions An object containing ContentPanel configs by region name
29337      */
29338     batchAdd : function(regions){
29339         this.beginUpdate();
29340         for(var rname in regions){
29341             var lr = this.regions[rname];
29342             if(lr){
29343                 this.addTypedPanels(lr, regions[rname]);
29344             }
29345         }
29346         this.endUpdate();
29347     },
29348
29349     // private
29350     addTypedPanels : function(lr, ps){
29351         if(typeof ps == 'string'){
29352             lr.add(new Roo.ContentPanel(ps));
29353         }
29354         else if(ps instanceof Array){
29355             for(var i =0, len = ps.length; i < len; i++){
29356                 this.addTypedPanels(lr, ps[i]);
29357             }
29358         }
29359         else if(!ps.events){ // raw config?
29360             var el = ps.el;
29361             delete ps.el; // prevent conflict
29362             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29363         }
29364         else {  // panel object assumed!
29365             lr.add(ps);
29366         }
29367     },
29368     /**
29369      * Adds a xtype elements to the layout.
29370      * <pre><code>
29371
29372 layout.addxtype({
29373        xtype : 'ContentPanel',
29374        region: 'west',
29375        items: [ .... ]
29376    }
29377 );
29378
29379 layout.addxtype({
29380         xtype : 'NestedLayoutPanel',
29381         region: 'west',
29382         layout: {
29383            center: { },
29384            west: { }   
29385         },
29386         items : [ ... list of content panels or nested layout panels.. ]
29387    }
29388 );
29389 </code></pre>
29390      * @param {Object} cfg Xtype definition of item to add.
29391      */
29392     addxtype : function(cfg)
29393     {
29394         // basically accepts a pannel...
29395         // can accept a layout region..!?!?
29396        // console.log('BorderLayout add ' + cfg.xtype)
29397         
29398         if (!cfg.xtype.match(/Panel$/)) {
29399             return false;
29400         }
29401         var ret = false;
29402         var region = cfg.region;
29403         delete cfg.region;
29404         
29405           
29406         var xitems = [];
29407         if (cfg.items) {
29408             xitems = cfg.items;
29409             delete cfg.items;
29410         }
29411         
29412         
29413         switch(cfg.xtype) 
29414         {
29415             case 'ContentPanel':  // ContentPanel (el, cfg)
29416             case 'ScrollPanel':  // ContentPanel (el, cfg)
29417                 if(cfg.autoCreate) {
29418                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29419                 } else {
29420                     var el = this.el.createChild();
29421                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29422                 }
29423                 
29424                 this.add(region, ret);
29425                 break;
29426             
29427             
29428             case 'TreePanel': // our new panel!
29429                 cfg.el = this.el.createChild();
29430                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29431                 this.add(region, ret);
29432                 break;
29433             
29434             case 'NestedLayoutPanel': 
29435                 // create a new Layout (which is  a Border Layout...
29436                 var el = this.el.createChild();
29437                 var clayout = cfg.layout;
29438                 delete cfg.layout;
29439                 clayout.items   = clayout.items  || [];
29440                 // replace this exitems with the clayout ones..
29441                 xitems = clayout.items;
29442                  
29443                 
29444                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29445                     cfg.background = false;
29446                 }
29447                 var layout = new Roo.BorderLayout(el, clayout);
29448                 
29449                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29450                 //console.log('adding nested layout panel '  + cfg.toSource());
29451                 this.add(region, ret);
29452                 
29453                 break;
29454                 
29455             case 'GridPanel': 
29456             
29457                 // needs grid and region
29458                 
29459                 //var el = this.getRegion(region).el.createChild();
29460                 var el = this.el.createChild();
29461                 // create the grid first...
29462                 
29463                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29464                 delete cfg.grid;
29465                 if (region == 'center' && this.active ) {
29466                     cfg.background = false;
29467                 }
29468                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29469                 
29470                 this.add(region, ret);
29471                 if (cfg.background) {
29472                     ret.on('activate', function(gp) {
29473                         if (!gp.grid.rendered) {
29474                             gp.grid.render();
29475                         }
29476                     });
29477                 } else {
29478                     grid.render();
29479                 }
29480                 break;
29481            
29482                
29483                 
29484                 
29485             default: 
29486                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29487                 return;
29488              // GridPanel (grid, cfg)
29489             
29490         }
29491         this.beginUpdate();
29492         // add children..
29493         Roo.each(xitems, function(i)  {
29494             ret.addxtype(i);
29495         });
29496         this.endUpdate();
29497         return ret;
29498         
29499     }
29500 });
29501
29502 /**
29503  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29504  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29505  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29506  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29507  * <pre><code>
29508 // shorthand
29509 var CP = Roo.ContentPanel;
29510
29511 var layout = Roo.BorderLayout.create({
29512     north: {
29513         initialSize: 25,
29514         titlebar: false,
29515         panels: [new CP("north", "North")]
29516     },
29517     west: {
29518         split:true,
29519         initialSize: 200,
29520         minSize: 175,
29521         maxSize: 400,
29522         titlebar: true,
29523         collapsible: true,
29524         panels: [new CP("west", {title: "West"})]
29525     },
29526     east: {
29527         split:true,
29528         initialSize: 202,
29529         minSize: 175,
29530         maxSize: 400,
29531         titlebar: true,
29532         collapsible: true,
29533         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29534     },
29535     south: {
29536         split:true,
29537         initialSize: 100,
29538         minSize: 100,
29539         maxSize: 200,
29540         titlebar: true,
29541         collapsible: true,
29542         panels: [new CP("south", {title: "South", closable: true})]
29543     },
29544     center: {
29545         titlebar: true,
29546         autoScroll:true,
29547         resizeTabs: true,
29548         minTabWidth: 50,
29549         preferredTabWidth: 150,
29550         panels: [
29551             new CP("center1", {title: "Close Me", closable: true}),
29552             new CP("center2", {title: "Center Panel", closable: false})
29553         ]
29554     }
29555 }, document.body);
29556
29557 layout.getRegion("center").showPanel("center1");
29558 </code></pre>
29559  * @param config
29560  * @param targetEl
29561  */
29562 Roo.BorderLayout.create = function(config, targetEl){
29563     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29564     layout.beginUpdate();
29565     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29566     for(var j = 0, jlen = regions.length; j < jlen; j++){
29567         var lr = regions[j];
29568         if(layout.regions[lr] && config[lr].panels){
29569             var r = layout.regions[lr];
29570             var ps = config[lr].panels;
29571             layout.addTypedPanels(r, ps);
29572         }
29573     }
29574     layout.endUpdate();
29575     return layout;
29576 };
29577
29578 // private
29579 Roo.BorderLayout.RegionFactory = {
29580     // private
29581     validRegions : ["north","south","east","west","center"],
29582
29583     // private
29584     create : function(target, mgr, config){
29585         target = target.toLowerCase();
29586         if(config.lightweight || config.basic){
29587             return new Roo.BasicLayoutRegion(mgr, config, target);
29588         }
29589         switch(target){
29590             case "north":
29591                 return new Roo.NorthLayoutRegion(mgr, config);
29592             case "south":
29593                 return new Roo.SouthLayoutRegion(mgr, config);
29594             case "east":
29595                 return new Roo.EastLayoutRegion(mgr, config);
29596             case "west":
29597                 return new Roo.WestLayoutRegion(mgr, config);
29598             case "center":
29599                 return new Roo.CenterLayoutRegion(mgr, config);
29600         }
29601         throw 'Layout region "'+target+'" not supported.';
29602     }
29603 };/*
29604  * Based on:
29605  * Ext JS Library 1.1.1
29606  * Copyright(c) 2006-2007, Ext JS, LLC.
29607  *
29608  * Originally Released Under LGPL - original licence link has changed is not relivant.
29609  *
29610  * Fork - LGPL
29611  * <script type="text/javascript">
29612  */
29613  
29614 /**
29615  * @class Roo.BasicLayoutRegion
29616  * @extends Roo.util.Observable
29617  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29618  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29619  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29620  */
29621 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29622     this.mgr = mgr;
29623     this.position  = pos;
29624     this.events = {
29625         /**
29626          * @scope Roo.BasicLayoutRegion
29627          */
29628         
29629         /**
29630          * @event beforeremove
29631          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29632          * @param {Roo.LayoutRegion} this
29633          * @param {Roo.ContentPanel} panel The panel
29634          * @param {Object} e The cancel event object
29635          */
29636         "beforeremove" : true,
29637         /**
29638          * @event invalidated
29639          * Fires when the layout for this region is changed.
29640          * @param {Roo.LayoutRegion} this
29641          */
29642         "invalidated" : true,
29643         /**
29644          * @event visibilitychange
29645          * Fires when this region is shown or hidden 
29646          * @param {Roo.LayoutRegion} this
29647          * @param {Boolean} visibility true or false
29648          */
29649         "visibilitychange" : true,
29650         /**
29651          * @event paneladded
29652          * Fires when a panel is added. 
29653          * @param {Roo.LayoutRegion} this
29654          * @param {Roo.ContentPanel} panel The panel
29655          */
29656         "paneladded" : true,
29657         /**
29658          * @event panelremoved
29659          * Fires when a panel is removed. 
29660          * @param {Roo.LayoutRegion} this
29661          * @param {Roo.ContentPanel} panel The panel
29662          */
29663         "panelremoved" : true,
29664         /**
29665          * @event collapsed
29666          * Fires when this region is collapsed.
29667          * @param {Roo.LayoutRegion} this
29668          */
29669         "collapsed" : true,
29670         /**
29671          * @event expanded
29672          * Fires when this region is expanded.
29673          * @param {Roo.LayoutRegion} this
29674          */
29675         "expanded" : true,
29676         /**
29677          * @event slideshow
29678          * Fires when this region is slid into view.
29679          * @param {Roo.LayoutRegion} this
29680          */
29681         "slideshow" : true,
29682         /**
29683          * @event slidehide
29684          * Fires when this region slides out of view. 
29685          * @param {Roo.LayoutRegion} this
29686          */
29687         "slidehide" : true,
29688         /**
29689          * @event panelactivated
29690          * Fires when a panel is activated. 
29691          * @param {Roo.LayoutRegion} this
29692          * @param {Roo.ContentPanel} panel The activated panel
29693          */
29694         "panelactivated" : true,
29695         /**
29696          * @event resized
29697          * Fires when the user resizes this region. 
29698          * @param {Roo.LayoutRegion} this
29699          * @param {Number} newSize The new size (width for east/west, height for north/south)
29700          */
29701         "resized" : true
29702     };
29703     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29704     this.panels = new Roo.util.MixedCollection();
29705     this.panels.getKey = this.getPanelId.createDelegate(this);
29706     this.box = null;
29707     this.activePanel = null;
29708     // ensure listeners are added...
29709     
29710     if (config.listeners || config.events) {
29711         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29712             listeners : config.listeners || {},
29713             events : config.events || {}
29714         });
29715     }
29716     
29717     if(skipConfig !== true){
29718         this.applyConfig(config);
29719     }
29720 };
29721
29722 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29723     getPanelId : function(p){
29724         return p.getId();
29725     },
29726     
29727     applyConfig : function(config){
29728         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29729         this.config = config;
29730         
29731     },
29732     
29733     /**
29734      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29735      * the width, for horizontal (north, south) the height.
29736      * @param {Number} newSize The new width or height
29737      */
29738     resizeTo : function(newSize){
29739         var el = this.el ? this.el :
29740                  (this.activePanel ? this.activePanel.getEl() : null);
29741         if(el){
29742             switch(this.position){
29743                 case "east":
29744                 case "west":
29745                     el.setWidth(newSize);
29746                     this.fireEvent("resized", this, newSize);
29747                 break;
29748                 case "north":
29749                 case "south":
29750                     el.setHeight(newSize);
29751                     this.fireEvent("resized", this, newSize);
29752                 break;                
29753             }
29754         }
29755     },
29756     
29757     getBox : function(){
29758         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29759     },
29760     
29761     getMargins : function(){
29762         return this.margins;
29763     },
29764     
29765     updateBox : function(box){
29766         this.box = box;
29767         var el = this.activePanel.getEl();
29768         el.dom.style.left = box.x + "px";
29769         el.dom.style.top = box.y + "px";
29770         this.activePanel.setSize(box.width, box.height);
29771     },
29772     
29773     /**
29774      * Returns the container element for this region.
29775      * @return {Roo.Element}
29776      */
29777     getEl : function(){
29778         return this.activePanel;
29779     },
29780     
29781     /**
29782      * Returns true if this region is currently visible.
29783      * @return {Boolean}
29784      */
29785     isVisible : function(){
29786         return this.activePanel ? true : false;
29787     },
29788     
29789     setActivePanel : function(panel){
29790         panel = this.getPanel(panel);
29791         if(this.activePanel && this.activePanel != panel){
29792             this.activePanel.setActiveState(false);
29793             this.activePanel.getEl().setLeftTop(-10000,-10000);
29794         }
29795         this.activePanel = panel;
29796         panel.setActiveState(true);
29797         if(this.box){
29798             panel.setSize(this.box.width, this.box.height);
29799         }
29800         this.fireEvent("panelactivated", this, panel);
29801         this.fireEvent("invalidated");
29802     },
29803     
29804     /**
29805      * Show the specified panel.
29806      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29807      * @return {Roo.ContentPanel} The shown panel or null
29808      */
29809     showPanel : function(panel){
29810         if(panel = this.getPanel(panel)){
29811             this.setActivePanel(panel);
29812         }
29813         return panel;
29814     },
29815     
29816     /**
29817      * Get the active panel for this region.
29818      * @return {Roo.ContentPanel} The active panel or null
29819      */
29820     getActivePanel : function(){
29821         return this.activePanel;
29822     },
29823     
29824     /**
29825      * Add the passed ContentPanel(s)
29826      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29827      * @return {Roo.ContentPanel} The panel added (if only one was added)
29828      */
29829     add : function(panel){
29830         if(arguments.length > 1){
29831             for(var i = 0, len = arguments.length; i < len; i++) {
29832                 this.add(arguments[i]);
29833             }
29834             return null;
29835         }
29836         if(this.hasPanel(panel)){
29837             this.showPanel(panel);
29838             return panel;
29839         }
29840         var el = panel.getEl();
29841         if(el.dom.parentNode != this.mgr.el.dom){
29842             this.mgr.el.dom.appendChild(el.dom);
29843         }
29844         if(panel.setRegion){
29845             panel.setRegion(this);
29846         }
29847         this.panels.add(panel);
29848         el.setStyle("position", "absolute");
29849         if(!panel.background){
29850             this.setActivePanel(panel);
29851             if(this.config.initialSize && this.panels.getCount()==1){
29852                 this.resizeTo(this.config.initialSize);
29853             }
29854         }
29855         this.fireEvent("paneladded", this, panel);
29856         return panel;
29857     },
29858     
29859     /**
29860      * Returns true if the panel is in this region.
29861      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29862      * @return {Boolean}
29863      */
29864     hasPanel : function(panel){
29865         if(typeof panel == "object"){ // must be panel obj
29866             panel = panel.getId();
29867         }
29868         return this.getPanel(panel) ? true : false;
29869     },
29870     
29871     /**
29872      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29873      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29874      * @param {Boolean} preservePanel Overrides the config preservePanel option
29875      * @return {Roo.ContentPanel} The panel that was removed
29876      */
29877     remove : function(panel, preservePanel){
29878         panel = this.getPanel(panel);
29879         if(!panel){
29880             return null;
29881         }
29882         var e = {};
29883         this.fireEvent("beforeremove", this, panel, e);
29884         if(e.cancel === true){
29885             return null;
29886         }
29887         var panelId = panel.getId();
29888         this.panels.removeKey(panelId);
29889         return panel;
29890     },
29891     
29892     /**
29893      * Returns the panel specified or null if it's not in this region.
29894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29895      * @return {Roo.ContentPanel}
29896      */
29897     getPanel : function(id){
29898         if(typeof id == "object"){ // must be panel obj
29899             return id;
29900         }
29901         return this.panels.get(id);
29902     },
29903     
29904     /**
29905      * Returns this regions position (north/south/east/west/center).
29906      * @return {String} 
29907      */
29908     getPosition: function(){
29909         return this.position;    
29910     }
29911 });/*
29912  * Based on:
29913  * Ext JS Library 1.1.1
29914  * Copyright(c) 2006-2007, Ext JS, LLC.
29915  *
29916  * Originally Released Under LGPL - original licence link has changed is not relivant.
29917  *
29918  * Fork - LGPL
29919  * <script type="text/javascript">
29920  */
29921  
29922 /**
29923  * @class Roo.LayoutRegion
29924  * @extends Roo.BasicLayoutRegion
29925  * This class represents a region in a layout manager.
29926  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29927  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29928  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29929  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29930  * @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})
29931  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29932  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29933  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29934  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29935  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29936  * @cfg {String} title The title for the region (overrides panel titles)
29937  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29938  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29939  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29940  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29941  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29942  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29943  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29944  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29945  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29946  * @cfg {Boolean} showPin True to show a pin button
29947 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29948 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29949 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29950 * @cfg {Number} width  For East/West panels
29951 * @cfg {Number} height For North/South panels
29952 * @cfg {Boolean} split To show the splitter
29953  */
29954 Roo.LayoutRegion = function(mgr, config, pos){
29955     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29956     var dh = Roo.DomHelper;
29957     /** This region's container element 
29958     * @type Roo.Element */
29959     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29960     /** This region's title element 
29961     * @type Roo.Element */
29962
29963     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29964         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29965         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29966     ]}, true);
29967     this.titleEl.enableDisplayMode();
29968     /** This region's title text element 
29969     * @type HTMLElement */
29970     this.titleTextEl = this.titleEl.dom.firstChild;
29971     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29972     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29973     this.closeBtn.enableDisplayMode();
29974     this.closeBtn.on("click", this.closeClicked, this);
29975     this.closeBtn.hide();
29976
29977     this.createBody(config);
29978     this.visible = true;
29979     this.collapsed = false;
29980
29981     if(config.hideWhenEmpty){
29982         this.hide();
29983         this.on("paneladded", this.validateVisibility, this);
29984         this.on("panelremoved", this.validateVisibility, this);
29985     }
29986     this.applyConfig(config);
29987 };
29988
29989 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29990
29991     createBody : function(){
29992         /** This region's body element 
29993         * @type Roo.Element */
29994         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29995     },
29996
29997     applyConfig : function(c){
29998         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29999             var dh = Roo.DomHelper;
30000             if(c.titlebar !== false){
30001                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30002                 this.collapseBtn.on("click", this.collapse, this);
30003                 this.collapseBtn.enableDisplayMode();
30004
30005                 if(c.showPin === true || this.showPin){
30006                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30007                     this.stickBtn.enableDisplayMode();
30008                     this.stickBtn.on("click", this.expand, this);
30009                     this.stickBtn.hide();
30010                 }
30011             }
30012             /** This region's collapsed element
30013             * @type Roo.Element */
30014             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30015                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30016             ]}, true);
30017             if(c.floatable !== false){
30018                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30019                this.collapsedEl.on("click", this.collapseClick, this);
30020             }
30021
30022             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30023                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30024                    id: "message", unselectable: "on", style:{"float":"left"}});
30025                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30026              }
30027             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30028             this.expandBtn.on("click", this.expand, this);
30029         }
30030         if(this.collapseBtn){
30031             this.collapseBtn.setVisible(c.collapsible == true);
30032         }
30033         this.cmargins = c.cmargins || this.cmargins ||
30034                          (this.position == "west" || this.position == "east" ?
30035                              {top: 0, left: 2, right:2, bottom: 0} :
30036                              {top: 2, left: 0, right:0, bottom: 2});
30037         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30038         this.bottomTabs = c.tabPosition != "top";
30039         this.autoScroll = c.autoScroll || false;
30040         if(this.autoScroll){
30041             this.bodyEl.setStyle("overflow", "auto");
30042         }else{
30043             this.bodyEl.setStyle("overflow", "hidden");
30044         }
30045         //if(c.titlebar !== false){
30046             if((!c.titlebar && !c.title) || c.titlebar === false){
30047                 this.titleEl.hide();
30048             }else{
30049                 this.titleEl.show();
30050                 if(c.title){
30051                     this.titleTextEl.innerHTML = c.title;
30052                 }
30053             }
30054         //}
30055         this.duration = c.duration || .30;
30056         this.slideDuration = c.slideDuration || .45;
30057         this.config = c;
30058         if(c.collapsed){
30059             this.collapse(true);
30060         }
30061         if(c.hidden){
30062             this.hide();
30063         }
30064     },
30065     /**
30066      * Returns true if this region is currently visible.
30067      * @return {Boolean}
30068      */
30069     isVisible : function(){
30070         return this.visible;
30071     },
30072
30073     /**
30074      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30075      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30076      */
30077     setCollapsedTitle : function(title){
30078         title = title || "&#160;";
30079         if(this.collapsedTitleTextEl){
30080             this.collapsedTitleTextEl.innerHTML = title;
30081         }
30082     },
30083
30084     getBox : function(){
30085         var b;
30086         if(!this.collapsed){
30087             b = this.el.getBox(false, true);
30088         }else{
30089             b = this.collapsedEl.getBox(false, true);
30090         }
30091         return b;
30092     },
30093
30094     getMargins : function(){
30095         return this.collapsed ? this.cmargins : this.margins;
30096     },
30097
30098     highlight : function(){
30099         this.el.addClass("x-layout-panel-dragover");
30100     },
30101
30102     unhighlight : function(){
30103         this.el.removeClass("x-layout-panel-dragover");
30104     },
30105
30106     updateBox : function(box){
30107         this.box = box;
30108         if(!this.collapsed){
30109             this.el.dom.style.left = box.x + "px";
30110             this.el.dom.style.top = box.y + "px";
30111             this.updateBody(box.width, box.height);
30112         }else{
30113             this.collapsedEl.dom.style.left = box.x + "px";
30114             this.collapsedEl.dom.style.top = box.y + "px";
30115             this.collapsedEl.setSize(box.width, box.height);
30116         }
30117         if(this.tabs){
30118             this.tabs.autoSizeTabs();
30119         }
30120     },
30121
30122     updateBody : function(w, h){
30123         if(w !== null){
30124             this.el.setWidth(w);
30125             w -= this.el.getBorderWidth("rl");
30126             if(this.config.adjustments){
30127                 w += this.config.adjustments[0];
30128             }
30129         }
30130         if(h !== null){
30131             this.el.setHeight(h);
30132             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30133             h -= this.el.getBorderWidth("tb");
30134             if(this.config.adjustments){
30135                 h += this.config.adjustments[1];
30136             }
30137             this.bodyEl.setHeight(h);
30138             if(this.tabs){
30139                 h = this.tabs.syncHeight(h);
30140             }
30141         }
30142         if(this.panelSize){
30143             w = w !== null ? w : this.panelSize.width;
30144             h = h !== null ? h : this.panelSize.height;
30145         }
30146         if(this.activePanel){
30147             var el = this.activePanel.getEl();
30148             w = w !== null ? w : el.getWidth();
30149             h = h !== null ? h : el.getHeight();
30150             this.panelSize = {width: w, height: h};
30151             this.activePanel.setSize(w, h);
30152         }
30153         if(Roo.isIE && this.tabs){
30154             this.tabs.el.repaint();
30155         }
30156     },
30157
30158     /**
30159      * Returns the container element for this region.
30160      * @return {Roo.Element}
30161      */
30162     getEl : function(){
30163         return this.el;
30164     },
30165
30166     /**
30167      * Hides this region.
30168      */
30169     hide : function(){
30170         if(!this.collapsed){
30171             this.el.dom.style.left = "-2000px";
30172             this.el.hide();
30173         }else{
30174             this.collapsedEl.dom.style.left = "-2000px";
30175             this.collapsedEl.hide();
30176         }
30177         this.visible = false;
30178         this.fireEvent("visibilitychange", this, false);
30179     },
30180
30181     /**
30182      * Shows this region if it was previously hidden.
30183      */
30184     show : function(){
30185         if(!this.collapsed){
30186             this.el.show();
30187         }else{
30188             this.collapsedEl.show();
30189         }
30190         this.visible = true;
30191         this.fireEvent("visibilitychange", this, true);
30192     },
30193
30194     closeClicked : function(){
30195         if(this.activePanel){
30196             this.remove(this.activePanel);
30197         }
30198     },
30199
30200     collapseClick : function(e){
30201         if(this.isSlid){
30202            e.stopPropagation();
30203            this.slideIn();
30204         }else{
30205            e.stopPropagation();
30206            this.slideOut();
30207         }
30208     },
30209
30210     /**
30211      * Collapses this region.
30212      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30213      */
30214     collapse : function(skipAnim){
30215         if(this.collapsed) return;
30216         this.collapsed = true;
30217         if(this.split){
30218             this.split.el.hide();
30219         }
30220         if(this.config.animate && skipAnim !== true){
30221             this.fireEvent("invalidated", this);
30222             this.animateCollapse();
30223         }else{
30224             this.el.setLocation(-20000,-20000);
30225             this.el.hide();
30226             this.collapsedEl.show();
30227             this.fireEvent("collapsed", this);
30228             this.fireEvent("invalidated", this);
30229         }
30230     },
30231
30232     animateCollapse : function(){
30233         // overridden
30234     },
30235
30236     /**
30237      * Expands this region if it was previously collapsed.
30238      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30239      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30240      */
30241     expand : function(e, skipAnim){
30242         if(e) e.stopPropagation();
30243         if(!this.collapsed || this.el.hasActiveFx()) return;
30244         if(this.isSlid){
30245             this.afterSlideIn();
30246             skipAnim = true;
30247         }
30248         this.collapsed = false;
30249         if(this.config.animate && skipAnim !== true){
30250             this.animateExpand();
30251         }else{
30252             this.el.show();
30253             if(this.split){
30254                 this.split.el.show();
30255             }
30256             this.collapsedEl.setLocation(-2000,-2000);
30257             this.collapsedEl.hide();
30258             this.fireEvent("invalidated", this);
30259             this.fireEvent("expanded", this);
30260         }
30261     },
30262
30263     animateExpand : function(){
30264         // overridden
30265     },
30266
30267     initTabs : function(){
30268         this.bodyEl.setStyle("overflow", "hidden");
30269         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30270             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30271             disableTooltips: this.config.disableTabTips
30272         });
30273         if(this.config.hideTabs){
30274             ts.stripWrap.setDisplayed(false);
30275         }
30276         this.tabs = ts;
30277         ts.resizeTabs = this.config.resizeTabs === true;
30278         ts.minTabWidth = this.config.minTabWidth || 40;
30279         ts.maxTabWidth = this.config.maxTabWidth || 250;
30280         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30281         ts.monitorResize = false;
30282         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30283         ts.bodyEl.addClass('x-layout-tabs-body');
30284         this.panels.each(this.initPanelAsTab, this);
30285     },
30286
30287     initPanelAsTab : function(panel){
30288         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30289                     this.config.closeOnTab && panel.isClosable());
30290         if(panel.tabTip !== undefined){
30291             ti.setTooltip(panel.tabTip);
30292         }
30293         ti.on("activate", function(){
30294               this.setActivePanel(panel);
30295         }, this);
30296         if(this.config.closeOnTab){
30297             ti.on("beforeclose", function(t, e){
30298                 e.cancel = true;
30299                 this.remove(panel);
30300             }, this);
30301         }
30302         return ti;
30303     },
30304
30305     updatePanelTitle : function(panel, title){
30306         if(this.activePanel == panel){
30307             this.updateTitle(title);
30308         }
30309         if(this.tabs){
30310             var ti = this.tabs.getTab(panel.getEl().id);
30311             ti.setText(title);
30312             if(panel.tabTip !== undefined){
30313                 ti.setTooltip(panel.tabTip);
30314             }
30315         }
30316     },
30317
30318     updateTitle : function(title){
30319         if(this.titleTextEl && !this.config.title){
30320             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30321         }
30322     },
30323
30324     setActivePanel : function(panel){
30325         panel = this.getPanel(panel);
30326         if(this.activePanel && this.activePanel != panel){
30327             this.activePanel.setActiveState(false);
30328         }
30329         this.activePanel = panel;
30330         panel.setActiveState(true);
30331         if(this.panelSize){
30332             panel.setSize(this.panelSize.width, this.panelSize.height);
30333         }
30334         if(this.closeBtn){
30335             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30336         }
30337         this.updateTitle(panel.getTitle());
30338         if(this.tabs){
30339             this.fireEvent("invalidated", this);
30340         }
30341         this.fireEvent("panelactivated", this, panel);
30342     },
30343
30344     /**
30345      * Shows the specified panel.
30346      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30347      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30348      */
30349     showPanel : function(panel){
30350         if(panel = this.getPanel(panel)){
30351             if(this.tabs){
30352                 var tab = this.tabs.getTab(panel.getEl().id);
30353                 if(tab.isHidden()){
30354                     this.tabs.unhideTab(tab.id);
30355                 }
30356                 tab.activate();
30357             }else{
30358                 this.setActivePanel(panel);
30359             }
30360         }
30361         return panel;
30362     },
30363
30364     /**
30365      * Get the active panel for this region.
30366      * @return {Roo.ContentPanel} The active panel or null
30367      */
30368     getActivePanel : function(){
30369         return this.activePanel;
30370     },
30371
30372     validateVisibility : function(){
30373         if(this.panels.getCount() < 1){
30374             this.updateTitle("&#160;");
30375             this.closeBtn.hide();
30376             this.hide();
30377         }else{
30378             if(!this.isVisible()){
30379                 this.show();
30380             }
30381         }
30382     },
30383
30384     /**
30385      * Adds the passed ContentPanel(s) to this region.
30386      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30387      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30388      */
30389     add : function(panel){
30390         if(arguments.length > 1){
30391             for(var i = 0, len = arguments.length; i < len; i++) {
30392                 this.add(arguments[i]);
30393             }
30394             return null;
30395         }
30396         if(this.hasPanel(panel)){
30397             this.showPanel(panel);
30398             return panel;
30399         }
30400         panel.setRegion(this);
30401         this.panels.add(panel);
30402         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30403             this.bodyEl.dom.appendChild(panel.getEl().dom);
30404             if(panel.background !== true){
30405                 this.setActivePanel(panel);
30406             }
30407             this.fireEvent("paneladded", this, panel);
30408             return panel;
30409         }
30410         if(!this.tabs){
30411             this.initTabs();
30412         }else{
30413             this.initPanelAsTab(panel);
30414         }
30415         if(panel.background !== true){
30416             this.tabs.activate(panel.getEl().id);
30417         }
30418         this.fireEvent("paneladded", this, panel);
30419         return panel;
30420     },
30421
30422     /**
30423      * Hides the tab for the specified panel.
30424      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30425      */
30426     hidePanel : function(panel){
30427         if(this.tabs && (panel = this.getPanel(panel))){
30428             this.tabs.hideTab(panel.getEl().id);
30429         }
30430     },
30431
30432     /**
30433      * Unhides the tab for a previously hidden panel.
30434      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30435      */
30436     unhidePanel : function(panel){
30437         if(this.tabs && (panel = this.getPanel(panel))){
30438             this.tabs.unhideTab(panel.getEl().id);
30439         }
30440     },
30441
30442     clearPanels : function(){
30443         while(this.panels.getCount() > 0){
30444              this.remove(this.panels.first());
30445         }
30446     },
30447
30448     /**
30449      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30450      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30451      * @param {Boolean} preservePanel Overrides the config preservePanel option
30452      * @return {Roo.ContentPanel} The panel that was removed
30453      */
30454     remove : function(panel, preservePanel){
30455         panel = this.getPanel(panel);
30456         if(!panel){
30457             return null;
30458         }
30459         var e = {};
30460         this.fireEvent("beforeremove", this, panel, e);
30461         if(e.cancel === true){
30462             return null;
30463         }
30464         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30465         var panelId = panel.getId();
30466         this.panels.removeKey(panelId);
30467         if(preservePanel){
30468             document.body.appendChild(panel.getEl().dom);
30469         }
30470         if(this.tabs){
30471             this.tabs.removeTab(panel.getEl().id);
30472         }else if (!preservePanel){
30473             this.bodyEl.dom.removeChild(panel.getEl().dom);
30474         }
30475         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30476             var p = this.panels.first();
30477             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30478             tempEl.appendChild(p.getEl().dom);
30479             this.bodyEl.update("");
30480             this.bodyEl.dom.appendChild(p.getEl().dom);
30481             tempEl = null;
30482             this.updateTitle(p.getTitle());
30483             this.tabs = null;
30484             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30485             this.setActivePanel(p);
30486         }
30487         panel.setRegion(null);
30488         if(this.activePanel == panel){
30489             this.activePanel = null;
30490         }
30491         if(this.config.autoDestroy !== false && preservePanel !== true){
30492             try{panel.destroy();}catch(e){}
30493         }
30494         this.fireEvent("panelremoved", this, panel);
30495         return panel;
30496     },
30497
30498     /**
30499      * Returns the TabPanel component used by this region
30500      * @return {Roo.TabPanel}
30501      */
30502     getTabs : function(){
30503         return this.tabs;
30504     },
30505
30506     createTool : function(parentEl, className){
30507         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30508             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30509         btn.addClassOnOver("x-layout-tools-button-over");
30510         return btn;
30511     }
30512 });/*
30513  * Based on:
30514  * Ext JS Library 1.1.1
30515  * Copyright(c) 2006-2007, Ext JS, LLC.
30516  *
30517  * Originally Released Under LGPL - original licence link has changed is not relivant.
30518  *
30519  * Fork - LGPL
30520  * <script type="text/javascript">
30521  */
30522  
30523
30524
30525 /**
30526  * @class Roo.SplitLayoutRegion
30527  * @extends Roo.LayoutRegion
30528  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30529  */
30530 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30531     this.cursor = cursor;
30532     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30533 };
30534
30535 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30536     splitTip : "Drag to resize.",
30537     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30538     useSplitTips : false,
30539
30540     applyConfig : function(config){
30541         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30542         if(config.split){
30543             if(!this.split){
30544                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30545                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30546                 /** The SplitBar for this region 
30547                 * @type Roo.SplitBar */
30548                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30549                 this.split.on("moved", this.onSplitMove, this);
30550                 this.split.useShim = config.useShim === true;
30551                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30552                 if(this.useSplitTips){
30553                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30554                 }
30555                 if(config.collapsible){
30556                     this.split.el.on("dblclick", this.collapse,  this);
30557                 }
30558             }
30559             if(typeof config.minSize != "undefined"){
30560                 this.split.minSize = config.minSize;
30561             }
30562             if(typeof config.maxSize != "undefined"){
30563                 this.split.maxSize = config.maxSize;
30564             }
30565             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30566                 this.hideSplitter();
30567             }
30568         }
30569     },
30570
30571     getHMaxSize : function(){
30572          var cmax = this.config.maxSize || 10000;
30573          var center = this.mgr.getRegion("center");
30574          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30575     },
30576
30577     getVMaxSize : function(){
30578          var cmax = this.config.maxSize || 10000;
30579          var center = this.mgr.getRegion("center");
30580          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30581     },
30582
30583     onSplitMove : function(split, newSize){
30584         this.fireEvent("resized", this, newSize);
30585     },
30586     
30587     /** 
30588      * Returns the {@link Roo.SplitBar} for this region.
30589      * @return {Roo.SplitBar}
30590      */
30591     getSplitBar : function(){
30592         return this.split;
30593     },
30594     
30595     hide : function(){
30596         this.hideSplitter();
30597         Roo.SplitLayoutRegion.superclass.hide.call(this);
30598     },
30599
30600     hideSplitter : function(){
30601         if(this.split){
30602             this.split.el.setLocation(-2000,-2000);
30603             this.split.el.hide();
30604         }
30605     },
30606
30607     show : function(){
30608         if(this.split){
30609             this.split.el.show();
30610         }
30611         Roo.SplitLayoutRegion.superclass.show.call(this);
30612     },
30613     
30614     beforeSlide: function(){
30615         if(Roo.isGecko){// firefox overflow auto bug workaround
30616             this.bodyEl.clip();
30617             if(this.tabs) this.tabs.bodyEl.clip();
30618             if(this.activePanel){
30619                 this.activePanel.getEl().clip();
30620                 
30621                 if(this.activePanel.beforeSlide){
30622                     this.activePanel.beforeSlide();
30623                 }
30624             }
30625         }
30626     },
30627     
30628     afterSlide : function(){
30629         if(Roo.isGecko){// firefox overflow auto bug workaround
30630             this.bodyEl.unclip();
30631             if(this.tabs) this.tabs.bodyEl.unclip();
30632             if(this.activePanel){
30633                 this.activePanel.getEl().unclip();
30634                 if(this.activePanel.afterSlide){
30635                     this.activePanel.afterSlide();
30636                 }
30637             }
30638         }
30639     },
30640
30641     initAutoHide : function(){
30642         if(this.autoHide !== false){
30643             if(!this.autoHideHd){
30644                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30645                 this.autoHideHd = {
30646                     "mouseout": function(e){
30647                         if(!e.within(this.el, true)){
30648                             st.delay(500);
30649                         }
30650                     },
30651                     "mouseover" : function(e){
30652                         st.cancel();
30653                     },
30654                     scope : this
30655                 };
30656             }
30657             this.el.on(this.autoHideHd);
30658         }
30659     },
30660
30661     clearAutoHide : function(){
30662         if(this.autoHide !== false){
30663             this.el.un("mouseout", this.autoHideHd.mouseout);
30664             this.el.un("mouseover", this.autoHideHd.mouseover);
30665         }
30666     },
30667
30668     clearMonitor : function(){
30669         Roo.get(document).un("click", this.slideInIf, this);
30670     },
30671
30672     // these names are backwards but not changed for compat
30673     slideOut : function(){
30674         if(this.isSlid || this.el.hasActiveFx()){
30675             return;
30676         }
30677         this.isSlid = true;
30678         if(this.collapseBtn){
30679             this.collapseBtn.hide();
30680         }
30681         this.closeBtnState = this.closeBtn.getStyle('display');
30682         this.closeBtn.hide();
30683         if(this.stickBtn){
30684             this.stickBtn.show();
30685         }
30686         this.el.show();
30687         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30688         this.beforeSlide();
30689         this.el.setStyle("z-index", 10001);
30690         this.el.slideIn(this.getSlideAnchor(), {
30691             callback: function(){
30692                 this.afterSlide();
30693                 this.initAutoHide();
30694                 Roo.get(document).on("click", this.slideInIf, this);
30695                 this.fireEvent("slideshow", this);
30696             },
30697             scope: this,
30698             block: true
30699         });
30700     },
30701
30702     afterSlideIn : function(){
30703         this.clearAutoHide();
30704         this.isSlid = false;
30705         this.clearMonitor();
30706         this.el.setStyle("z-index", "");
30707         if(this.collapseBtn){
30708             this.collapseBtn.show();
30709         }
30710         this.closeBtn.setStyle('display', this.closeBtnState);
30711         if(this.stickBtn){
30712             this.stickBtn.hide();
30713         }
30714         this.fireEvent("slidehide", this);
30715     },
30716
30717     slideIn : function(cb){
30718         if(!this.isSlid || this.el.hasActiveFx()){
30719             Roo.callback(cb);
30720             return;
30721         }
30722         this.isSlid = false;
30723         this.beforeSlide();
30724         this.el.slideOut(this.getSlideAnchor(), {
30725             callback: function(){
30726                 this.el.setLeftTop(-10000, -10000);
30727                 this.afterSlide();
30728                 this.afterSlideIn();
30729                 Roo.callback(cb);
30730             },
30731             scope: this,
30732             block: true
30733         });
30734     },
30735     
30736     slideInIf : function(e){
30737         if(!e.within(this.el)){
30738             this.slideIn();
30739         }
30740     },
30741
30742     animateCollapse : function(){
30743         this.beforeSlide();
30744         this.el.setStyle("z-index", 20000);
30745         var anchor = this.getSlideAnchor();
30746         this.el.slideOut(anchor, {
30747             callback : function(){
30748                 this.el.setStyle("z-index", "");
30749                 this.collapsedEl.slideIn(anchor, {duration:.3});
30750                 this.afterSlide();
30751                 this.el.setLocation(-10000,-10000);
30752                 this.el.hide();
30753                 this.fireEvent("collapsed", this);
30754             },
30755             scope: this,
30756             block: true
30757         });
30758     },
30759
30760     animateExpand : function(){
30761         this.beforeSlide();
30762         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30763         this.el.setStyle("z-index", 20000);
30764         this.collapsedEl.hide({
30765             duration:.1
30766         });
30767         this.el.slideIn(this.getSlideAnchor(), {
30768             callback : function(){
30769                 this.el.setStyle("z-index", "");
30770                 this.afterSlide();
30771                 if(this.split){
30772                     this.split.el.show();
30773                 }
30774                 this.fireEvent("invalidated", this);
30775                 this.fireEvent("expanded", this);
30776             },
30777             scope: this,
30778             block: true
30779         });
30780     },
30781
30782     anchors : {
30783         "west" : "left",
30784         "east" : "right",
30785         "north" : "top",
30786         "south" : "bottom"
30787     },
30788
30789     sanchors : {
30790         "west" : "l",
30791         "east" : "r",
30792         "north" : "t",
30793         "south" : "b"
30794     },
30795
30796     canchors : {
30797         "west" : "tl-tr",
30798         "east" : "tr-tl",
30799         "north" : "tl-bl",
30800         "south" : "bl-tl"
30801     },
30802
30803     getAnchor : function(){
30804         return this.anchors[this.position];
30805     },
30806
30807     getCollapseAnchor : function(){
30808         return this.canchors[this.position];
30809     },
30810
30811     getSlideAnchor : function(){
30812         return this.sanchors[this.position];
30813     },
30814
30815     getAlignAdj : function(){
30816         var cm = this.cmargins;
30817         switch(this.position){
30818             case "west":
30819                 return [0, 0];
30820             break;
30821             case "east":
30822                 return [0, 0];
30823             break;
30824             case "north":
30825                 return [0, 0];
30826             break;
30827             case "south":
30828                 return [0, 0];
30829             break;
30830         }
30831     },
30832
30833     getExpandAdj : function(){
30834         var c = this.collapsedEl, cm = this.cmargins;
30835         switch(this.position){
30836             case "west":
30837                 return [-(cm.right+c.getWidth()+cm.left), 0];
30838             break;
30839             case "east":
30840                 return [cm.right+c.getWidth()+cm.left, 0];
30841             break;
30842             case "north":
30843                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30844             break;
30845             case "south":
30846                 return [0, cm.top+cm.bottom+c.getHeight()];
30847             break;
30848         }
30849     }
30850 });/*
30851  * Based on:
30852  * Ext JS Library 1.1.1
30853  * Copyright(c) 2006-2007, Ext JS, LLC.
30854  *
30855  * Originally Released Under LGPL - original licence link has changed is not relivant.
30856  *
30857  * Fork - LGPL
30858  * <script type="text/javascript">
30859  */
30860 /*
30861  * These classes are private internal classes
30862  */
30863 Roo.CenterLayoutRegion = function(mgr, config){
30864     Roo.LayoutRegion.call(this, mgr, config, "center");
30865     this.visible = true;
30866     this.minWidth = config.minWidth || 20;
30867     this.minHeight = config.minHeight || 20;
30868 };
30869
30870 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30871     hide : function(){
30872         // center panel can't be hidden
30873     },
30874     
30875     show : function(){
30876         // center panel can't be hidden
30877     },
30878     
30879     getMinWidth: function(){
30880         return this.minWidth;
30881     },
30882     
30883     getMinHeight: function(){
30884         return this.minHeight;
30885     }
30886 });
30887
30888
30889 Roo.NorthLayoutRegion = function(mgr, config){
30890     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30891     if(this.split){
30892         this.split.placement = Roo.SplitBar.TOP;
30893         this.split.orientation = Roo.SplitBar.VERTICAL;
30894         this.split.el.addClass("x-layout-split-v");
30895     }
30896     var size = config.initialSize || config.height;
30897     if(typeof size != "undefined"){
30898         this.el.setHeight(size);
30899     }
30900 };
30901 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30902     orientation: Roo.SplitBar.VERTICAL,
30903     getBox : function(){
30904         if(this.collapsed){
30905             return this.collapsedEl.getBox();
30906         }
30907         var box = this.el.getBox();
30908         if(this.split){
30909             box.height += this.split.el.getHeight();
30910         }
30911         return box;
30912     },
30913     
30914     updateBox : function(box){
30915         if(this.split && !this.collapsed){
30916             box.height -= this.split.el.getHeight();
30917             this.split.el.setLeft(box.x);
30918             this.split.el.setTop(box.y+box.height);
30919             this.split.el.setWidth(box.width);
30920         }
30921         if(this.collapsed){
30922             this.updateBody(box.width, null);
30923         }
30924         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30925     }
30926 });
30927
30928 Roo.SouthLayoutRegion = function(mgr, config){
30929     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30930     if(this.split){
30931         this.split.placement = Roo.SplitBar.BOTTOM;
30932         this.split.orientation = Roo.SplitBar.VERTICAL;
30933         this.split.el.addClass("x-layout-split-v");
30934     }
30935     var size = config.initialSize || config.height;
30936     if(typeof size != "undefined"){
30937         this.el.setHeight(size);
30938     }
30939 };
30940 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30941     orientation: Roo.SplitBar.VERTICAL,
30942     getBox : function(){
30943         if(this.collapsed){
30944             return this.collapsedEl.getBox();
30945         }
30946         var box = this.el.getBox();
30947         if(this.split){
30948             var sh = this.split.el.getHeight();
30949             box.height += sh;
30950             box.y -= sh;
30951         }
30952         return box;
30953     },
30954     
30955     updateBox : function(box){
30956         if(this.split && !this.collapsed){
30957             var sh = this.split.el.getHeight();
30958             box.height -= sh;
30959             box.y += sh;
30960             this.split.el.setLeft(box.x);
30961             this.split.el.setTop(box.y-sh);
30962             this.split.el.setWidth(box.width);
30963         }
30964         if(this.collapsed){
30965             this.updateBody(box.width, null);
30966         }
30967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30968     }
30969 });
30970
30971 Roo.EastLayoutRegion = function(mgr, config){
30972     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30973     if(this.split){
30974         this.split.placement = Roo.SplitBar.RIGHT;
30975         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30976         this.split.el.addClass("x-layout-split-h");
30977     }
30978     var size = config.initialSize || config.width;
30979     if(typeof size != "undefined"){
30980         this.el.setWidth(size);
30981     }
30982 };
30983 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30984     orientation: Roo.SplitBar.HORIZONTAL,
30985     getBox : function(){
30986         if(this.collapsed){
30987             return this.collapsedEl.getBox();
30988         }
30989         var box = this.el.getBox();
30990         if(this.split){
30991             var sw = this.split.el.getWidth();
30992             box.width += sw;
30993             box.x -= sw;
30994         }
30995         return box;
30996     },
30997
30998     updateBox : function(box){
30999         if(this.split && !this.collapsed){
31000             var sw = this.split.el.getWidth();
31001             box.width -= sw;
31002             this.split.el.setLeft(box.x);
31003             this.split.el.setTop(box.y);
31004             this.split.el.setHeight(box.height);
31005             box.x += sw;
31006         }
31007         if(this.collapsed){
31008             this.updateBody(null, box.height);
31009         }
31010         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31011     }
31012 });
31013
31014 Roo.WestLayoutRegion = function(mgr, config){
31015     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31016     if(this.split){
31017         this.split.placement = Roo.SplitBar.LEFT;
31018         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31019         this.split.el.addClass("x-layout-split-h");
31020     }
31021     var size = config.initialSize || config.width;
31022     if(typeof size != "undefined"){
31023         this.el.setWidth(size);
31024     }
31025 };
31026 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31027     orientation: Roo.SplitBar.HORIZONTAL,
31028     getBox : function(){
31029         if(this.collapsed){
31030             return this.collapsedEl.getBox();
31031         }
31032         var box = this.el.getBox();
31033         if(this.split){
31034             box.width += this.split.el.getWidth();
31035         }
31036         return box;
31037     },
31038     
31039     updateBox : function(box){
31040         if(this.split && !this.collapsed){
31041             var sw = this.split.el.getWidth();
31042             box.width -= sw;
31043             this.split.el.setLeft(box.x+box.width);
31044             this.split.el.setTop(box.y);
31045             this.split.el.setHeight(box.height);
31046         }
31047         if(this.collapsed){
31048             this.updateBody(null, box.height);
31049         }
31050         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31051     }
31052 });
31053 /*
31054  * Based on:
31055  * Ext JS Library 1.1.1
31056  * Copyright(c) 2006-2007, Ext JS, LLC.
31057  *
31058  * Originally Released Under LGPL - original licence link has changed is not relivant.
31059  *
31060  * Fork - LGPL
31061  * <script type="text/javascript">
31062  */
31063  
31064  
31065 /*
31066  * Private internal class for reading and applying state
31067  */
31068 Roo.LayoutStateManager = function(layout){
31069      // default empty state
31070      this.state = {
31071         north: {},
31072         south: {},
31073         east: {},
31074         west: {}       
31075     };
31076 };
31077
31078 Roo.LayoutStateManager.prototype = {
31079     init : function(layout, provider){
31080         this.provider = provider;
31081         var state = provider.get(layout.id+"-layout-state");
31082         if(state){
31083             var wasUpdating = layout.isUpdating();
31084             if(!wasUpdating){
31085                 layout.beginUpdate();
31086             }
31087             for(var key in state){
31088                 if(typeof state[key] != "function"){
31089                     var rstate = state[key];
31090                     var r = layout.getRegion(key);
31091                     if(r && rstate){
31092                         if(rstate.size){
31093                             r.resizeTo(rstate.size);
31094                         }
31095                         if(rstate.collapsed == true){
31096                             r.collapse(true);
31097                         }else{
31098                             r.expand(null, true);
31099                         }
31100                     }
31101                 }
31102             }
31103             if(!wasUpdating){
31104                 layout.endUpdate();
31105             }
31106             this.state = state; 
31107         }
31108         this.layout = layout;
31109         layout.on("regionresized", this.onRegionResized, this);
31110         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31111         layout.on("regionexpanded", this.onRegionExpanded, this);
31112     },
31113     
31114     storeState : function(){
31115         this.provider.set(this.layout.id+"-layout-state", this.state);
31116     },
31117     
31118     onRegionResized : function(region, newSize){
31119         this.state[region.getPosition()].size = newSize;
31120         this.storeState();
31121     },
31122     
31123     onRegionCollapsed : function(region){
31124         this.state[region.getPosition()].collapsed = true;
31125         this.storeState();
31126     },
31127     
31128     onRegionExpanded : function(region){
31129         this.state[region.getPosition()].collapsed = false;
31130         this.storeState();
31131     }
31132 };/*
31133  * Based on:
31134  * Ext JS Library 1.1.1
31135  * Copyright(c) 2006-2007, Ext JS, LLC.
31136  *
31137  * Originally Released Under LGPL - original licence link has changed is not relivant.
31138  *
31139  * Fork - LGPL
31140  * <script type="text/javascript">
31141  */
31142 /**
31143  * @class Roo.ContentPanel
31144  * @extends Roo.util.Observable
31145  * A basic ContentPanel element.
31146  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31147  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31148  * @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
31149  * @cfg {Boolean} closable True if the panel can be closed/removed
31150  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
31151  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31152  * @cfg {Toolbar} toolbar A toolbar for this panel
31153  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
31154  * @cfg {String} title The title for this panel
31155  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31156  * @cfg {String} url Calls {@link #setUrl} with this value
31157  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31158  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
31159  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
31160  * @constructor
31161  * Create a new ContentPanel.
31162  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31163  * @param {String/Object} config A string to set only the title or a config object
31164  * @param {String} content (optional) Set the HTML content for this panel
31165  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31166  */
31167 Roo.ContentPanel = function(el, config, content){
31168     
31169      
31170     /*
31171     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31172         config = el;
31173         el = Roo.id();
31174     }
31175     if (config && config.parentLayout) { 
31176         el = config.parentLayout.el.createChild(); 
31177     }
31178     */
31179     if(el.autoCreate){ // xtype is available if this is called from factory
31180         config = el;
31181         el = Roo.id();
31182     }
31183     this.el = Roo.get(el);
31184     if(!this.el && config && config.autoCreate){
31185         if(typeof config.autoCreate == "object"){
31186             if(!config.autoCreate.id){
31187                 config.autoCreate.id = config.id||el;
31188             }
31189             this.el = Roo.DomHelper.append(document.body,
31190                         config.autoCreate, true);
31191         }else{
31192             this.el = Roo.DomHelper.append(document.body,
31193                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31194         }
31195     }
31196     this.closable = false;
31197     this.loaded = false;
31198     this.active = false;
31199     if(typeof config == "string"){
31200         this.title = config;
31201     }else{
31202         Roo.apply(this, config);
31203     }
31204     
31205     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31206         this.wrapEl = this.el.wrap();    
31207         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31208         
31209     }
31210     
31211     
31212     
31213     if(this.resizeEl){
31214         this.resizeEl = Roo.get(this.resizeEl, true);
31215     }else{
31216         this.resizeEl = this.el;
31217     }
31218     this.addEvents({
31219         /**
31220          * @event activate
31221          * Fires when this panel is activated. 
31222          * @param {Roo.ContentPanel} this
31223          */
31224         "activate" : true,
31225         /**
31226          * @event deactivate
31227          * Fires when this panel is activated. 
31228          * @param {Roo.ContentPanel} this
31229          */
31230         "deactivate" : true,
31231
31232         /**
31233          * @event resize
31234          * Fires when this panel is resized if fitToFrame is true.
31235          * @param {Roo.ContentPanel} this
31236          * @param {Number} width The width after any component adjustments
31237          * @param {Number} height The height after any component adjustments
31238          */
31239         "resize" : true
31240     });
31241     if(this.autoScroll){
31242         this.resizeEl.setStyle("overflow", "auto");
31243     } else {
31244         // fix randome scrolling
31245         this.el.on('scroll', function() {
31246             Roo.log('fix random scolling');
31247             this.scrollTo('top',0); 
31248         });
31249     }
31250     content = content || this.content;
31251     if(content){
31252         this.setContent(content);
31253     }
31254     if(config && config.url){
31255         this.setUrl(this.url, this.params, this.loadOnce);
31256     }
31257     
31258     
31259     
31260     Roo.ContentPanel.superclass.constructor.call(this);
31261 };
31262
31263 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31264     tabTip:'',
31265     setRegion : function(region){
31266         this.region = region;
31267         if(region){
31268            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31269         }else{
31270            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31271         } 
31272     },
31273     
31274     /**
31275      * Returns the toolbar for this Panel if one was configured. 
31276      * @return {Roo.Toolbar} 
31277      */
31278     getToolbar : function(){
31279         return this.toolbar;
31280     },
31281     
31282     setActiveState : function(active){
31283         this.active = active;
31284         if(!active){
31285             this.fireEvent("deactivate", this);
31286         }else{
31287             this.fireEvent("activate", this);
31288         }
31289     },
31290     /**
31291      * Updates this panel's element
31292      * @param {String} content The new content
31293      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31294     */
31295     setContent : function(content, loadScripts){
31296         this.el.update(content, loadScripts);
31297     },
31298
31299     ignoreResize : function(w, h){
31300         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31301             return true;
31302         }else{
31303             this.lastSize = {width: w, height: h};
31304             return false;
31305         }
31306     },
31307     /**
31308      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31309      * @return {Roo.UpdateManager} The UpdateManager
31310      */
31311     getUpdateManager : function(){
31312         return this.el.getUpdateManager();
31313     },
31314      /**
31315      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31316      * @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:
31317 <pre><code>
31318 panel.load({
31319     url: "your-url.php",
31320     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31321     callback: yourFunction,
31322     scope: yourObject, //(optional scope)
31323     discardUrl: false,
31324     nocache: false,
31325     text: "Loading...",
31326     timeout: 30,
31327     scripts: false
31328 });
31329 </code></pre>
31330      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31331      * 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.
31332      * @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}
31333      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31334      * @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.
31335      * @return {Roo.ContentPanel} this
31336      */
31337     load : function(){
31338         var um = this.el.getUpdateManager();
31339         um.update.apply(um, arguments);
31340         return this;
31341     },
31342
31343
31344     /**
31345      * 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.
31346      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31347      * @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)
31348      * @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)
31349      * @return {Roo.UpdateManager} The UpdateManager
31350      */
31351     setUrl : function(url, params, loadOnce){
31352         if(this.refreshDelegate){
31353             this.removeListener("activate", this.refreshDelegate);
31354         }
31355         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31356         this.on("activate", this.refreshDelegate);
31357         return this.el.getUpdateManager();
31358     },
31359     
31360     _handleRefresh : function(url, params, loadOnce){
31361         if(!loadOnce || !this.loaded){
31362             var updater = this.el.getUpdateManager();
31363             updater.update(url, params, this._setLoaded.createDelegate(this));
31364         }
31365     },
31366     
31367     _setLoaded : function(){
31368         this.loaded = true;
31369     }, 
31370     
31371     /**
31372      * Returns this panel's id
31373      * @return {String} 
31374      */
31375     getId : function(){
31376         return this.el.id;
31377     },
31378     
31379     /** 
31380      * Returns this panel's element - used by regiosn to add.
31381      * @return {Roo.Element} 
31382      */
31383     getEl : function(){
31384         return this.wrapEl || this.el;
31385     },
31386     
31387     adjustForComponents : function(width, height){
31388         if(this.resizeEl != this.el){
31389             width -= this.el.getFrameWidth('lr');
31390             height -= this.el.getFrameWidth('tb');
31391         }
31392         if(this.toolbar){
31393             var te = this.toolbar.getEl();
31394             height -= te.getHeight();
31395             te.setWidth(width);
31396         }
31397         if(this.adjustments){
31398             width += this.adjustments[0];
31399             height += this.adjustments[1];
31400         }
31401         return {"width": width, "height": height};
31402     },
31403     
31404     setSize : function(width, height){
31405         if(this.fitToFrame && !this.ignoreResize(width, height)){
31406             if(this.fitContainer && this.resizeEl != this.el){
31407                 this.el.setSize(width, height);
31408             }
31409             var size = this.adjustForComponents(width, height);
31410             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31411             this.fireEvent('resize', this, size.width, size.height);
31412         }
31413     },
31414     
31415     /**
31416      * Returns this panel's title
31417      * @return {String} 
31418      */
31419     getTitle : function(){
31420         return this.title;
31421     },
31422     
31423     /**
31424      * Set this panel's title
31425      * @param {String} title
31426      */
31427     setTitle : function(title){
31428         this.title = title;
31429         if(this.region){
31430             this.region.updatePanelTitle(this, title);
31431         }
31432     },
31433     
31434     /**
31435      * Returns true is this panel was configured to be closable
31436      * @return {Boolean} 
31437      */
31438     isClosable : function(){
31439         return this.closable;
31440     },
31441     
31442     beforeSlide : function(){
31443         this.el.clip();
31444         this.resizeEl.clip();
31445     },
31446     
31447     afterSlide : function(){
31448         this.el.unclip();
31449         this.resizeEl.unclip();
31450     },
31451     
31452     /**
31453      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31454      *   Will fail silently if the {@link #setUrl} method has not been called.
31455      *   This does not activate the panel, just updates its content.
31456      */
31457     refresh : function(){
31458         if(this.refreshDelegate){
31459            this.loaded = false;
31460            this.refreshDelegate();
31461         }
31462     },
31463     
31464     /**
31465      * Destroys this panel
31466      */
31467     destroy : function(){
31468         this.el.removeAllListeners();
31469         var tempEl = document.createElement("span");
31470         tempEl.appendChild(this.el.dom);
31471         tempEl.innerHTML = "";
31472         this.el.remove();
31473         this.el = null;
31474     },
31475     
31476       /**
31477      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31478      * <pre><code>
31479
31480 layout.addxtype({
31481        xtype : 'Form',
31482        items: [ .... ]
31483    }
31484 );
31485
31486 </code></pre>
31487      * @param {Object} cfg Xtype definition of item to add.
31488      */
31489     
31490     addxtype : function(cfg) {
31491         // add form..
31492         if (cfg.xtype.match(/^Form$/)) {
31493             var el = this.el.createChild();
31494
31495             this.form = new  Roo.form.Form(cfg);
31496             
31497             
31498             if ( this.form.allItems.length) this.form.render(el.dom);
31499             return this.form;
31500         }
31501         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31502             // views..
31503             cfg.el = this.el.appendChild(document.createElement("div"));
31504             // factory?
31505             var ret = new Roo[cfg.xtype](cfg);
31506             ret.render(false, ''); // render blank..
31507             return ret;
31508             
31509         }
31510         return false;
31511         
31512     }
31513 });
31514
31515 /**
31516  * @class Roo.GridPanel
31517  * @extends Roo.ContentPanel
31518  * @constructor
31519  * Create a new GridPanel.
31520  * @param {Roo.grid.Grid} grid The grid for this panel
31521  * @param {String/Object} config A string to set only the panel's title, or a config object
31522  */
31523 Roo.GridPanel = function(grid, config){
31524     
31525   
31526     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31527         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31528         
31529     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31530     
31531     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31532     
31533     if(this.toolbar){
31534         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31535     }
31536     // xtype created footer. - not sure if will work as we normally have to render first..
31537     if (this.footer && !this.footer.el && this.footer.xtype) {
31538         
31539         this.footer.container = this.grid.getView().getFooterPanel(true);
31540         this.footer.dataSource = this.grid.dataSource;
31541         this.footer = Roo.factory(this.footer, Roo);
31542         
31543     }
31544     
31545     grid.monitorWindowResize = false; // turn off autosizing
31546     grid.autoHeight = false;
31547     grid.autoWidth = false;
31548     this.grid = grid;
31549     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31550 };
31551
31552 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31553     getId : function(){
31554         return this.grid.id;
31555     },
31556     
31557     /**
31558      * Returns the grid for this panel
31559      * @return {Roo.grid.Grid} 
31560      */
31561     getGrid : function(){
31562         return this.grid;    
31563     },
31564     
31565     setSize : function(width, height){
31566         if(!this.ignoreResize(width, height)){
31567             var grid = this.grid;
31568             var size = this.adjustForComponents(width, height);
31569             grid.getGridEl().setSize(size.width, size.height);
31570             grid.autoSize();
31571         }
31572     },
31573     
31574     beforeSlide : function(){
31575         this.grid.getView().scroller.clip();
31576     },
31577     
31578     afterSlide : function(){
31579         this.grid.getView().scroller.unclip();
31580     },
31581     
31582     destroy : function(){
31583         this.grid.destroy();
31584         delete this.grid;
31585         Roo.GridPanel.superclass.destroy.call(this); 
31586     }
31587 });
31588
31589
31590 /**
31591  * @class Roo.NestedLayoutPanel
31592  * @extends Roo.ContentPanel
31593  * @constructor
31594  * Create a new NestedLayoutPanel.
31595  * 
31596  * 
31597  * @param {Roo.BorderLayout} layout The layout for this panel
31598  * @param {String/Object} config A string to set only the title or a config object
31599  */
31600 Roo.NestedLayoutPanel = function(layout, config)
31601 {
31602     // construct with only one argument..
31603     /* FIXME - implement nicer consturctors
31604     if (layout.layout) {
31605         config = layout;
31606         layout = config.layout;
31607         delete config.layout;
31608     }
31609     if (layout.xtype && !layout.getEl) {
31610         // then layout needs constructing..
31611         layout = Roo.factory(layout, Roo);
31612     }
31613     */
31614     
31615     
31616     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31617     
31618     layout.monitorWindowResize = false; // turn off autosizing
31619     this.layout = layout;
31620     this.layout.getEl().addClass("x-layout-nested-layout");
31621     
31622     
31623     
31624     
31625 };
31626
31627 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31628
31629     setSize : function(width, height){
31630         if(!this.ignoreResize(width, height)){
31631             var size = this.adjustForComponents(width, height);
31632             var el = this.layout.getEl();
31633             el.setSize(size.width, size.height);
31634             var touch = el.dom.offsetWidth;
31635             this.layout.layout();
31636             // ie requires a double layout on the first pass
31637             if(Roo.isIE && !this.initialized){
31638                 this.initialized = true;
31639                 this.layout.layout();
31640             }
31641         }
31642     },
31643     
31644     // activate all subpanels if not currently active..
31645     
31646     setActiveState : function(active){
31647         this.active = active;
31648         if(!active){
31649             this.fireEvent("deactivate", this);
31650             return;
31651         }
31652         
31653         this.fireEvent("activate", this);
31654         // not sure if this should happen before or after..
31655         if (!this.layout) {
31656             return; // should not happen..
31657         }
31658         var reg = false;
31659         for (var r in this.layout.regions) {
31660             reg = this.layout.getRegion(r);
31661             if (reg.getActivePanel()) {
31662                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31663                 reg.setActivePanel(reg.getActivePanel());
31664                 continue;
31665             }
31666             if (!reg.panels.length) {
31667                 continue;
31668             }
31669             reg.showPanel(reg.getPanel(0));
31670         }
31671         
31672         
31673         
31674         
31675     },
31676     
31677     /**
31678      * Returns the nested BorderLayout for this panel
31679      * @return {Roo.BorderLayout} 
31680      */
31681     getLayout : function(){
31682         return this.layout;
31683     },
31684     
31685      /**
31686      * Adds a xtype elements to the layout of the nested panel
31687      * <pre><code>
31688
31689 panel.addxtype({
31690        xtype : 'ContentPanel',
31691        region: 'west',
31692        items: [ .... ]
31693    }
31694 );
31695
31696 panel.addxtype({
31697         xtype : 'NestedLayoutPanel',
31698         region: 'west',
31699         layout: {
31700            center: { },
31701            west: { }   
31702         },
31703         items : [ ... list of content panels or nested layout panels.. ]
31704    }
31705 );
31706 </code></pre>
31707      * @param {Object} cfg Xtype definition of item to add.
31708      */
31709     addxtype : function(cfg) {
31710         return this.layout.addxtype(cfg);
31711     
31712     }
31713 });
31714
31715 Roo.ScrollPanel = function(el, config, content){
31716     config = config || {};
31717     config.fitToFrame = true;
31718     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31719     
31720     this.el.dom.style.overflow = "hidden";
31721     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31722     this.el.removeClass("x-layout-inactive-content");
31723     this.el.on("mousewheel", this.onWheel, this);
31724
31725     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31726     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31727     up.unselectable(); down.unselectable();
31728     up.on("click", this.scrollUp, this);
31729     down.on("click", this.scrollDown, this);
31730     up.addClassOnOver("x-scroller-btn-over");
31731     down.addClassOnOver("x-scroller-btn-over");
31732     up.addClassOnClick("x-scroller-btn-click");
31733     down.addClassOnClick("x-scroller-btn-click");
31734     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31735
31736     this.resizeEl = this.el;
31737     this.el = wrap; this.up = up; this.down = down;
31738 };
31739
31740 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31741     increment : 100,
31742     wheelIncrement : 5,
31743     scrollUp : function(){
31744         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31745     },
31746
31747     scrollDown : function(){
31748         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31749     },
31750
31751     afterScroll : function(){
31752         var el = this.resizeEl;
31753         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31754         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31755         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31756     },
31757
31758     setSize : function(){
31759         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31760         this.afterScroll();
31761     },
31762
31763     onWheel : function(e){
31764         var d = e.getWheelDelta();
31765         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31766         this.afterScroll();
31767         e.stopEvent();
31768     },
31769
31770     setContent : function(content, loadScripts){
31771         this.resizeEl.update(content, loadScripts);
31772     }
31773
31774 });
31775
31776
31777
31778
31779
31780
31781
31782
31783
31784 /**
31785  * @class Roo.TreePanel
31786  * @extends Roo.ContentPanel
31787  * @constructor
31788  * Create a new TreePanel. - defaults to fit/scoll contents.
31789  * @param {String/Object} config A string to set only the panel's title, or a config object
31790  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31791  */
31792 Roo.TreePanel = function(config){
31793     var el = config.el;
31794     var tree = config.tree;
31795     delete config.tree; 
31796     delete config.el; // hopefull!
31797     
31798     // wrapper for IE7 strict & safari scroll issue
31799     
31800     var treeEl = el.createChild();
31801     config.resizeEl = treeEl;
31802     
31803     
31804     
31805     Roo.TreePanel.superclass.constructor.call(this, el, config);
31806  
31807  
31808     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31809     //console.log(tree);
31810     this.on('activate', function()
31811     {
31812         if (this.tree.rendered) {
31813             return;
31814         }
31815         //console.log('render tree');
31816         this.tree.render();
31817     });
31818     
31819     this.on('resize',  function (cp, w, h) {
31820             this.tree.innerCt.setWidth(w);
31821             this.tree.innerCt.setHeight(h);
31822             this.tree.innerCt.setStyle('overflow-y', 'auto');
31823     });
31824
31825         
31826     
31827 };
31828
31829 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31830     fitToFrame : true,
31831     autoScroll : true
31832 });
31833
31834
31835
31836
31837
31838
31839
31840
31841
31842
31843
31844 /*
31845  * Based on:
31846  * Ext JS Library 1.1.1
31847  * Copyright(c) 2006-2007, Ext JS, LLC.
31848  *
31849  * Originally Released Under LGPL - original licence link has changed is not relivant.
31850  *
31851  * Fork - LGPL
31852  * <script type="text/javascript">
31853  */
31854  
31855
31856 /**
31857  * @class Roo.ReaderLayout
31858  * @extends Roo.BorderLayout
31859  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31860  * center region containing two nested regions (a top one for a list view and one for item preview below),
31861  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31862  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31863  * expedites the setup of the overall layout and regions for this common application style.
31864  * Example:
31865  <pre><code>
31866 var reader = new Roo.ReaderLayout();
31867 var CP = Roo.ContentPanel;  // shortcut for adding
31868
31869 reader.beginUpdate();
31870 reader.add("north", new CP("north", "North"));
31871 reader.add("west", new CP("west", {title: "West"}));
31872 reader.add("east", new CP("east", {title: "East"}));
31873
31874 reader.regions.listView.add(new CP("listView", "List"));
31875 reader.regions.preview.add(new CP("preview", "Preview"));
31876 reader.endUpdate();
31877 </code></pre>
31878 * @constructor
31879 * Create a new ReaderLayout
31880 * @param {Object} config Configuration options
31881 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31882 * document.body if omitted)
31883 */
31884 Roo.ReaderLayout = function(config, renderTo){
31885     var c = config || {size:{}};
31886     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31887         north: c.north !== false ? Roo.apply({
31888             split:false,
31889             initialSize: 32,
31890             titlebar: false
31891         }, c.north) : false,
31892         west: c.west !== false ? Roo.apply({
31893             split:true,
31894             initialSize: 200,
31895             minSize: 175,
31896             maxSize: 400,
31897             titlebar: true,
31898             collapsible: true,
31899             animate: true,
31900             margins:{left:5,right:0,bottom:5,top:5},
31901             cmargins:{left:5,right:5,bottom:5,top:5}
31902         }, c.west) : false,
31903         east: c.east !== false ? Roo.apply({
31904             split:true,
31905             initialSize: 200,
31906             minSize: 175,
31907             maxSize: 400,
31908             titlebar: true,
31909             collapsible: true,
31910             animate: true,
31911             margins:{left:0,right:5,bottom:5,top:5},
31912             cmargins:{left:5,right:5,bottom:5,top:5}
31913         }, c.east) : false,
31914         center: Roo.apply({
31915             tabPosition: 'top',
31916             autoScroll:false,
31917             closeOnTab: true,
31918             titlebar:false,
31919             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31920         }, c.center)
31921     });
31922
31923     this.el.addClass('x-reader');
31924
31925     this.beginUpdate();
31926
31927     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31928         south: c.preview !== false ? Roo.apply({
31929             split:true,
31930             initialSize: 200,
31931             minSize: 100,
31932             autoScroll:true,
31933             collapsible:true,
31934             titlebar: true,
31935             cmargins:{top:5,left:0, right:0, bottom:0}
31936         }, c.preview) : false,
31937         center: Roo.apply({
31938             autoScroll:false,
31939             titlebar:false,
31940             minHeight:200
31941         }, c.listView)
31942     });
31943     this.add('center', new Roo.NestedLayoutPanel(inner,
31944             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31945
31946     this.endUpdate();
31947
31948     this.regions.preview = inner.getRegion('south');
31949     this.regions.listView = inner.getRegion('center');
31950 };
31951
31952 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31953  * Based on:
31954  * Ext JS Library 1.1.1
31955  * Copyright(c) 2006-2007, Ext JS, LLC.
31956  *
31957  * Originally Released Under LGPL - original licence link has changed is not relivant.
31958  *
31959  * Fork - LGPL
31960  * <script type="text/javascript">
31961  */
31962  
31963 /**
31964  * @class Roo.grid.Grid
31965  * @extends Roo.util.Observable
31966  * This class represents the primary interface of a component based grid control.
31967  * <br><br>Usage:<pre><code>
31968  var grid = new Roo.grid.Grid("my-container-id", {
31969      ds: myDataStore,
31970      cm: myColModel,
31971      selModel: mySelectionModel,
31972      autoSizeColumns: true,
31973      monitorWindowResize: false,
31974      trackMouseOver: true
31975  });
31976  // set any options
31977  grid.render();
31978  * </code></pre>
31979  * <b>Common Problems:</b><br/>
31980  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31981  * element will correct this<br/>
31982  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31983  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31984  * are unpredictable.<br/>
31985  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31986  * grid to calculate dimensions/offsets.<br/>
31987   * @constructor
31988  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31989  * The container MUST have some type of size defined for the grid to fill. The container will be
31990  * automatically set to position relative if it isn't already.
31991  * @param {Object} config A config object that sets properties on this grid.
31992  */
31993 Roo.grid.Grid = function(container, config){
31994         // initialize the container
31995         this.container = Roo.get(container);
31996         this.container.update("");
31997         this.container.setStyle("overflow", "hidden");
31998     this.container.addClass('x-grid-container');
31999
32000     this.id = this.container.id;
32001
32002     Roo.apply(this, config);
32003     // check and correct shorthanded configs
32004     if(this.ds){
32005         this.dataSource = this.ds;
32006         delete this.ds;
32007     }
32008     if(this.cm){
32009         this.colModel = this.cm;
32010         delete this.cm;
32011     }
32012     if(this.sm){
32013         this.selModel = this.sm;
32014         delete this.sm;
32015     }
32016
32017     if (this.selModel) {
32018         this.selModel = Roo.factory(this.selModel, Roo.grid);
32019         this.sm = this.selModel;
32020         this.sm.xmodule = this.xmodule || false;
32021     }
32022     if (typeof(this.colModel.config) == 'undefined') {
32023         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32024         this.cm = this.colModel;
32025         this.cm.xmodule = this.xmodule || false;
32026     }
32027     if (this.dataSource) {
32028         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32029         this.ds = this.dataSource;
32030         this.ds.xmodule = this.xmodule || false;
32031          
32032     }
32033     
32034     
32035     
32036     if(this.width){
32037         this.container.setWidth(this.width);
32038     }
32039
32040     if(this.height){
32041         this.container.setHeight(this.height);
32042     }
32043     /** @private */
32044         this.addEvents({
32045         // raw events
32046         /**
32047          * @event click
32048          * The raw click event for the entire grid.
32049          * @param {Roo.EventObject} e
32050          */
32051         "click" : true,
32052         /**
32053          * @event dblclick
32054          * The raw dblclick event for the entire grid.
32055          * @param {Roo.EventObject} e
32056          */
32057         "dblclick" : true,
32058         /**
32059          * @event contextmenu
32060          * The raw contextmenu event for the entire grid.
32061          * @param {Roo.EventObject} e
32062          */
32063         "contextmenu" : true,
32064         /**
32065          * @event mousedown
32066          * The raw mousedown event for the entire grid.
32067          * @param {Roo.EventObject} e
32068          */
32069         "mousedown" : true,
32070         /**
32071          * @event mouseup
32072          * The raw mouseup event for the entire grid.
32073          * @param {Roo.EventObject} e
32074          */
32075         "mouseup" : true,
32076         /**
32077          * @event mouseover
32078          * The raw mouseover event for the entire grid.
32079          * @param {Roo.EventObject} e
32080          */
32081         "mouseover" : true,
32082         /**
32083          * @event mouseout
32084          * The raw mouseout event for the entire grid.
32085          * @param {Roo.EventObject} e
32086          */
32087         "mouseout" : true,
32088         /**
32089          * @event keypress
32090          * The raw keypress event for the entire grid.
32091          * @param {Roo.EventObject} e
32092          */
32093         "keypress" : true,
32094         /**
32095          * @event keydown
32096          * The raw keydown event for the entire grid.
32097          * @param {Roo.EventObject} e
32098          */
32099         "keydown" : true,
32100
32101         // custom events
32102
32103         /**
32104          * @event cellclick
32105          * Fires when a cell is clicked
32106          * @param {Grid} this
32107          * @param {Number} rowIndex
32108          * @param {Number} columnIndex
32109          * @param {Roo.EventObject} e
32110          */
32111         "cellclick" : true,
32112         /**
32113          * @event celldblclick
32114          * Fires when a cell is double clicked
32115          * @param {Grid} this
32116          * @param {Number} rowIndex
32117          * @param {Number} columnIndex
32118          * @param {Roo.EventObject} e
32119          */
32120         "celldblclick" : true,
32121         /**
32122          * @event rowclick
32123          * Fires when a row is clicked
32124          * @param {Grid} this
32125          * @param {Number} rowIndex
32126          * @param {Roo.EventObject} e
32127          */
32128         "rowclick" : true,
32129         /**
32130          * @event rowdblclick
32131          * Fires when a row is double clicked
32132          * @param {Grid} this
32133          * @param {Number} rowIndex
32134          * @param {Roo.EventObject} e
32135          */
32136         "rowdblclick" : true,
32137         /**
32138          * @event headerclick
32139          * Fires when a header is clicked
32140          * @param {Grid} this
32141          * @param {Number} columnIndex
32142          * @param {Roo.EventObject} e
32143          */
32144         "headerclick" : true,
32145         /**
32146          * @event headerdblclick
32147          * Fires when a header cell is double clicked
32148          * @param {Grid} this
32149          * @param {Number} columnIndex
32150          * @param {Roo.EventObject} e
32151          */
32152         "headerdblclick" : true,
32153         /**
32154          * @event rowcontextmenu
32155          * Fires when a row is right clicked
32156          * @param {Grid} this
32157          * @param {Number} rowIndex
32158          * @param {Roo.EventObject} e
32159          */
32160         "rowcontextmenu" : true,
32161         /**
32162          * @event cellcontextmenu
32163          * Fires when a cell is right clicked
32164          * @param {Grid} this
32165          * @param {Number} rowIndex
32166          * @param {Number} cellIndex
32167          * @param {Roo.EventObject} e
32168          */
32169          "cellcontextmenu" : true,
32170         /**
32171          * @event headercontextmenu
32172          * Fires when a header is right clicked
32173          * @param {Grid} this
32174          * @param {Number} columnIndex
32175          * @param {Roo.EventObject} e
32176          */
32177         "headercontextmenu" : true,
32178         /**
32179          * @event bodyscroll
32180          * Fires when the body element is scrolled
32181          * @param {Number} scrollLeft
32182          * @param {Number} scrollTop
32183          */
32184         "bodyscroll" : true,
32185         /**
32186          * @event columnresize
32187          * Fires when the user resizes a column
32188          * @param {Number} columnIndex
32189          * @param {Number} newSize
32190          */
32191         "columnresize" : true,
32192         /**
32193          * @event columnmove
32194          * Fires when the user moves a column
32195          * @param {Number} oldIndex
32196          * @param {Number} newIndex
32197          */
32198         "columnmove" : true,
32199         /**
32200          * @event startdrag
32201          * Fires when row(s) start being dragged
32202          * @param {Grid} this
32203          * @param {Roo.GridDD} dd The drag drop object
32204          * @param {event} e The raw browser event
32205          */
32206         "startdrag" : true,
32207         /**
32208          * @event enddrag
32209          * Fires when a drag operation is complete
32210          * @param {Grid} this
32211          * @param {Roo.GridDD} dd The drag drop object
32212          * @param {event} e The raw browser event
32213          */
32214         "enddrag" : true,
32215         /**
32216          * @event dragdrop
32217          * Fires when dragged row(s) are dropped on a valid DD target
32218          * @param {Grid} this
32219          * @param {Roo.GridDD} dd The drag drop object
32220          * @param {String} targetId The target drag drop object
32221          * @param {event} e The raw browser event
32222          */
32223         "dragdrop" : true,
32224         /**
32225          * @event dragover
32226          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32227          * @param {Grid} this
32228          * @param {Roo.GridDD} dd The drag drop object
32229          * @param {String} targetId The target drag drop object
32230          * @param {event} e The raw browser event
32231          */
32232         "dragover" : true,
32233         /**
32234          * @event dragenter
32235          *  Fires when the dragged row(s) first cross another DD target while being dragged
32236          * @param {Grid} this
32237          * @param {Roo.GridDD} dd The drag drop object
32238          * @param {String} targetId The target drag drop object
32239          * @param {event} e The raw browser event
32240          */
32241         "dragenter" : true,
32242         /**
32243          * @event dragout
32244          * Fires when the dragged row(s) leave another DD target while being dragged
32245          * @param {Grid} this
32246          * @param {Roo.GridDD} dd The drag drop object
32247          * @param {String} targetId The target drag drop object
32248          * @param {event} e The raw browser event
32249          */
32250         "dragout" : true,
32251         /**
32252          * @event rowclass
32253          * Fires when a row is rendered, so you can change add a style to it.
32254          * @param {GridView} gridview   The grid view
32255          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32256          */
32257         'rowclass' : true,
32258
32259         /**
32260          * @event render
32261          * Fires when the grid is rendered
32262          * @param {Grid} grid
32263          */
32264         'render' : true
32265     });
32266
32267     Roo.grid.Grid.superclass.constructor.call(this);
32268 };
32269 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32270     
32271     /**
32272      * @cfg {String} ddGroup - drag drop group.
32273      */
32274
32275     /**
32276      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32277      */
32278     minColumnWidth : 25,
32279
32280     /**
32281      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32282      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32283      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32284      */
32285     autoSizeColumns : false,
32286
32287     /**
32288      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32289      */
32290     autoSizeHeaders : true,
32291
32292     /**
32293      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32294      */
32295     monitorWindowResize : true,
32296
32297     /**
32298      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32299      * rows measured to get a columns size. Default is 0 (all rows).
32300      */
32301     maxRowsToMeasure : 0,
32302
32303     /**
32304      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32305      */
32306     trackMouseOver : true,
32307
32308     /**
32309     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32310     */
32311     
32312     /**
32313     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32314     */
32315     enableDragDrop : false,
32316     
32317     /**
32318     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32319     */
32320     enableColumnMove : true,
32321     
32322     /**
32323     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32324     */
32325     enableColumnHide : true,
32326     
32327     /**
32328     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32329     */
32330     enableRowHeightSync : false,
32331     
32332     /**
32333     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32334     */
32335     stripeRows : true,
32336     
32337     /**
32338     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32339     */
32340     autoHeight : false,
32341
32342     /**
32343      * @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.
32344      */
32345     autoExpandColumn : false,
32346
32347     /**
32348     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32349     * Default is 50.
32350     */
32351     autoExpandMin : 50,
32352
32353     /**
32354     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32355     */
32356     autoExpandMax : 1000,
32357
32358     /**
32359     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32360     */
32361     view : null,
32362
32363     /**
32364     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32365     */
32366     loadMask : false,
32367     /**
32368     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32369     */
32370     dropTarget: false,
32371     
32372    
32373     
32374     // private
32375     rendered : false,
32376
32377     /**
32378     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32379     * of a fixed width. Default is false.
32380     */
32381     /**
32382     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32383     */
32384     /**
32385      * Called once after all setup has been completed and the grid is ready to be rendered.
32386      * @return {Roo.grid.Grid} this
32387      */
32388     render : function()
32389     {
32390         var c = this.container;
32391         // try to detect autoHeight/width mode
32392         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32393             this.autoHeight = true;
32394         }
32395         var view = this.getView();
32396         view.init(this);
32397
32398         c.on("click", this.onClick, this);
32399         c.on("dblclick", this.onDblClick, this);
32400         c.on("contextmenu", this.onContextMenu, this);
32401         c.on("keydown", this.onKeyDown, this);
32402
32403         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32404
32405         this.getSelectionModel().init(this);
32406
32407         view.render();
32408
32409         if(this.loadMask){
32410             this.loadMask = new Roo.LoadMask(this.container,
32411                     Roo.apply({store:this.dataSource}, this.loadMask));
32412         }
32413         
32414         
32415         if (this.toolbar && this.toolbar.xtype) {
32416             this.toolbar.container = this.getView().getHeaderPanel(true);
32417             this.toolbar = new Roo.Toolbar(this.toolbar);
32418         }
32419         if (this.footer && this.footer.xtype) {
32420             this.footer.dataSource = this.getDataSource();
32421             this.footer.container = this.getView().getFooterPanel(true);
32422             this.footer = Roo.factory(this.footer, Roo);
32423         }
32424         if (this.dropTarget && this.dropTarget.xtype) {
32425             delete this.dropTarget.xtype;
32426             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32427         }
32428         
32429         
32430         this.rendered = true;
32431         this.fireEvent('render', this);
32432         return this;
32433     },
32434
32435         /**
32436          * Reconfigures the grid to use a different Store and Column Model.
32437          * The View will be bound to the new objects and refreshed.
32438          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32439          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32440          */
32441     reconfigure : function(dataSource, colModel){
32442         if(this.loadMask){
32443             this.loadMask.destroy();
32444             this.loadMask = new Roo.LoadMask(this.container,
32445                     Roo.apply({store:dataSource}, this.loadMask));
32446         }
32447         this.view.bind(dataSource, colModel);
32448         this.dataSource = dataSource;
32449         this.colModel = colModel;
32450         this.view.refresh(true);
32451     },
32452
32453     // private
32454     onKeyDown : function(e){
32455         this.fireEvent("keydown", e);
32456     },
32457
32458     /**
32459      * Destroy this grid.
32460      * @param {Boolean} removeEl True to remove the element
32461      */
32462     destroy : function(removeEl, keepListeners){
32463         if(this.loadMask){
32464             this.loadMask.destroy();
32465         }
32466         var c = this.container;
32467         c.removeAllListeners();
32468         this.view.destroy();
32469         this.colModel.purgeListeners();
32470         if(!keepListeners){
32471             this.purgeListeners();
32472         }
32473         c.update("");
32474         if(removeEl === true){
32475             c.remove();
32476         }
32477     },
32478
32479     // private
32480     processEvent : function(name, e){
32481         this.fireEvent(name, e);
32482         var t = e.getTarget();
32483         var v = this.view;
32484         var header = v.findHeaderIndex(t);
32485         if(header !== false){
32486             this.fireEvent("header" + name, this, header, e);
32487         }else{
32488             var row = v.findRowIndex(t);
32489             var cell = v.findCellIndex(t);
32490             if(row !== false){
32491                 this.fireEvent("row" + name, this, row, e);
32492                 if(cell !== false){
32493                     this.fireEvent("cell" + name, this, row, cell, e);
32494                 }
32495             }
32496         }
32497     },
32498
32499     // private
32500     onClick : function(e){
32501         this.processEvent("click", e);
32502     },
32503
32504     // private
32505     onContextMenu : function(e, t){
32506         this.processEvent("contextmenu", e);
32507     },
32508
32509     // private
32510     onDblClick : function(e){
32511         this.processEvent("dblclick", e);
32512     },
32513
32514     // private
32515     walkCells : function(row, col, step, fn, scope){
32516         var cm = this.colModel, clen = cm.getColumnCount();
32517         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32518         if(step < 0){
32519             if(col < 0){
32520                 row--;
32521                 first = false;
32522             }
32523             while(row >= 0){
32524                 if(!first){
32525                     col = clen-1;
32526                 }
32527                 first = false;
32528                 while(col >= 0){
32529                     if(fn.call(scope || this, row, col, cm) === true){
32530                         return [row, col];
32531                     }
32532                     col--;
32533                 }
32534                 row--;
32535             }
32536         } else {
32537             if(col >= clen){
32538                 row++;
32539                 first = false;
32540             }
32541             while(row < rlen){
32542                 if(!first){
32543                     col = 0;
32544                 }
32545                 first = false;
32546                 while(col < clen){
32547                     if(fn.call(scope || this, row, col, cm) === true){
32548                         return [row, col];
32549                     }
32550                     col++;
32551                 }
32552                 row++;
32553             }
32554         }
32555         return null;
32556     },
32557
32558     // private
32559     getSelections : function(){
32560         return this.selModel.getSelections();
32561     },
32562
32563     /**
32564      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32565      * but if manual update is required this method will initiate it.
32566      */
32567     autoSize : function(){
32568         if(this.rendered){
32569             this.view.layout();
32570             if(this.view.adjustForScroll){
32571                 this.view.adjustForScroll();
32572             }
32573         }
32574     },
32575
32576     /**
32577      * Returns the grid's underlying element.
32578      * @return {Element} The element
32579      */
32580     getGridEl : function(){
32581         return this.container;
32582     },
32583
32584     // private for compatibility, overridden by editor grid
32585     stopEditing : function(){},
32586
32587     /**
32588      * Returns the grid's SelectionModel.
32589      * @return {SelectionModel}
32590      */
32591     getSelectionModel : function(){
32592         if(!this.selModel){
32593             this.selModel = new Roo.grid.RowSelectionModel();
32594         }
32595         return this.selModel;
32596     },
32597
32598     /**
32599      * Returns the grid's DataSource.
32600      * @return {DataSource}
32601      */
32602     getDataSource : function(){
32603         return this.dataSource;
32604     },
32605
32606     /**
32607      * Returns the grid's ColumnModel.
32608      * @return {ColumnModel}
32609      */
32610     getColumnModel : function(){
32611         return this.colModel;
32612     },
32613
32614     /**
32615      * Returns the grid's GridView object.
32616      * @return {GridView}
32617      */
32618     getView : function(){
32619         if(!this.view){
32620             this.view = new Roo.grid.GridView(this.viewConfig);
32621         }
32622         return this.view;
32623     },
32624     /**
32625      * Called to get grid's drag proxy text, by default returns this.ddText.
32626      * @return {String}
32627      */
32628     getDragDropText : function(){
32629         var count = this.selModel.getCount();
32630         return String.format(this.ddText, count, count == 1 ? '' : 's');
32631     }
32632 });
32633 /**
32634  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32635  * %0 is replaced with the number of selected rows.
32636  * @type String
32637  */
32638 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32639  * Based on:
32640  * Ext JS Library 1.1.1
32641  * Copyright(c) 2006-2007, Ext JS, LLC.
32642  *
32643  * Originally Released Under LGPL - original licence link has changed is not relivant.
32644  *
32645  * Fork - LGPL
32646  * <script type="text/javascript">
32647  */
32648  
32649 Roo.grid.AbstractGridView = function(){
32650         this.grid = null;
32651         
32652         this.events = {
32653             "beforerowremoved" : true,
32654             "beforerowsinserted" : true,
32655             "beforerefresh" : true,
32656             "rowremoved" : true,
32657             "rowsinserted" : true,
32658             "rowupdated" : true,
32659             "refresh" : true
32660         };
32661     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32662 };
32663
32664 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32665     rowClass : "x-grid-row",
32666     cellClass : "x-grid-cell",
32667     tdClass : "x-grid-td",
32668     hdClass : "x-grid-hd",
32669     splitClass : "x-grid-hd-split",
32670     
32671         init: function(grid){
32672         this.grid = grid;
32673                 var cid = this.grid.getGridEl().id;
32674         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32675         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32676         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32677         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32678         },
32679         
32680         getColumnRenderers : function(){
32681         var renderers = [];
32682         var cm = this.grid.colModel;
32683         var colCount = cm.getColumnCount();
32684         for(var i = 0; i < colCount; i++){
32685             renderers[i] = cm.getRenderer(i);
32686         }
32687         return renderers;
32688     },
32689     
32690     getColumnIds : function(){
32691         var ids = [];
32692         var cm = this.grid.colModel;
32693         var colCount = cm.getColumnCount();
32694         for(var i = 0; i < colCount; i++){
32695             ids[i] = cm.getColumnId(i);
32696         }
32697         return ids;
32698     },
32699     
32700     getDataIndexes : function(){
32701         if(!this.indexMap){
32702             this.indexMap = this.buildIndexMap();
32703         }
32704         return this.indexMap.colToData;
32705     },
32706     
32707     getColumnIndexByDataIndex : function(dataIndex){
32708         if(!this.indexMap){
32709             this.indexMap = this.buildIndexMap();
32710         }
32711         return this.indexMap.dataToCol[dataIndex];
32712     },
32713     
32714     /**
32715      * Set a css style for a column dynamically. 
32716      * @param {Number} colIndex The index of the column
32717      * @param {String} name The css property name
32718      * @param {String} value The css value
32719      */
32720     setCSSStyle : function(colIndex, name, value){
32721         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32722         Roo.util.CSS.updateRule(selector, name, value);
32723     },
32724     
32725     generateRules : function(cm){
32726         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32727         Roo.util.CSS.removeStyleSheet(rulesId);
32728         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32729             var cid = cm.getColumnId(i);
32730             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32731                          this.tdSelector, cid, " {\n}\n",
32732                          this.hdSelector, cid, " {\n}\n",
32733                          this.splitSelector, cid, " {\n}\n");
32734         }
32735         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32736     }
32737 });/*
32738  * Based on:
32739  * Ext JS Library 1.1.1
32740  * Copyright(c) 2006-2007, Ext JS, LLC.
32741  *
32742  * Originally Released Under LGPL - original licence link has changed is not relivant.
32743  *
32744  * Fork - LGPL
32745  * <script type="text/javascript">
32746  */
32747
32748 // private
32749 // This is a support class used internally by the Grid components
32750 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32751     this.grid = grid;
32752     this.view = grid.getView();
32753     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32754     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32755     if(hd2){
32756         this.setHandleElId(Roo.id(hd));
32757         this.setOuterHandleElId(Roo.id(hd2));
32758     }
32759     this.scroll = false;
32760 };
32761 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32762     maxDragWidth: 120,
32763     getDragData : function(e){
32764         var t = Roo.lib.Event.getTarget(e);
32765         var h = this.view.findHeaderCell(t);
32766         if(h){
32767             return {ddel: h.firstChild, header:h};
32768         }
32769         return false;
32770     },
32771
32772     onInitDrag : function(e){
32773         this.view.headersDisabled = true;
32774         var clone = this.dragData.ddel.cloneNode(true);
32775         clone.id = Roo.id();
32776         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32777         this.proxy.update(clone);
32778         return true;
32779     },
32780
32781     afterValidDrop : function(){
32782         var v = this.view;
32783         setTimeout(function(){
32784             v.headersDisabled = false;
32785         }, 50);
32786     },
32787
32788     afterInvalidDrop : function(){
32789         var v = this.view;
32790         setTimeout(function(){
32791             v.headersDisabled = false;
32792         }, 50);
32793     }
32794 });
32795 /*
32796  * Based on:
32797  * Ext JS Library 1.1.1
32798  * Copyright(c) 2006-2007, Ext JS, LLC.
32799  *
32800  * Originally Released Under LGPL - original licence link has changed is not relivant.
32801  *
32802  * Fork - LGPL
32803  * <script type="text/javascript">
32804  */
32805 // private
32806 // This is a support class used internally by the Grid components
32807 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32808     this.grid = grid;
32809     this.view = grid.getView();
32810     // split the proxies so they don't interfere with mouse events
32811     this.proxyTop = Roo.DomHelper.append(document.body, {
32812         cls:"col-move-top", html:"&#160;"
32813     }, true);
32814     this.proxyBottom = Roo.DomHelper.append(document.body, {
32815         cls:"col-move-bottom", html:"&#160;"
32816     }, true);
32817     this.proxyTop.hide = this.proxyBottom.hide = function(){
32818         this.setLeftTop(-100,-100);
32819         this.setStyle("visibility", "hidden");
32820     };
32821     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32822     // temporarily disabled
32823     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32824     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32825 };
32826 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32827     proxyOffsets : [-4, -9],
32828     fly: Roo.Element.fly,
32829
32830     getTargetFromEvent : function(e){
32831         var t = Roo.lib.Event.getTarget(e);
32832         var cindex = this.view.findCellIndex(t);
32833         if(cindex !== false){
32834             return this.view.getHeaderCell(cindex);
32835         }
32836         return null;
32837     },
32838
32839     nextVisible : function(h){
32840         var v = this.view, cm = this.grid.colModel;
32841         h = h.nextSibling;
32842         while(h){
32843             if(!cm.isHidden(v.getCellIndex(h))){
32844                 return h;
32845             }
32846             h = h.nextSibling;
32847         }
32848         return null;
32849     },
32850
32851     prevVisible : function(h){
32852         var v = this.view, cm = this.grid.colModel;
32853         h = h.prevSibling;
32854         while(h){
32855             if(!cm.isHidden(v.getCellIndex(h))){
32856                 return h;
32857             }
32858             h = h.prevSibling;
32859         }
32860         return null;
32861     },
32862
32863     positionIndicator : function(h, n, e){
32864         var x = Roo.lib.Event.getPageX(e);
32865         var r = Roo.lib.Dom.getRegion(n.firstChild);
32866         var px, pt, py = r.top + this.proxyOffsets[1];
32867         if((r.right - x) <= (r.right-r.left)/2){
32868             px = r.right+this.view.borderWidth;
32869             pt = "after";
32870         }else{
32871             px = r.left;
32872             pt = "before";
32873         }
32874         var oldIndex = this.view.getCellIndex(h);
32875         var newIndex = this.view.getCellIndex(n);
32876
32877         if(this.grid.colModel.isFixed(newIndex)){
32878             return false;
32879         }
32880
32881         var locked = this.grid.colModel.isLocked(newIndex);
32882
32883         if(pt == "after"){
32884             newIndex++;
32885         }
32886         if(oldIndex < newIndex){
32887             newIndex--;
32888         }
32889         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32890             return false;
32891         }
32892         px +=  this.proxyOffsets[0];
32893         this.proxyTop.setLeftTop(px, py);
32894         this.proxyTop.show();
32895         if(!this.bottomOffset){
32896             this.bottomOffset = this.view.mainHd.getHeight();
32897         }
32898         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32899         this.proxyBottom.show();
32900         return pt;
32901     },
32902
32903     onNodeEnter : function(n, dd, e, data){
32904         if(data.header != n){
32905             this.positionIndicator(data.header, n, e);
32906         }
32907     },
32908
32909     onNodeOver : function(n, dd, e, data){
32910         var result = false;
32911         if(data.header != n){
32912             result = this.positionIndicator(data.header, n, e);
32913         }
32914         if(!result){
32915             this.proxyTop.hide();
32916             this.proxyBottom.hide();
32917         }
32918         return result ? this.dropAllowed : this.dropNotAllowed;
32919     },
32920
32921     onNodeOut : function(n, dd, e, data){
32922         this.proxyTop.hide();
32923         this.proxyBottom.hide();
32924     },
32925
32926     onNodeDrop : function(n, dd, e, data){
32927         var h = data.header;
32928         if(h != n){
32929             var cm = this.grid.colModel;
32930             var x = Roo.lib.Event.getPageX(e);
32931             var r = Roo.lib.Dom.getRegion(n.firstChild);
32932             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32933             var oldIndex = this.view.getCellIndex(h);
32934             var newIndex = this.view.getCellIndex(n);
32935             var locked = cm.isLocked(newIndex);
32936             if(pt == "after"){
32937                 newIndex++;
32938             }
32939             if(oldIndex < newIndex){
32940                 newIndex--;
32941             }
32942             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32943                 return false;
32944             }
32945             cm.setLocked(oldIndex, locked, true);
32946             cm.moveColumn(oldIndex, newIndex);
32947             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32948             return true;
32949         }
32950         return false;
32951     }
32952 });
32953 /*
32954  * Based on:
32955  * Ext JS Library 1.1.1
32956  * Copyright(c) 2006-2007, Ext JS, LLC.
32957  *
32958  * Originally Released Under LGPL - original licence link has changed is not relivant.
32959  *
32960  * Fork - LGPL
32961  * <script type="text/javascript">
32962  */
32963   
32964 /**
32965  * @class Roo.grid.GridView
32966  * @extends Roo.util.Observable
32967  *
32968  * @constructor
32969  * @param {Object} config
32970  */
32971 Roo.grid.GridView = function(config){
32972     Roo.grid.GridView.superclass.constructor.call(this);
32973     this.el = null;
32974
32975     Roo.apply(this, config);
32976 };
32977
32978 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32979
32980     /**
32981      * Override this function to apply custom css classes to rows during rendering
32982      * @param {Record} record The record
32983      * @param {Number} index
32984      * @method getRowClass
32985      */
32986     rowClass : "x-grid-row",
32987
32988     cellClass : "x-grid-col",
32989
32990     tdClass : "x-grid-td",
32991
32992     hdClass : "x-grid-hd",
32993
32994     splitClass : "x-grid-split",
32995
32996     sortClasses : ["sort-asc", "sort-desc"],
32997
32998     enableMoveAnim : false,
32999
33000     hlColor: "C3DAF9",
33001
33002     dh : Roo.DomHelper,
33003
33004     fly : Roo.Element.fly,
33005
33006     css : Roo.util.CSS,
33007
33008     borderWidth: 1,
33009
33010     splitOffset: 3,
33011
33012     scrollIncrement : 22,
33013
33014     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33015
33016     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33017
33018     bind : function(ds, cm){
33019         if(this.ds){
33020             this.ds.un("load", this.onLoad, this);
33021             this.ds.un("datachanged", this.onDataChange, this);
33022             this.ds.un("add", this.onAdd, this);
33023             this.ds.un("remove", this.onRemove, this);
33024             this.ds.un("update", this.onUpdate, this);
33025             this.ds.un("clear", this.onClear, this);
33026         }
33027         if(ds){
33028             ds.on("load", this.onLoad, this);
33029             ds.on("datachanged", this.onDataChange, this);
33030             ds.on("add", this.onAdd, this);
33031             ds.on("remove", this.onRemove, this);
33032             ds.on("update", this.onUpdate, this);
33033             ds.on("clear", this.onClear, this);
33034         }
33035         this.ds = ds;
33036
33037         if(this.cm){
33038             this.cm.un("widthchange", this.onColWidthChange, this);
33039             this.cm.un("headerchange", this.onHeaderChange, this);
33040             this.cm.un("hiddenchange", this.onHiddenChange, this);
33041             this.cm.un("columnmoved", this.onColumnMove, this);
33042             this.cm.un("columnlockchange", this.onColumnLock, this);
33043         }
33044         if(cm){
33045             this.generateRules(cm);
33046             cm.on("widthchange", this.onColWidthChange, this);
33047             cm.on("headerchange", this.onHeaderChange, this);
33048             cm.on("hiddenchange", this.onHiddenChange, this);
33049             cm.on("columnmoved", this.onColumnMove, this);
33050             cm.on("columnlockchange", this.onColumnLock, this);
33051         }
33052         this.cm = cm;
33053     },
33054
33055     init: function(grid){
33056         Roo.grid.GridView.superclass.init.call(this, grid);
33057
33058         this.bind(grid.dataSource, grid.colModel);
33059
33060         grid.on("headerclick", this.handleHeaderClick, this);
33061
33062         if(grid.trackMouseOver){
33063             grid.on("mouseover", this.onRowOver, this);
33064             grid.on("mouseout", this.onRowOut, this);
33065         }
33066         grid.cancelTextSelection = function(){};
33067         this.gridId = grid.id;
33068
33069         var tpls = this.templates || {};
33070
33071         if(!tpls.master){
33072             tpls.master = new Roo.Template(
33073                '<div class="x-grid" hidefocus="true">',
33074                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33075                   '<div class="x-grid-topbar"></div>',
33076                   '<div class="x-grid-scroller"><div></div></div>',
33077                   '<div class="x-grid-locked">',
33078                       '<div class="x-grid-header">{lockedHeader}</div>',
33079                       '<div class="x-grid-body">{lockedBody}</div>',
33080                   "</div>",
33081                   '<div class="x-grid-viewport">',
33082                       '<div class="x-grid-header">{header}</div>',
33083                       '<div class="x-grid-body">{body}</div>',
33084                   "</div>",
33085                   '<div class="x-grid-bottombar"></div>',
33086                  
33087                   '<div class="x-grid-resize-proxy">&#160;</div>',
33088                "</div>"
33089             );
33090             tpls.master.disableformats = true;
33091         }
33092
33093         if(!tpls.header){
33094             tpls.header = new Roo.Template(
33095                '<table border="0" cellspacing="0" cellpadding="0">',
33096                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33097                "</table>{splits}"
33098             );
33099             tpls.header.disableformats = true;
33100         }
33101         tpls.header.compile();
33102
33103         if(!tpls.hcell){
33104             tpls.hcell = new Roo.Template(
33105                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33106                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33107                 "</div></td>"
33108              );
33109              tpls.hcell.disableFormats = true;
33110         }
33111         tpls.hcell.compile();
33112
33113         if(!tpls.hsplit){
33114             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33115             tpls.hsplit.disableFormats = true;
33116         }
33117         tpls.hsplit.compile();
33118
33119         if(!tpls.body){
33120             tpls.body = new Roo.Template(
33121                '<table border="0" cellspacing="0" cellpadding="0">',
33122                "<tbody>{rows}</tbody>",
33123                "</table>"
33124             );
33125             tpls.body.disableFormats = true;
33126         }
33127         tpls.body.compile();
33128
33129         if(!tpls.row){
33130             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33131             tpls.row.disableFormats = true;
33132         }
33133         tpls.row.compile();
33134
33135         if(!tpls.cell){
33136             tpls.cell = new Roo.Template(
33137                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33138                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33139                 "</td>"
33140             );
33141             tpls.cell.disableFormats = true;
33142         }
33143         tpls.cell.compile();
33144
33145         this.templates = tpls;
33146     },
33147
33148     // remap these for backwards compat
33149     onColWidthChange : function(){
33150         this.updateColumns.apply(this, arguments);
33151     },
33152     onHeaderChange : function(){
33153         this.updateHeaders.apply(this, arguments);
33154     }, 
33155     onHiddenChange : function(){
33156         this.handleHiddenChange.apply(this, arguments);
33157     },
33158     onColumnMove : function(){
33159         this.handleColumnMove.apply(this, arguments);
33160     },
33161     onColumnLock : function(){
33162         this.handleLockChange.apply(this, arguments);
33163     },
33164
33165     onDataChange : function(){
33166         this.refresh();
33167         this.updateHeaderSortState();
33168     },
33169
33170     onClear : function(){
33171         this.refresh();
33172     },
33173
33174     onUpdate : function(ds, record){
33175         this.refreshRow(record);
33176     },
33177
33178     refreshRow : function(record){
33179         var ds = this.ds, index;
33180         if(typeof record == 'number'){
33181             index = record;
33182             record = ds.getAt(index);
33183         }else{
33184             index = ds.indexOf(record);
33185         }
33186         this.insertRows(ds, index, index, true);
33187         this.onRemove(ds, record, index+1, true);
33188         this.syncRowHeights(index, index);
33189         this.layout();
33190         this.fireEvent("rowupdated", this, index, record);
33191     },
33192
33193     onAdd : function(ds, records, index){
33194         this.insertRows(ds, index, index + (records.length-1));
33195     },
33196
33197     onRemove : function(ds, record, index, isUpdate){
33198         if(isUpdate !== true){
33199             this.fireEvent("beforerowremoved", this, index, record);
33200         }
33201         var bt = this.getBodyTable(), lt = this.getLockedTable();
33202         if(bt.rows[index]){
33203             bt.firstChild.removeChild(bt.rows[index]);
33204         }
33205         if(lt.rows[index]){
33206             lt.firstChild.removeChild(lt.rows[index]);
33207         }
33208         if(isUpdate !== true){
33209             this.stripeRows(index);
33210             this.syncRowHeights(index, index);
33211             this.layout();
33212             this.fireEvent("rowremoved", this, index, record);
33213         }
33214     },
33215
33216     onLoad : function(){
33217         this.scrollToTop();
33218     },
33219
33220     /**
33221      * Scrolls the grid to the top
33222      */
33223     scrollToTop : function(){
33224         if(this.scroller){
33225             this.scroller.dom.scrollTop = 0;
33226             this.syncScroll();
33227         }
33228     },
33229
33230     /**
33231      * Gets a panel in the header of the grid that can be used for toolbars etc.
33232      * After modifying the contents of this panel a call to grid.autoSize() may be
33233      * required to register any changes in size.
33234      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33235      * @return Roo.Element
33236      */
33237     getHeaderPanel : function(doShow){
33238         if(doShow){
33239             this.headerPanel.show();
33240         }
33241         return this.headerPanel;
33242     },
33243
33244     /**
33245      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33246      * After modifying the contents of this panel a call to grid.autoSize() may be
33247      * required to register any changes in size.
33248      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33249      * @return Roo.Element
33250      */
33251     getFooterPanel : function(doShow){
33252         if(doShow){
33253             this.footerPanel.show();
33254         }
33255         return this.footerPanel;
33256     },
33257
33258     initElements : function(){
33259         var E = Roo.Element;
33260         var el = this.grid.getGridEl().dom.firstChild;
33261         var cs = el.childNodes;
33262
33263         this.el = new E(el);
33264         
33265          this.focusEl = new E(el.firstChild);
33266         this.focusEl.swallowEvent("click", true);
33267         
33268         this.headerPanel = new E(cs[1]);
33269         this.headerPanel.enableDisplayMode("block");
33270
33271         this.scroller = new E(cs[2]);
33272         this.scrollSizer = new E(this.scroller.dom.firstChild);
33273
33274         this.lockedWrap = new E(cs[3]);
33275         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33276         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33277
33278         this.mainWrap = new E(cs[4]);
33279         this.mainHd = new E(this.mainWrap.dom.firstChild);
33280         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33281
33282         this.footerPanel = new E(cs[5]);
33283         this.footerPanel.enableDisplayMode("block");
33284
33285         this.resizeProxy = new E(cs[6]);
33286
33287         this.headerSelector = String.format(
33288            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33289            this.lockedHd.id, this.mainHd.id
33290         );
33291
33292         this.splitterSelector = String.format(
33293            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33294            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33295         );
33296     },
33297     idToCssName : function(s)
33298     {
33299         return s.replace(/[^a-z0-9]+/ig, '-');
33300     },
33301
33302     getHeaderCell : function(index){
33303         return Roo.DomQuery.select(this.headerSelector)[index];
33304     },
33305
33306     getHeaderCellMeasure : function(index){
33307         return this.getHeaderCell(index).firstChild;
33308     },
33309
33310     getHeaderCellText : function(index){
33311         return this.getHeaderCell(index).firstChild.firstChild;
33312     },
33313
33314     getLockedTable : function(){
33315         return this.lockedBody.dom.firstChild;
33316     },
33317
33318     getBodyTable : function(){
33319         return this.mainBody.dom.firstChild;
33320     },
33321
33322     getLockedRow : function(index){
33323         return this.getLockedTable().rows[index];
33324     },
33325
33326     getRow : function(index){
33327         return this.getBodyTable().rows[index];
33328     },
33329
33330     getRowComposite : function(index){
33331         if(!this.rowEl){
33332             this.rowEl = new Roo.CompositeElementLite();
33333         }
33334         var els = [], lrow, mrow;
33335         if(lrow = this.getLockedRow(index)){
33336             els.push(lrow);
33337         }
33338         if(mrow = this.getRow(index)){
33339             els.push(mrow);
33340         }
33341         this.rowEl.elements = els;
33342         return this.rowEl;
33343     },
33344
33345     getCell : function(rowIndex, colIndex){
33346         var locked = this.cm.getLockedCount();
33347         var source;
33348         if(colIndex < locked){
33349             source = this.lockedBody.dom.firstChild;
33350         }else{
33351             source = this.mainBody.dom.firstChild;
33352             colIndex -= locked;
33353         }
33354         return source.rows[rowIndex].childNodes[colIndex];
33355     },
33356
33357     getCellText : function(rowIndex, colIndex){
33358         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33359     },
33360
33361     getCellBox : function(cell){
33362         var b = this.fly(cell).getBox();
33363         if(Roo.isOpera){ // opera fails to report the Y
33364             b.y = cell.offsetTop + this.mainBody.getY();
33365         }
33366         return b;
33367     },
33368
33369     getCellIndex : function(cell){
33370         var id = String(cell.className).match(this.cellRE);
33371         if(id){
33372             return parseInt(id[1], 10);
33373         }
33374         return 0;
33375     },
33376
33377     findHeaderIndex : function(n){
33378         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33379         return r ? this.getCellIndex(r) : false;
33380     },
33381
33382     findHeaderCell : function(n){
33383         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33384         return r ? r : false;
33385     },
33386
33387     findRowIndex : function(n){
33388         if(!n){
33389             return false;
33390         }
33391         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33392         return r ? r.rowIndex : false;
33393     },
33394
33395     findCellIndex : function(node){
33396         var stop = this.el.dom;
33397         while(node && node != stop){
33398             if(this.findRE.test(node.className)){
33399                 return this.getCellIndex(node);
33400             }
33401             node = node.parentNode;
33402         }
33403         return false;
33404     },
33405
33406     getColumnId : function(index){
33407         return this.cm.getColumnId(index);
33408     },
33409
33410     getSplitters : function()
33411     {
33412         if(this.splitterSelector){
33413            return Roo.DomQuery.select(this.splitterSelector);
33414         }else{
33415             return null;
33416       }
33417     },
33418
33419     getSplitter : function(index){
33420         return this.getSplitters()[index];
33421     },
33422
33423     onRowOver : function(e, t){
33424         var row;
33425         if((row = this.findRowIndex(t)) !== false){
33426             this.getRowComposite(row).addClass("x-grid-row-over");
33427         }
33428     },
33429
33430     onRowOut : function(e, t){
33431         var row;
33432         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33433             this.getRowComposite(row).removeClass("x-grid-row-over");
33434         }
33435     },
33436
33437     renderHeaders : function(){
33438         var cm = this.cm;
33439         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33440         var cb = [], lb = [], sb = [], lsb = [], p = {};
33441         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33442             p.cellId = "x-grid-hd-0-" + i;
33443             p.splitId = "x-grid-csplit-0-" + i;
33444             p.id = cm.getColumnId(i);
33445             p.title = cm.getColumnTooltip(i) || "";
33446             p.value = cm.getColumnHeader(i) || "";
33447             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33448             if(!cm.isLocked(i)){
33449                 cb[cb.length] = ct.apply(p);
33450                 sb[sb.length] = st.apply(p);
33451             }else{
33452                 lb[lb.length] = ct.apply(p);
33453                 lsb[lsb.length] = st.apply(p);
33454             }
33455         }
33456         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33457                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33458     },
33459
33460     updateHeaders : function(){
33461         var html = this.renderHeaders();
33462         this.lockedHd.update(html[0]);
33463         this.mainHd.update(html[1]);
33464     },
33465
33466     /**
33467      * Focuses the specified row.
33468      * @param {Number} row The row index
33469      */
33470     focusRow : function(row)
33471     {
33472         //Roo.log('GridView.focusRow');
33473         var x = this.scroller.dom.scrollLeft;
33474         this.focusCell(row, 0, false);
33475         this.scroller.dom.scrollLeft = x;
33476     },
33477
33478     /**
33479      * Focuses the specified cell.
33480      * @param {Number} row The row index
33481      * @param {Number} col The column index
33482      * @param {Boolean} hscroll false to disable horizontal scrolling
33483      */
33484     focusCell : function(row, col, hscroll)
33485     {
33486         //Roo.log('GridView.focusCell');
33487         var el = this.ensureVisible(row, col, hscroll);
33488         this.focusEl.alignTo(el, "tl-tl");
33489         if(Roo.isGecko){
33490             this.focusEl.focus();
33491         }else{
33492             this.focusEl.focus.defer(1, this.focusEl);
33493         }
33494     },
33495
33496     /**
33497      * Scrolls the specified cell into view
33498      * @param {Number} row The row index
33499      * @param {Number} col The column index
33500      * @param {Boolean} hscroll false to disable horizontal scrolling
33501      */
33502     ensureVisible : function(row, col, hscroll)
33503     {
33504         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33505         //return null; //disable for testing.
33506         if(typeof row != "number"){
33507             row = row.rowIndex;
33508         }
33509         if(row < 0 && row >= this.ds.getCount()){
33510             return  null;
33511         }
33512         col = (col !== undefined ? col : 0);
33513         var cm = this.grid.colModel;
33514         while(cm.isHidden(col)){
33515             col++;
33516         }
33517
33518         var el = this.getCell(row, col);
33519         if(!el){
33520             return null;
33521         }
33522         var c = this.scroller.dom;
33523
33524         var ctop = parseInt(el.offsetTop, 10);
33525         var cleft = parseInt(el.offsetLeft, 10);
33526         var cbot = ctop + el.offsetHeight;
33527         var cright = cleft + el.offsetWidth;
33528         
33529         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33530         var stop = parseInt(c.scrollTop, 10);
33531         var sleft = parseInt(c.scrollLeft, 10);
33532         var sbot = stop + ch;
33533         var sright = sleft + c.clientWidth;
33534         /*
33535         Roo.log('GridView.ensureVisible:' +
33536                 ' ctop:' + ctop +
33537                 ' c.clientHeight:' + c.clientHeight +
33538                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33539                 ' stop:' + stop +
33540                 ' cbot:' + cbot +
33541                 ' sbot:' + sbot +
33542                 ' ch:' + ch  
33543                 );
33544         */
33545         if(ctop < stop){
33546              c.scrollTop = ctop;
33547             //Roo.log("set scrolltop to ctop DISABLE?");
33548         }else if(cbot > sbot){
33549             //Roo.log("set scrolltop to cbot-ch");
33550             c.scrollTop = cbot-ch;
33551         }
33552         
33553         if(hscroll !== false){
33554             if(cleft < sleft){
33555                 c.scrollLeft = cleft;
33556             }else if(cright > sright){
33557                 c.scrollLeft = cright-c.clientWidth;
33558             }
33559         }
33560          
33561         return el;
33562     },
33563
33564     updateColumns : function(){
33565         this.grid.stopEditing();
33566         var cm = this.grid.colModel, colIds = this.getColumnIds();
33567         //var totalWidth = cm.getTotalWidth();
33568         var pos = 0;
33569         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33570             //if(cm.isHidden(i)) continue;
33571             var w = cm.getColumnWidth(i);
33572             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33573             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33574         }
33575         this.updateSplitters();
33576     },
33577
33578     generateRules : function(cm){
33579         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33580         Roo.util.CSS.removeStyleSheet(rulesId);
33581         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33582             var cid = cm.getColumnId(i);
33583             var align = '';
33584             if(cm.config[i].align){
33585                 align = 'text-align:'+cm.config[i].align+';';
33586             }
33587             var hidden = '';
33588             if(cm.isHidden(i)){
33589                 hidden = 'display:none;';
33590             }
33591             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33592             ruleBuf.push(
33593                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33594                     this.hdSelector, cid, " {\n", align, width, "}\n",
33595                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33596                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33597         }
33598         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33599     },
33600
33601     updateSplitters : function(){
33602         var cm = this.cm, s = this.getSplitters();
33603         if(s){ // splitters not created yet
33604             var pos = 0, locked = true;
33605             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33606                 if(cm.isHidden(i)) continue;
33607                 var w = cm.getColumnWidth(i); // make sure it's a number
33608                 if(!cm.isLocked(i) && locked){
33609                     pos = 0;
33610                     locked = false;
33611                 }
33612                 pos += w;
33613                 s[i].style.left = (pos-this.splitOffset) + "px";
33614             }
33615         }
33616     },
33617
33618     handleHiddenChange : function(colModel, colIndex, hidden){
33619         if(hidden){
33620             this.hideColumn(colIndex);
33621         }else{
33622             this.unhideColumn(colIndex);
33623         }
33624     },
33625
33626     hideColumn : function(colIndex){
33627         var cid = this.getColumnId(colIndex);
33628         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33629         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33630         if(Roo.isSafari){
33631             this.updateHeaders();
33632         }
33633         this.updateSplitters();
33634         this.layout();
33635     },
33636
33637     unhideColumn : function(colIndex){
33638         var cid = this.getColumnId(colIndex);
33639         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33640         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33641
33642         if(Roo.isSafari){
33643             this.updateHeaders();
33644         }
33645         this.updateSplitters();
33646         this.layout();
33647     },
33648
33649     insertRows : function(dm, firstRow, lastRow, isUpdate){
33650         if(firstRow == 0 && lastRow == dm.getCount()-1){
33651             this.refresh();
33652         }else{
33653             if(!isUpdate){
33654                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33655             }
33656             var s = this.getScrollState();
33657             var markup = this.renderRows(firstRow, lastRow);
33658             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33659             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33660             this.restoreScroll(s);
33661             if(!isUpdate){
33662                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33663                 this.syncRowHeights(firstRow, lastRow);
33664                 this.stripeRows(firstRow);
33665                 this.layout();
33666             }
33667         }
33668     },
33669
33670     bufferRows : function(markup, target, index){
33671         var before = null, trows = target.rows, tbody = target.tBodies[0];
33672         if(index < trows.length){
33673             before = trows[index];
33674         }
33675         var b = document.createElement("div");
33676         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33677         var rows = b.firstChild.rows;
33678         for(var i = 0, len = rows.length; i < len; i++){
33679             if(before){
33680                 tbody.insertBefore(rows[0], before);
33681             }else{
33682                 tbody.appendChild(rows[0]);
33683             }
33684         }
33685         b.innerHTML = "";
33686         b = null;
33687     },
33688
33689     deleteRows : function(dm, firstRow, lastRow){
33690         if(dm.getRowCount()<1){
33691             this.fireEvent("beforerefresh", this);
33692             this.mainBody.update("");
33693             this.lockedBody.update("");
33694             this.fireEvent("refresh", this);
33695         }else{
33696             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33697             var bt = this.getBodyTable();
33698             var tbody = bt.firstChild;
33699             var rows = bt.rows;
33700             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33701                 tbody.removeChild(rows[firstRow]);
33702             }
33703             this.stripeRows(firstRow);
33704             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33705         }
33706     },
33707
33708     updateRows : function(dataSource, firstRow, lastRow){
33709         var s = this.getScrollState();
33710         this.refresh();
33711         this.restoreScroll(s);
33712     },
33713
33714     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33715         if(!noRefresh){
33716            this.refresh();
33717         }
33718         this.updateHeaderSortState();
33719     },
33720
33721     getScrollState : function(){
33722         
33723         var sb = this.scroller.dom;
33724         return {left: sb.scrollLeft, top: sb.scrollTop};
33725     },
33726
33727     stripeRows : function(startRow){
33728         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33729             return;
33730         }
33731         startRow = startRow || 0;
33732         var rows = this.getBodyTable().rows;
33733         var lrows = this.getLockedTable().rows;
33734         var cls = ' x-grid-row-alt ';
33735         for(var i = startRow, len = rows.length; i < len; i++){
33736             var row = rows[i], lrow = lrows[i];
33737             var isAlt = ((i+1) % 2 == 0);
33738             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33739             if(isAlt == hasAlt){
33740                 continue;
33741             }
33742             if(isAlt){
33743                 row.className += " x-grid-row-alt";
33744             }else{
33745                 row.className = row.className.replace("x-grid-row-alt", "");
33746             }
33747             if(lrow){
33748                 lrow.className = row.className;
33749             }
33750         }
33751     },
33752
33753     restoreScroll : function(state){
33754         //Roo.log('GridView.restoreScroll');
33755         var sb = this.scroller.dom;
33756         sb.scrollLeft = state.left;
33757         sb.scrollTop = state.top;
33758         this.syncScroll();
33759     },
33760
33761     syncScroll : function(){
33762         //Roo.log('GridView.syncScroll');
33763         var sb = this.scroller.dom;
33764         var sh = this.mainHd.dom;
33765         var bs = this.mainBody.dom;
33766         var lv = this.lockedBody.dom;
33767         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33768         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33769     },
33770
33771     handleScroll : function(e){
33772         this.syncScroll();
33773         var sb = this.scroller.dom;
33774         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33775         e.stopEvent();
33776     },
33777
33778     handleWheel : function(e){
33779         var d = e.getWheelDelta();
33780         this.scroller.dom.scrollTop -= d*22;
33781         // set this here to prevent jumpy scrolling on large tables
33782         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33783         e.stopEvent();
33784     },
33785
33786     renderRows : function(startRow, endRow){
33787         // pull in all the crap needed to render rows
33788         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33789         var colCount = cm.getColumnCount();
33790
33791         if(ds.getCount() < 1){
33792             return ["", ""];
33793         }
33794
33795         // build a map for all the columns
33796         var cs = [];
33797         for(var i = 0; i < colCount; i++){
33798             var name = cm.getDataIndex(i);
33799             cs[i] = {
33800                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33801                 renderer : cm.getRenderer(i),
33802                 id : cm.getColumnId(i),
33803                 locked : cm.isLocked(i)
33804             };
33805         }
33806
33807         startRow = startRow || 0;
33808         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33809
33810         // records to render
33811         var rs = ds.getRange(startRow, endRow);
33812
33813         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33814     },
33815
33816     // As much as I hate to duplicate code, this was branched because FireFox really hates
33817     // [].join("") on strings. The performance difference was substantial enough to
33818     // branch this function
33819     doRender : Roo.isGecko ?
33820             function(cs, rs, ds, startRow, colCount, stripe){
33821                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33822                 // buffers
33823                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33824                 
33825                 var hasListener = this.grid.hasListener('rowclass');
33826                 var rowcfg = {};
33827                 for(var j = 0, len = rs.length; j < len; j++){
33828                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33829                     for(var i = 0; i < colCount; i++){
33830                         c = cs[i];
33831                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33832                         p.id = c.id;
33833                         p.css = p.attr = "";
33834                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33835                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33836                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33837                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33838                         }
33839                         var markup = ct.apply(p);
33840                         if(!c.locked){
33841                             cb+= markup;
33842                         }else{
33843                             lcb+= markup;
33844                         }
33845                     }
33846                     var alt = [];
33847                     if(stripe && ((rowIndex+1) % 2 == 0)){
33848                         alt.push("x-grid-row-alt")
33849                     }
33850                     if(r.dirty){
33851                         alt.push(  " x-grid-dirty-row");
33852                     }
33853                     rp.cells = lcb;
33854                     if(this.getRowClass){
33855                         alt.push(this.getRowClass(r, rowIndex));
33856                     }
33857                     if (hasListener) {
33858                         rowcfg = {
33859                              
33860                             record: r,
33861                             rowIndex : rowIndex,
33862                             rowClass : ''
33863                         }
33864                         this.grid.fireEvent('rowclass', this, rowcfg);
33865                         alt.push(rowcfg.rowClass);
33866                     }
33867                     rp.alt = alt.join(" ");
33868                     lbuf+= rt.apply(rp);
33869                     rp.cells = cb;
33870                     buf+=  rt.apply(rp);
33871                 }
33872                 return [lbuf, buf];
33873             } :
33874             function(cs, rs, ds, startRow, colCount, stripe){
33875                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33876                 // buffers
33877                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33878                 var hasListener = this.grid.hasListener('rowclass');
33879                 var rowcfg = {};
33880                 for(var j = 0, len = rs.length; j < len; j++){
33881                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33882                     for(var i = 0; i < colCount; i++){
33883                         c = cs[i];
33884                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33885                         p.id = c.id;
33886                         p.css = p.attr = "";
33887                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33888                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33889                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33890                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33891                         }
33892                         var markup = ct.apply(p);
33893                         if(!c.locked){
33894                             cb[cb.length] = markup;
33895                         }else{
33896                             lcb[lcb.length] = markup;
33897                         }
33898                     }
33899                     var alt = [];
33900                     if(stripe && ((rowIndex+1) % 2 == 0)){
33901                         alt.push( "x-grid-row-alt");
33902                     }
33903                     if(r.dirty){
33904                         alt.push(" x-grid-dirty-row");
33905                     }
33906                     rp.cells = lcb;
33907                     if(this.getRowClass){
33908                         alt.push( this.getRowClass(r, rowIndex));
33909                     }
33910                     if (hasListener) {
33911                         rowcfg = {
33912                              
33913                             record: r,
33914                             rowIndex : rowIndex,
33915                             rowClass : ''
33916                         }
33917                         this.grid.fireEvent('rowclass', this, rowcfg);
33918                         alt.push(rowcfg.rowClass);
33919                     }
33920                     rp.alt = alt.join(" ");
33921                     rp.cells = lcb.join("");
33922                     lbuf[lbuf.length] = rt.apply(rp);
33923                     rp.cells = cb.join("");
33924                     buf[buf.length] =  rt.apply(rp);
33925                 }
33926                 return [lbuf.join(""), buf.join("")];
33927             },
33928
33929     renderBody : function(){
33930         var markup = this.renderRows();
33931         var bt = this.templates.body;
33932         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33933     },
33934
33935     /**
33936      * Refreshes the grid
33937      * @param {Boolean} headersToo
33938      */
33939     refresh : function(headersToo){
33940         this.fireEvent("beforerefresh", this);
33941         this.grid.stopEditing();
33942         var result = this.renderBody();
33943         this.lockedBody.update(result[0]);
33944         this.mainBody.update(result[1]);
33945         if(headersToo === true){
33946             this.updateHeaders();
33947             this.updateColumns();
33948             this.updateSplitters();
33949             this.updateHeaderSortState();
33950         }
33951         this.syncRowHeights();
33952         this.layout();
33953         this.fireEvent("refresh", this);
33954     },
33955
33956     handleColumnMove : function(cm, oldIndex, newIndex){
33957         this.indexMap = null;
33958         var s = this.getScrollState();
33959         this.refresh(true);
33960         this.restoreScroll(s);
33961         this.afterMove(newIndex);
33962     },
33963
33964     afterMove : function(colIndex){
33965         if(this.enableMoveAnim && Roo.enableFx){
33966             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33967         }
33968         // if multisort - fix sortOrder, and reload..
33969         if (this.grid.dataSource.multiSort) {
33970             // the we can call sort again..
33971             var dm = this.grid.dataSource;
33972             var cm = this.grid.colModel;
33973             var so = [];
33974             for(var i = 0; i < cm.config.length; i++ ) {
33975                 
33976                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33977                     continue; // dont' bother, it's not in sort list or being set.
33978                 }
33979                 
33980                 so.push(cm.config[i].dataIndex);
33981             };
33982             dm.sortOrder = so;
33983             dm.load(dm.lastOptions);
33984             
33985             
33986         }
33987         
33988     },
33989
33990     updateCell : function(dm, rowIndex, dataIndex){
33991         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33992         if(typeof colIndex == "undefined"){ // not present in grid
33993             return;
33994         }
33995         var cm = this.grid.colModel;
33996         var cell = this.getCell(rowIndex, colIndex);
33997         var cellText = this.getCellText(rowIndex, colIndex);
33998
33999         var p = {
34000             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34001             id : cm.getColumnId(colIndex),
34002             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34003         };
34004         var renderer = cm.getRenderer(colIndex);
34005         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34006         if(typeof val == "undefined" || val === "") val = "&#160;";
34007         cellText.innerHTML = val;
34008         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34009         this.syncRowHeights(rowIndex, rowIndex);
34010     },
34011
34012     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34013         var maxWidth = 0;
34014         if(this.grid.autoSizeHeaders){
34015             var h = this.getHeaderCellMeasure(colIndex);
34016             maxWidth = Math.max(maxWidth, h.scrollWidth);
34017         }
34018         var tb, index;
34019         if(this.cm.isLocked(colIndex)){
34020             tb = this.getLockedTable();
34021             index = colIndex;
34022         }else{
34023             tb = this.getBodyTable();
34024             index = colIndex - this.cm.getLockedCount();
34025         }
34026         if(tb && tb.rows){
34027             var rows = tb.rows;
34028             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34029             for(var i = 0; i < stopIndex; i++){
34030                 var cell = rows[i].childNodes[index].firstChild;
34031                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34032             }
34033         }
34034         return maxWidth + /*margin for error in IE*/ 5;
34035     },
34036     /**
34037      * Autofit a column to its content.
34038      * @param {Number} colIndex
34039      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34040      */
34041      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34042          if(this.cm.isHidden(colIndex)){
34043              return; // can't calc a hidden column
34044          }
34045         if(forceMinSize){
34046             var cid = this.cm.getColumnId(colIndex);
34047             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34048            if(this.grid.autoSizeHeaders){
34049                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34050            }
34051         }
34052         var newWidth = this.calcColumnWidth(colIndex);
34053         this.cm.setColumnWidth(colIndex,
34054             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34055         if(!suppressEvent){
34056             this.grid.fireEvent("columnresize", colIndex, newWidth);
34057         }
34058     },
34059
34060     /**
34061      * Autofits all columns to their content and then expands to fit any extra space in the grid
34062      */
34063      autoSizeColumns : function(){
34064         var cm = this.grid.colModel;
34065         var colCount = cm.getColumnCount();
34066         for(var i = 0; i < colCount; i++){
34067             this.autoSizeColumn(i, true, true);
34068         }
34069         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34070             this.fitColumns();
34071         }else{
34072             this.updateColumns();
34073             this.layout();
34074         }
34075     },
34076
34077     /**
34078      * Autofits all columns to the grid's width proportionate with their current size
34079      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34080      */
34081     fitColumns : function(reserveScrollSpace){
34082         var cm = this.grid.colModel;
34083         var colCount = cm.getColumnCount();
34084         var cols = [];
34085         var width = 0;
34086         var i, w;
34087         for (i = 0; i < colCount; i++){
34088             if(!cm.isHidden(i) && !cm.isFixed(i)){
34089                 w = cm.getColumnWidth(i);
34090                 cols.push(i);
34091                 cols.push(w);
34092                 width += w;
34093             }
34094         }
34095         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34096         if(reserveScrollSpace){
34097             avail -= 17;
34098         }
34099         var frac = (avail - cm.getTotalWidth())/width;
34100         while (cols.length){
34101             w = cols.pop();
34102             i = cols.pop();
34103             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34104         }
34105         this.updateColumns();
34106         this.layout();
34107     },
34108
34109     onRowSelect : function(rowIndex){
34110         var row = this.getRowComposite(rowIndex);
34111         row.addClass("x-grid-row-selected");
34112     },
34113
34114     onRowDeselect : function(rowIndex){
34115         var row = this.getRowComposite(rowIndex);
34116         row.removeClass("x-grid-row-selected");
34117     },
34118
34119     onCellSelect : function(row, col){
34120         var cell = this.getCell(row, col);
34121         if(cell){
34122             Roo.fly(cell).addClass("x-grid-cell-selected");
34123         }
34124     },
34125
34126     onCellDeselect : function(row, col){
34127         var cell = this.getCell(row, col);
34128         if(cell){
34129             Roo.fly(cell).removeClass("x-grid-cell-selected");
34130         }
34131     },
34132
34133     updateHeaderSortState : function(){
34134         
34135         // sort state can be single { field: xxx, direction : yyy}
34136         // or   { xxx=>ASC , yyy : DESC ..... }
34137         
34138         var mstate = {};
34139         if (!this.ds.multiSort) { 
34140             var state = this.ds.getSortState();
34141             if(!state){
34142                 return;
34143             }
34144             mstate[state.field] = state.direction;
34145             // FIXME... - this is not used here.. but might be elsewhere..
34146             this.sortState = state;
34147             
34148         } else {
34149             mstate = this.ds.sortToggle;
34150         }
34151         //remove existing sort classes..
34152         
34153         var sc = this.sortClasses;
34154         var hds = this.el.select(this.headerSelector).removeClass(sc);
34155         
34156         for(var f in mstate) {
34157         
34158             var sortColumn = this.cm.findColumnIndex(f);
34159             
34160             if(sortColumn != -1){
34161                 var sortDir = mstate[f];        
34162                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34163             }
34164         }
34165         
34166          
34167         
34168     },
34169
34170
34171     handleHeaderClick : function(g, index){
34172         if(this.headersDisabled){
34173             return;
34174         }
34175         var dm = g.dataSource, cm = g.colModel;
34176         if(!cm.isSortable(index)){
34177             return;
34178         }
34179         g.stopEditing();
34180         
34181         if (dm.multiSort) {
34182             // update the sortOrder
34183             var so = [];
34184             for(var i = 0; i < cm.config.length; i++ ) {
34185                 
34186                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34187                     continue; // dont' bother, it's not in sort list or being set.
34188                 }
34189                 
34190                 so.push(cm.config[i].dataIndex);
34191             };
34192             dm.sortOrder = so;
34193         }
34194         
34195         
34196         dm.sort(cm.getDataIndex(index));
34197     },
34198
34199
34200     destroy : function(){
34201         if(this.colMenu){
34202             this.colMenu.removeAll();
34203             Roo.menu.MenuMgr.unregister(this.colMenu);
34204             this.colMenu.getEl().remove();
34205             delete this.colMenu;
34206         }
34207         if(this.hmenu){
34208             this.hmenu.removeAll();
34209             Roo.menu.MenuMgr.unregister(this.hmenu);
34210             this.hmenu.getEl().remove();
34211             delete this.hmenu;
34212         }
34213         if(this.grid.enableColumnMove){
34214             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34215             if(dds){
34216                 for(var dd in dds){
34217                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34218                         var elid = dds[dd].dragElId;
34219                         dds[dd].unreg();
34220                         Roo.get(elid).remove();
34221                     } else if(dds[dd].config.isTarget){
34222                         dds[dd].proxyTop.remove();
34223                         dds[dd].proxyBottom.remove();
34224                         dds[dd].unreg();
34225                     }
34226                     if(Roo.dd.DDM.locationCache[dd]){
34227                         delete Roo.dd.DDM.locationCache[dd];
34228                     }
34229                 }
34230                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34231             }
34232         }
34233         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34234         this.bind(null, null);
34235         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34236     },
34237
34238     handleLockChange : function(){
34239         this.refresh(true);
34240     },
34241
34242     onDenyColumnLock : function(){
34243
34244     },
34245
34246     onDenyColumnHide : function(){
34247
34248     },
34249
34250     handleHdMenuClick : function(item){
34251         var index = this.hdCtxIndex;
34252         var cm = this.cm, ds = this.ds;
34253         switch(item.id){
34254             case "asc":
34255                 ds.sort(cm.getDataIndex(index), "ASC");
34256                 break;
34257             case "desc":
34258                 ds.sort(cm.getDataIndex(index), "DESC");
34259                 break;
34260             case "lock":
34261                 var lc = cm.getLockedCount();
34262                 if(cm.getColumnCount(true) <= lc+1){
34263                     this.onDenyColumnLock();
34264                     return;
34265                 }
34266                 if(lc != index){
34267                     cm.setLocked(index, true, true);
34268                     cm.moveColumn(index, lc);
34269                     this.grid.fireEvent("columnmove", index, lc);
34270                 }else{
34271                     cm.setLocked(index, true);
34272                 }
34273             break;
34274             case "unlock":
34275                 var lc = cm.getLockedCount();
34276                 if((lc-1) != index){
34277                     cm.setLocked(index, false, true);
34278                     cm.moveColumn(index, lc-1);
34279                     this.grid.fireEvent("columnmove", index, lc-1);
34280                 }else{
34281                     cm.setLocked(index, false);
34282                 }
34283             break;
34284             default:
34285                 index = cm.getIndexById(item.id.substr(4));
34286                 if(index != -1){
34287                     if(item.checked && cm.getColumnCount(true) <= 1){
34288                         this.onDenyColumnHide();
34289                         return false;
34290                     }
34291                     cm.setHidden(index, item.checked);
34292                 }
34293         }
34294         return true;
34295     },
34296
34297     beforeColMenuShow : function(){
34298         var cm = this.cm,  colCount = cm.getColumnCount();
34299         this.colMenu.removeAll();
34300         for(var i = 0; i < colCount; i++){
34301             this.colMenu.add(new Roo.menu.CheckItem({
34302                 id: "col-"+cm.getColumnId(i),
34303                 text: cm.getColumnHeader(i),
34304                 checked: !cm.isHidden(i),
34305                 hideOnClick:false
34306             }));
34307         }
34308     },
34309
34310     handleHdCtx : function(g, index, e){
34311         e.stopEvent();
34312         var hd = this.getHeaderCell(index);
34313         this.hdCtxIndex = index;
34314         var ms = this.hmenu.items, cm = this.cm;
34315         ms.get("asc").setDisabled(!cm.isSortable(index));
34316         ms.get("desc").setDisabled(!cm.isSortable(index));
34317         if(this.grid.enableColLock !== false){
34318             ms.get("lock").setDisabled(cm.isLocked(index));
34319             ms.get("unlock").setDisabled(!cm.isLocked(index));
34320         }
34321         this.hmenu.show(hd, "tl-bl");
34322     },
34323
34324     handleHdOver : function(e){
34325         var hd = this.findHeaderCell(e.getTarget());
34326         if(hd && !this.headersDisabled){
34327             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34328                this.fly(hd).addClass("x-grid-hd-over");
34329             }
34330         }
34331     },
34332
34333     handleHdOut : function(e){
34334         var hd = this.findHeaderCell(e.getTarget());
34335         if(hd){
34336             this.fly(hd).removeClass("x-grid-hd-over");
34337         }
34338     },
34339
34340     handleSplitDblClick : function(e, t){
34341         var i = this.getCellIndex(t);
34342         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34343             this.autoSizeColumn(i, true);
34344             this.layout();
34345         }
34346     },
34347
34348     render : function(){
34349
34350         var cm = this.cm;
34351         var colCount = cm.getColumnCount();
34352
34353         if(this.grid.monitorWindowResize === true){
34354             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34355         }
34356         var header = this.renderHeaders();
34357         var body = this.templates.body.apply({rows:""});
34358         var html = this.templates.master.apply({
34359             lockedBody: body,
34360             body: body,
34361             lockedHeader: header[0],
34362             header: header[1]
34363         });
34364
34365         //this.updateColumns();
34366
34367         this.grid.getGridEl().dom.innerHTML = html;
34368
34369         this.initElements();
34370         
34371         // a kludge to fix the random scolling effect in webkit
34372         this.el.on("scroll", function() {
34373             this.el.dom.scrollTop=0; // hopefully not recursive..
34374         },this);
34375
34376         this.scroller.on("scroll", this.handleScroll, this);
34377         this.lockedBody.on("mousewheel", this.handleWheel, this);
34378         this.mainBody.on("mousewheel", this.handleWheel, this);
34379
34380         this.mainHd.on("mouseover", this.handleHdOver, this);
34381         this.mainHd.on("mouseout", this.handleHdOut, this);
34382         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34383                 {delegate: "."+this.splitClass});
34384
34385         this.lockedHd.on("mouseover", this.handleHdOver, this);
34386         this.lockedHd.on("mouseout", this.handleHdOut, this);
34387         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34388                 {delegate: "."+this.splitClass});
34389
34390         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34391             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34392         }
34393
34394         this.updateSplitters();
34395
34396         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34397             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34398             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34399         }
34400
34401         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34402             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34403             this.hmenu.add(
34404                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34405                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34406             );
34407             if(this.grid.enableColLock !== false){
34408                 this.hmenu.add('-',
34409                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34410                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34411                 );
34412             }
34413             if(this.grid.enableColumnHide !== false){
34414
34415                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34416                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34417                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34418
34419                 this.hmenu.add('-',
34420                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34421                 );
34422             }
34423             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34424
34425             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34426         }
34427
34428         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34429             this.dd = new Roo.grid.GridDragZone(this.grid, {
34430                 ddGroup : this.grid.ddGroup || 'GridDD'
34431             });
34432         }
34433
34434         /*
34435         for(var i = 0; i < colCount; i++){
34436             if(cm.isHidden(i)){
34437                 this.hideColumn(i);
34438             }
34439             if(cm.config[i].align){
34440                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34441                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34442             }
34443         }*/
34444         
34445         this.updateHeaderSortState();
34446
34447         this.beforeInitialResize();
34448         this.layout(true);
34449
34450         // two part rendering gives faster view to the user
34451         this.renderPhase2.defer(1, this);
34452     },
34453
34454     renderPhase2 : function(){
34455         // render the rows now
34456         this.refresh();
34457         if(this.grid.autoSizeColumns){
34458             this.autoSizeColumns();
34459         }
34460     },
34461
34462     beforeInitialResize : function(){
34463
34464     },
34465
34466     onColumnSplitterMoved : function(i, w){
34467         this.userResized = true;
34468         var cm = this.grid.colModel;
34469         cm.setColumnWidth(i, w, true);
34470         var cid = cm.getColumnId(i);
34471         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34472         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34473         this.updateSplitters();
34474         this.layout();
34475         this.grid.fireEvent("columnresize", i, w);
34476     },
34477
34478     syncRowHeights : function(startIndex, endIndex){
34479         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34480             startIndex = startIndex || 0;
34481             var mrows = this.getBodyTable().rows;
34482             var lrows = this.getLockedTable().rows;
34483             var len = mrows.length-1;
34484             endIndex = Math.min(endIndex || len, len);
34485             for(var i = startIndex; i <= endIndex; i++){
34486                 var m = mrows[i], l = lrows[i];
34487                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34488                 m.style.height = l.style.height = h + "px";
34489             }
34490         }
34491     },
34492
34493     layout : function(initialRender, is2ndPass){
34494         var g = this.grid;
34495         var auto = g.autoHeight;
34496         var scrollOffset = 16;
34497         var c = g.getGridEl(), cm = this.cm,
34498                 expandCol = g.autoExpandColumn,
34499                 gv = this;
34500         //c.beginMeasure();
34501
34502         if(!c.dom.offsetWidth){ // display:none?
34503             if(initialRender){
34504                 this.lockedWrap.show();
34505                 this.mainWrap.show();
34506             }
34507             return;
34508         }
34509
34510         var hasLock = this.cm.isLocked(0);
34511
34512         var tbh = this.headerPanel.getHeight();
34513         var bbh = this.footerPanel.getHeight();
34514
34515         if(auto){
34516             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34517             var newHeight = ch + c.getBorderWidth("tb");
34518             if(g.maxHeight){
34519                 newHeight = Math.min(g.maxHeight, newHeight);
34520             }
34521             c.setHeight(newHeight);
34522         }
34523
34524         if(g.autoWidth){
34525             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34526         }
34527
34528         var s = this.scroller;
34529
34530         var csize = c.getSize(true);
34531
34532         this.el.setSize(csize.width, csize.height);
34533
34534         this.headerPanel.setWidth(csize.width);
34535         this.footerPanel.setWidth(csize.width);
34536
34537         var hdHeight = this.mainHd.getHeight();
34538         var vw = csize.width;
34539         var vh = csize.height - (tbh + bbh);
34540
34541         s.setSize(vw, vh);
34542
34543         var bt = this.getBodyTable();
34544         var ltWidth = hasLock ?
34545                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34546
34547         var scrollHeight = bt.offsetHeight;
34548         var scrollWidth = ltWidth + bt.offsetWidth;
34549         var vscroll = false, hscroll = false;
34550
34551         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34552
34553         var lw = this.lockedWrap, mw = this.mainWrap;
34554         var lb = this.lockedBody, mb = this.mainBody;
34555
34556         setTimeout(function(){
34557             var t = s.dom.offsetTop;
34558             var w = s.dom.clientWidth,
34559                 h = s.dom.clientHeight;
34560
34561             lw.setTop(t);
34562             lw.setSize(ltWidth, h);
34563
34564             mw.setLeftTop(ltWidth, t);
34565             mw.setSize(w-ltWidth, h);
34566
34567             lb.setHeight(h-hdHeight);
34568             mb.setHeight(h-hdHeight);
34569
34570             if(is2ndPass !== true && !gv.userResized && expandCol){
34571                 // high speed resize without full column calculation
34572                 
34573                 var ci = cm.getIndexById(expandCol);
34574                 if (ci < 0) {
34575                     ci = cm.findColumnIndex(expandCol);
34576                 }
34577                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34578                 var expandId = cm.getColumnId(ci);
34579                 var  tw = cm.getTotalWidth(false);
34580                 var currentWidth = cm.getColumnWidth(ci);
34581                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34582                 if(currentWidth != cw){
34583                     cm.setColumnWidth(ci, cw, true);
34584                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34585                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34586                     gv.updateSplitters();
34587                     gv.layout(false, true);
34588                 }
34589             }
34590
34591             if(initialRender){
34592                 lw.show();
34593                 mw.show();
34594             }
34595             //c.endMeasure();
34596         }, 10);
34597     },
34598
34599     onWindowResize : function(){
34600         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34601             return;
34602         }
34603         this.layout();
34604     },
34605
34606     appendFooter : function(parentEl){
34607         return null;
34608     },
34609
34610     sortAscText : "Sort Ascending",
34611     sortDescText : "Sort Descending",
34612     lockText : "Lock Column",
34613     unlockText : "Unlock Column",
34614     columnsText : "Columns"
34615 });
34616
34617
34618 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34619     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34620     this.proxy.el.addClass('x-grid3-col-dd');
34621 };
34622
34623 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34624     handleMouseDown : function(e){
34625
34626     },
34627
34628     callHandleMouseDown : function(e){
34629         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34630     }
34631 });
34632 /*
34633  * Based on:
34634  * Ext JS Library 1.1.1
34635  * Copyright(c) 2006-2007, Ext JS, LLC.
34636  *
34637  * Originally Released Under LGPL - original licence link has changed is not relivant.
34638  *
34639  * Fork - LGPL
34640  * <script type="text/javascript">
34641  */
34642  
34643 // private
34644 // This is a support class used internally by the Grid components
34645 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34646     this.grid = grid;
34647     this.view = grid.getView();
34648     this.proxy = this.view.resizeProxy;
34649     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34650         "gridSplitters" + this.grid.getGridEl().id, {
34651         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34652     });
34653     this.setHandleElId(Roo.id(hd));
34654     this.setOuterHandleElId(Roo.id(hd2));
34655     this.scroll = false;
34656 };
34657 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34658     fly: Roo.Element.fly,
34659
34660     b4StartDrag : function(x, y){
34661         this.view.headersDisabled = true;
34662         this.proxy.setHeight(this.view.mainWrap.getHeight());
34663         var w = this.cm.getColumnWidth(this.cellIndex);
34664         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34665         this.resetConstraints();
34666         this.setXConstraint(minw, 1000);
34667         this.setYConstraint(0, 0);
34668         this.minX = x - minw;
34669         this.maxX = x + 1000;
34670         this.startPos = x;
34671         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34672     },
34673
34674
34675     handleMouseDown : function(e){
34676         ev = Roo.EventObject.setEvent(e);
34677         var t = this.fly(ev.getTarget());
34678         if(t.hasClass("x-grid-split")){
34679             this.cellIndex = this.view.getCellIndex(t.dom);
34680             this.split = t.dom;
34681             this.cm = this.grid.colModel;
34682             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34683                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34684             }
34685         }
34686     },
34687
34688     endDrag : function(e){
34689         this.view.headersDisabled = false;
34690         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34691         var diff = endX - this.startPos;
34692         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34693     },
34694
34695     autoOffset : function(){
34696         this.setDelta(0,0);
34697     }
34698 });/*
34699  * Based on:
34700  * Ext JS Library 1.1.1
34701  * Copyright(c) 2006-2007, Ext JS, LLC.
34702  *
34703  * Originally Released Under LGPL - original licence link has changed is not relivant.
34704  *
34705  * Fork - LGPL
34706  * <script type="text/javascript">
34707  */
34708  
34709 // private
34710 // This is a support class used internally by the Grid components
34711 Roo.grid.GridDragZone = function(grid, config){
34712     this.view = grid.getView();
34713     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34714     if(this.view.lockedBody){
34715         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34716         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34717     }
34718     this.scroll = false;
34719     this.grid = grid;
34720     this.ddel = document.createElement('div');
34721     this.ddel.className = 'x-grid-dd-wrap';
34722 };
34723
34724 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34725     ddGroup : "GridDD",
34726
34727     getDragData : function(e){
34728         var t = Roo.lib.Event.getTarget(e);
34729         var rowIndex = this.view.findRowIndex(t);
34730         if(rowIndex !== false){
34731             var sm = this.grid.selModel;
34732             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34733               //  sm.mouseDown(e, t);
34734             //}
34735             if (e.hasModifier()){
34736                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34737             }
34738             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34739         }
34740         return false;
34741     },
34742
34743     onInitDrag : function(e){
34744         var data = this.dragData;
34745         this.ddel.innerHTML = this.grid.getDragDropText();
34746         this.proxy.update(this.ddel);
34747         // fire start drag?
34748     },
34749
34750     afterRepair : function(){
34751         this.dragging = false;
34752     },
34753
34754     getRepairXY : function(e, data){
34755         return false;
34756     },
34757
34758     onEndDrag : function(data, e){
34759         // fire end drag?
34760     },
34761
34762     onValidDrop : function(dd, e, id){
34763         // fire drag drop?
34764         this.hideProxy();
34765     },
34766
34767     beforeInvalidDrop : function(e, id){
34768
34769     }
34770 });/*
34771  * Based on:
34772  * Ext JS Library 1.1.1
34773  * Copyright(c) 2006-2007, Ext JS, LLC.
34774  *
34775  * Originally Released Under LGPL - original licence link has changed is not relivant.
34776  *
34777  * Fork - LGPL
34778  * <script type="text/javascript">
34779  */
34780  
34781
34782 /**
34783  * @class Roo.grid.ColumnModel
34784  * @extends Roo.util.Observable
34785  * This is the default implementation of a ColumnModel used by the Grid. It defines
34786  * the columns in the grid.
34787  * <br>Usage:<br>
34788  <pre><code>
34789  var colModel = new Roo.grid.ColumnModel([
34790         {header: "Ticker", width: 60, sortable: true, locked: true},
34791         {header: "Company Name", width: 150, sortable: true},
34792         {header: "Market Cap.", width: 100, sortable: true},
34793         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34794         {header: "Employees", width: 100, sortable: true, resizable: false}
34795  ]);
34796  </code></pre>
34797  * <p>
34798  
34799  * The config options listed for this class are options which may appear in each
34800  * individual column definition.
34801  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34802  * @constructor
34803  * @param {Object} config An Array of column config objects. See this class's
34804  * config objects for details.
34805 */
34806 Roo.grid.ColumnModel = function(config){
34807         /**
34808      * The config passed into the constructor
34809      */
34810     this.config = config;
34811     this.lookup = {};
34812
34813     // if no id, create one
34814     // if the column does not have a dataIndex mapping,
34815     // map it to the order it is in the config
34816     for(var i = 0, len = config.length; i < len; i++){
34817         var c = config[i];
34818         if(typeof c.dataIndex == "undefined"){
34819             c.dataIndex = i;
34820         }
34821         if(typeof c.renderer == "string"){
34822             c.renderer = Roo.util.Format[c.renderer];
34823         }
34824         if(typeof c.id == "undefined"){
34825             c.id = Roo.id();
34826         }
34827         if(c.editor && c.editor.xtype){
34828             c.editor  = Roo.factory(c.editor, Roo.grid);
34829         }
34830         if(c.editor && c.editor.isFormField){
34831             c.editor = new Roo.grid.GridEditor(c.editor);
34832         }
34833         this.lookup[c.id] = c;
34834     }
34835
34836     /**
34837      * The width of columns which have no width specified (defaults to 100)
34838      * @type Number
34839      */
34840     this.defaultWidth = 100;
34841
34842     /**
34843      * Default sortable of columns which have no sortable specified (defaults to false)
34844      * @type Boolean
34845      */
34846     this.defaultSortable = false;
34847
34848     this.addEvents({
34849         /**
34850              * @event widthchange
34851              * Fires when the width of a column changes.
34852              * @param {ColumnModel} this
34853              * @param {Number} columnIndex The column index
34854              * @param {Number} newWidth The new width
34855              */
34856             "widthchange": true,
34857         /**
34858              * @event headerchange
34859              * Fires when the text of a header changes.
34860              * @param {ColumnModel} this
34861              * @param {Number} columnIndex The column index
34862              * @param {Number} newText The new header text
34863              */
34864             "headerchange": true,
34865         /**
34866              * @event hiddenchange
34867              * Fires when a column is hidden or "unhidden".
34868              * @param {ColumnModel} this
34869              * @param {Number} columnIndex The column index
34870              * @param {Boolean} hidden true if hidden, false otherwise
34871              */
34872             "hiddenchange": true,
34873             /**
34874          * @event columnmoved
34875          * Fires when a column is moved.
34876          * @param {ColumnModel} this
34877          * @param {Number} oldIndex
34878          * @param {Number} newIndex
34879          */
34880         "columnmoved" : true,
34881         /**
34882          * @event columlockchange
34883          * Fires when a column's locked state is changed
34884          * @param {ColumnModel} this
34885          * @param {Number} colIndex
34886          * @param {Boolean} locked true if locked
34887          */
34888         "columnlockchange" : true
34889     });
34890     Roo.grid.ColumnModel.superclass.constructor.call(this);
34891 };
34892 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34893     /**
34894      * @cfg {String} header The header text to display in the Grid view.
34895      */
34896     /**
34897      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34898      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34899      * specified, the column's index is used as an index into the Record's data Array.
34900      */
34901     /**
34902      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34903      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34904      */
34905     /**
34906      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34907      * Defaults to the value of the {@link #defaultSortable} property.
34908      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34909      */
34910     /**
34911      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34912      */
34913     /**
34914      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34915      */
34916     /**
34917      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34918      */
34919     /**
34920      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34921      */
34922     /**
34923      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34924      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34925      * default renderer uses the raw data value.
34926      */
34927        /**
34928      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34929      */
34930     /**
34931      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34932      */
34933
34934     /**
34935      * Returns the id of the column at the specified index.
34936      * @param {Number} index The column index
34937      * @return {String} the id
34938      */
34939     getColumnId : function(index){
34940         return this.config[index].id;
34941     },
34942
34943     /**
34944      * Returns the column for a specified id.
34945      * @param {String} id The column id
34946      * @return {Object} the column
34947      */
34948     getColumnById : function(id){
34949         return this.lookup[id];
34950     },
34951
34952     
34953     /**
34954      * Returns the column for a specified dataIndex.
34955      * @param {String} dataIndex The column dataIndex
34956      * @return {Object|Boolean} the column or false if not found
34957      */
34958     getColumnByDataIndex: function(dataIndex){
34959         var index = this.findColumnIndex(dataIndex);
34960         return index > -1 ? this.config[index] : false;
34961     },
34962     
34963     /**
34964      * Returns the index for a specified column id.
34965      * @param {String} id The column id
34966      * @return {Number} the index, or -1 if not found
34967      */
34968     getIndexById : function(id){
34969         for(var i = 0, len = this.config.length; i < len; i++){
34970             if(this.config[i].id == id){
34971                 return i;
34972             }
34973         }
34974         return -1;
34975     },
34976     
34977     /**
34978      * Returns the index for a specified column dataIndex.
34979      * @param {String} dataIndex The column dataIndex
34980      * @return {Number} the index, or -1 if not found
34981      */
34982     
34983     findColumnIndex : function(dataIndex){
34984         for(var i = 0, len = this.config.length; i < len; i++){
34985             if(this.config[i].dataIndex == dataIndex){
34986                 return i;
34987             }
34988         }
34989         return -1;
34990     },
34991     
34992     
34993     moveColumn : function(oldIndex, newIndex){
34994         var c = this.config[oldIndex];
34995         this.config.splice(oldIndex, 1);
34996         this.config.splice(newIndex, 0, c);
34997         this.dataMap = null;
34998         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34999     },
35000
35001     isLocked : function(colIndex){
35002         return this.config[colIndex].locked === true;
35003     },
35004
35005     setLocked : function(colIndex, value, suppressEvent){
35006         if(this.isLocked(colIndex) == value){
35007             return;
35008         }
35009         this.config[colIndex].locked = value;
35010         if(!suppressEvent){
35011             this.fireEvent("columnlockchange", this, colIndex, value);
35012         }
35013     },
35014
35015     getTotalLockedWidth : function(){
35016         var totalWidth = 0;
35017         for(var i = 0; i < this.config.length; i++){
35018             if(this.isLocked(i) && !this.isHidden(i)){
35019                 this.totalWidth += this.getColumnWidth(i);
35020             }
35021         }
35022         return totalWidth;
35023     },
35024
35025     getLockedCount : function(){
35026         for(var i = 0, len = this.config.length; i < len; i++){
35027             if(!this.isLocked(i)){
35028                 return i;
35029             }
35030         }
35031     },
35032
35033     /**
35034      * Returns the number of columns.
35035      * @return {Number}
35036      */
35037     getColumnCount : function(visibleOnly){
35038         if(visibleOnly === true){
35039             var c = 0;
35040             for(var i = 0, len = this.config.length; i < len; i++){
35041                 if(!this.isHidden(i)){
35042                     c++;
35043                 }
35044             }
35045             return c;
35046         }
35047         return this.config.length;
35048     },
35049
35050     /**
35051      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35052      * @param {Function} fn
35053      * @param {Object} scope (optional)
35054      * @return {Array} result
35055      */
35056     getColumnsBy : function(fn, scope){
35057         var r = [];
35058         for(var i = 0, len = this.config.length; i < len; i++){
35059             var c = this.config[i];
35060             if(fn.call(scope||this, c, i) === true){
35061                 r[r.length] = c;
35062             }
35063         }
35064         return r;
35065     },
35066
35067     /**
35068      * Returns true if the specified column is sortable.
35069      * @param {Number} col The column index
35070      * @return {Boolean}
35071      */
35072     isSortable : function(col){
35073         if(typeof this.config[col].sortable == "undefined"){
35074             return this.defaultSortable;
35075         }
35076         return this.config[col].sortable;
35077     },
35078
35079     /**
35080      * Returns the rendering (formatting) function defined for the column.
35081      * @param {Number} col The column index.
35082      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35083      */
35084     getRenderer : function(col){
35085         if(!this.config[col].renderer){
35086             return Roo.grid.ColumnModel.defaultRenderer;
35087         }
35088         return this.config[col].renderer;
35089     },
35090
35091     /**
35092      * Sets the rendering (formatting) function for a column.
35093      * @param {Number} col The column index
35094      * @param {Function} fn The function to use to process the cell's raw data
35095      * to return HTML markup for the grid view. The render function is called with
35096      * the following parameters:<ul>
35097      * <li>Data value.</li>
35098      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35099      * <li>css A CSS style string to apply to the table cell.</li>
35100      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35101      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35102      * <li>Row index</li>
35103      * <li>Column index</li>
35104      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35105      */
35106     setRenderer : function(col, fn){
35107         this.config[col].renderer = fn;
35108     },
35109
35110     /**
35111      * Returns the width for the specified column.
35112      * @param {Number} col The column index
35113      * @return {Number}
35114      */
35115     getColumnWidth : function(col){
35116         return this.config[col].width * 1 || this.defaultWidth;
35117     },
35118
35119     /**
35120      * Sets the width for a column.
35121      * @param {Number} col The column index
35122      * @param {Number} width The new width
35123      */
35124     setColumnWidth : function(col, width, suppressEvent){
35125         this.config[col].width = width;
35126         this.totalWidth = null;
35127         if(!suppressEvent){
35128              this.fireEvent("widthchange", this, col, width);
35129         }
35130     },
35131
35132     /**
35133      * Returns the total width of all columns.
35134      * @param {Boolean} includeHidden True to include hidden column widths
35135      * @return {Number}
35136      */
35137     getTotalWidth : function(includeHidden){
35138         if(!this.totalWidth){
35139             this.totalWidth = 0;
35140             for(var i = 0, len = this.config.length; i < len; i++){
35141                 if(includeHidden || !this.isHidden(i)){
35142                     this.totalWidth += this.getColumnWidth(i);
35143                 }
35144             }
35145         }
35146         return this.totalWidth;
35147     },
35148
35149     /**
35150      * Returns the header for the specified column.
35151      * @param {Number} col The column index
35152      * @return {String}
35153      */
35154     getColumnHeader : function(col){
35155         return this.config[col].header;
35156     },
35157
35158     /**
35159      * Sets the header for a column.
35160      * @param {Number} col The column index
35161      * @param {String} header The new header
35162      */
35163     setColumnHeader : function(col, header){
35164         this.config[col].header = header;
35165         this.fireEvent("headerchange", this, col, header);
35166     },
35167
35168     /**
35169      * Returns the tooltip for the specified column.
35170      * @param {Number} col The column index
35171      * @return {String}
35172      */
35173     getColumnTooltip : function(col){
35174             return this.config[col].tooltip;
35175     },
35176     /**
35177      * Sets the tooltip for a column.
35178      * @param {Number} col The column index
35179      * @param {String} tooltip The new tooltip
35180      */
35181     setColumnTooltip : function(col, tooltip){
35182             this.config[col].tooltip = tooltip;
35183     },
35184
35185     /**
35186      * Returns the dataIndex for the specified column.
35187      * @param {Number} col The column index
35188      * @return {Number}
35189      */
35190     getDataIndex : function(col){
35191         return this.config[col].dataIndex;
35192     },
35193
35194     /**
35195      * Sets the dataIndex for a column.
35196      * @param {Number} col The column index
35197      * @param {Number} dataIndex The new dataIndex
35198      */
35199     setDataIndex : function(col, dataIndex){
35200         this.config[col].dataIndex = dataIndex;
35201     },
35202
35203     
35204     
35205     /**
35206      * Returns true if the cell is editable.
35207      * @param {Number} colIndex The column index
35208      * @param {Number} rowIndex The row index
35209      * @return {Boolean}
35210      */
35211     isCellEditable : function(colIndex, rowIndex){
35212         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35213     },
35214
35215     /**
35216      * Returns the editor defined for the cell/column.
35217      * return false or null to disable editing.
35218      * @param {Number} colIndex The column index
35219      * @param {Number} rowIndex The row index
35220      * @return {Object}
35221      */
35222     getCellEditor : function(colIndex, rowIndex){
35223         return this.config[colIndex].editor;
35224     },
35225
35226     /**
35227      * Sets if a column is editable.
35228      * @param {Number} col The column index
35229      * @param {Boolean} editable True if the column is editable
35230      */
35231     setEditable : function(col, editable){
35232         this.config[col].editable = editable;
35233     },
35234
35235
35236     /**
35237      * Returns true if the column is hidden.
35238      * @param {Number} colIndex The column index
35239      * @return {Boolean}
35240      */
35241     isHidden : function(colIndex){
35242         return this.config[colIndex].hidden;
35243     },
35244
35245
35246     /**
35247      * Returns true if the column width cannot be changed
35248      */
35249     isFixed : function(colIndex){
35250         return this.config[colIndex].fixed;
35251     },
35252
35253     /**
35254      * Returns true if the column can be resized
35255      * @return {Boolean}
35256      */
35257     isResizable : function(colIndex){
35258         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35259     },
35260     /**
35261      * Sets if a column is hidden.
35262      * @param {Number} colIndex The column index
35263      * @param {Boolean} hidden True if the column is hidden
35264      */
35265     setHidden : function(colIndex, hidden){
35266         this.config[colIndex].hidden = hidden;
35267         this.totalWidth = null;
35268         this.fireEvent("hiddenchange", this, colIndex, hidden);
35269     },
35270
35271     /**
35272      * Sets the editor for a column.
35273      * @param {Number} col The column index
35274      * @param {Object} editor The editor object
35275      */
35276     setEditor : function(col, editor){
35277         this.config[col].editor = editor;
35278     }
35279 });
35280
35281 Roo.grid.ColumnModel.defaultRenderer = function(value){
35282         if(typeof value == "string" && value.length < 1){
35283             return "&#160;";
35284         }
35285         return value;
35286 };
35287
35288 // Alias for backwards compatibility
35289 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35290 /*
35291  * Based on:
35292  * Ext JS Library 1.1.1
35293  * Copyright(c) 2006-2007, Ext JS, LLC.
35294  *
35295  * Originally Released Under LGPL - original licence link has changed is not relivant.
35296  *
35297  * Fork - LGPL
35298  * <script type="text/javascript">
35299  */
35300
35301 /**
35302  * @class Roo.grid.AbstractSelectionModel
35303  * @extends Roo.util.Observable
35304  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35305  * implemented by descendant classes.  This class should not be directly instantiated.
35306  * @constructor
35307  */
35308 Roo.grid.AbstractSelectionModel = function(){
35309     this.locked = false;
35310     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35311 };
35312
35313 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35314     /** @ignore Called by the grid automatically. Do not call directly. */
35315     init : function(grid){
35316         this.grid = grid;
35317         this.initEvents();
35318     },
35319
35320     /**
35321      * Locks the selections.
35322      */
35323     lock : function(){
35324         this.locked = true;
35325     },
35326
35327     /**
35328      * Unlocks the selections.
35329      */
35330     unlock : function(){
35331         this.locked = false;
35332     },
35333
35334     /**
35335      * Returns true if the selections are locked.
35336      * @return {Boolean}
35337      */
35338     isLocked : function(){
35339         return this.locked;
35340     }
35341 });/*
35342  * Based on:
35343  * Ext JS Library 1.1.1
35344  * Copyright(c) 2006-2007, Ext JS, LLC.
35345  *
35346  * Originally Released Under LGPL - original licence link has changed is not relivant.
35347  *
35348  * Fork - LGPL
35349  * <script type="text/javascript">
35350  */
35351 /**
35352  * @extends Roo.grid.AbstractSelectionModel
35353  * @class Roo.grid.RowSelectionModel
35354  * The default SelectionModel used by {@link Roo.grid.Grid}.
35355  * It supports multiple selections and keyboard selection/navigation. 
35356  * @constructor
35357  * @param {Object} config
35358  */
35359 Roo.grid.RowSelectionModel = function(config){
35360     Roo.apply(this, config);
35361     this.selections = new Roo.util.MixedCollection(false, function(o){
35362         return o.id;
35363     });
35364
35365     this.last = false;
35366     this.lastActive = false;
35367
35368     this.addEvents({
35369         /**
35370              * @event selectionchange
35371              * Fires when the selection changes
35372              * @param {SelectionModel} this
35373              */
35374             "selectionchange" : true,
35375         /**
35376              * @event afterselectionchange
35377              * Fires after the selection changes (eg. by key press or clicking)
35378              * @param {SelectionModel} this
35379              */
35380             "afterselectionchange" : true,
35381         /**
35382              * @event beforerowselect
35383              * Fires when a row is selected being selected, return false to cancel.
35384              * @param {SelectionModel} this
35385              * @param {Number} rowIndex The selected index
35386              * @param {Boolean} keepExisting False if other selections will be cleared
35387              */
35388             "beforerowselect" : true,
35389         /**
35390              * @event rowselect
35391              * Fires when a row is selected.
35392              * @param {SelectionModel} this
35393              * @param {Number} rowIndex The selected index
35394              * @param {Roo.data.Record} r The record
35395              */
35396             "rowselect" : true,
35397         /**
35398              * @event rowdeselect
35399              * Fires when a row is deselected.
35400              * @param {SelectionModel} this
35401              * @param {Number} rowIndex The selected index
35402              */
35403         "rowdeselect" : true
35404     });
35405     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35406     this.locked = false;
35407 };
35408
35409 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35410     /**
35411      * @cfg {Boolean} singleSelect
35412      * True to allow selection of only one row at a time (defaults to false)
35413      */
35414     singleSelect : false,
35415
35416     // private
35417     initEvents : function(){
35418
35419         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35420             this.grid.on("mousedown", this.handleMouseDown, this);
35421         }else{ // allow click to work like normal
35422             this.grid.on("rowclick", this.handleDragableRowClick, this);
35423         }
35424
35425         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35426             "up" : function(e){
35427                 if(!e.shiftKey){
35428                     this.selectPrevious(e.shiftKey);
35429                 }else if(this.last !== false && this.lastActive !== false){
35430                     var last = this.last;
35431                     this.selectRange(this.last,  this.lastActive-1);
35432                     this.grid.getView().focusRow(this.lastActive);
35433                     if(last !== false){
35434                         this.last = last;
35435                     }
35436                 }else{
35437                     this.selectFirstRow();
35438                 }
35439                 this.fireEvent("afterselectionchange", this);
35440             },
35441             "down" : function(e){
35442                 if(!e.shiftKey){
35443                     this.selectNext(e.shiftKey);
35444                 }else if(this.last !== false && this.lastActive !== false){
35445                     var last = this.last;
35446                     this.selectRange(this.last,  this.lastActive+1);
35447                     this.grid.getView().focusRow(this.lastActive);
35448                     if(last !== false){
35449                         this.last = last;
35450                     }
35451                 }else{
35452                     this.selectFirstRow();
35453                 }
35454                 this.fireEvent("afterselectionchange", this);
35455             },
35456             scope: this
35457         });
35458
35459         var view = this.grid.view;
35460         view.on("refresh", this.onRefresh, this);
35461         view.on("rowupdated", this.onRowUpdated, this);
35462         view.on("rowremoved", this.onRemove, this);
35463     },
35464
35465     // private
35466     onRefresh : function(){
35467         var ds = this.grid.dataSource, i, v = this.grid.view;
35468         var s = this.selections;
35469         s.each(function(r){
35470             if((i = ds.indexOfId(r.id)) != -1){
35471                 v.onRowSelect(i);
35472             }else{
35473                 s.remove(r);
35474             }
35475         });
35476     },
35477
35478     // private
35479     onRemove : function(v, index, r){
35480         this.selections.remove(r);
35481     },
35482
35483     // private
35484     onRowUpdated : function(v, index, r){
35485         if(this.isSelected(r)){
35486             v.onRowSelect(index);
35487         }
35488     },
35489
35490     /**
35491      * Select records.
35492      * @param {Array} records The records to select
35493      * @param {Boolean} keepExisting (optional) True to keep existing selections
35494      */
35495     selectRecords : function(records, keepExisting){
35496         if(!keepExisting){
35497             this.clearSelections();
35498         }
35499         var ds = this.grid.dataSource;
35500         for(var i = 0, len = records.length; i < len; i++){
35501             this.selectRow(ds.indexOf(records[i]), true);
35502         }
35503     },
35504
35505     /**
35506      * Gets the number of selected rows.
35507      * @return {Number}
35508      */
35509     getCount : function(){
35510         return this.selections.length;
35511     },
35512
35513     /**
35514      * Selects the first row in the grid.
35515      */
35516     selectFirstRow : function(){
35517         this.selectRow(0);
35518     },
35519
35520     /**
35521      * Select the last row.
35522      * @param {Boolean} keepExisting (optional) True to keep existing selections
35523      */
35524     selectLastRow : function(keepExisting){
35525         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35526     },
35527
35528     /**
35529      * Selects the row immediately following the last selected row.
35530      * @param {Boolean} keepExisting (optional) True to keep existing selections
35531      */
35532     selectNext : function(keepExisting){
35533         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35534             this.selectRow(this.last+1, keepExisting);
35535             this.grid.getView().focusRow(this.last);
35536         }
35537     },
35538
35539     /**
35540      * Selects the row that precedes the last selected row.
35541      * @param {Boolean} keepExisting (optional) True to keep existing selections
35542      */
35543     selectPrevious : function(keepExisting){
35544         if(this.last){
35545             this.selectRow(this.last-1, keepExisting);
35546             this.grid.getView().focusRow(this.last);
35547         }
35548     },
35549
35550     /**
35551      * Returns the selected records
35552      * @return {Array} Array of selected records
35553      */
35554     getSelections : function(){
35555         return [].concat(this.selections.items);
35556     },
35557
35558     /**
35559      * Returns the first selected record.
35560      * @return {Record}
35561      */
35562     getSelected : function(){
35563         return this.selections.itemAt(0);
35564     },
35565
35566
35567     /**
35568      * Clears all selections.
35569      */
35570     clearSelections : function(fast){
35571         if(this.locked) return;
35572         if(fast !== true){
35573             var ds = this.grid.dataSource;
35574             var s = this.selections;
35575             s.each(function(r){
35576                 this.deselectRow(ds.indexOfId(r.id));
35577             }, this);
35578             s.clear();
35579         }else{
35580             this.selections.clear();
35581         }
35582         this.last = false;
35583     },
35584
35585
35586     /**
35587      * Selects all rows.
35588      */
35589     selectAll : function(){
35590         if(this.locked) return;
35591         this.selections.clear();
35592         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35593             this.selectRow(i, true);
35594         }
35595     },
35596
35597     /**
35598      * Returns True if there is a selection.
35599      * @return {Boolean}
35600      */
35601     hasSelection : function(){
35602         return this.selections.length > 0;
35603     },
35604
35605     /**
35606      * Returns True if the specified row is selected.
35607      * @param {Number/Record} record The record or index of the record to check
35608      * @return {Boolean}
35609      */
35610     isSelected : function(index){
35611         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35612         return (r && this.selections.key(r.id) ? true : false);
35613     },
35614
35615     /**
35616      * Returns True if the specified record id is selected.
35617      * @param {String} id The id of record to check
35618      * @return {Boolean}
35619      */
35620     isIdSelected : function(id){
35621         return (this.selections.key(id) ? true : false);
35622     },
35623
35624     // private
35625     handleMouseDown : function(e, t){
35626         var view = this.grid.getView(), rowIndex;
35627         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35628             return;
35629         };
35630         if(e.shiftKey && this.last !== false){
35631             var last = this.last;
35632             this.selectRange(last, rowIndex, e.ctrlKey);
35633             this.last = last; // reset the last
35634             view.focusRow(rowIndex);
35635         }else{
35636             var isSelected = this.isSelected(rowIndex);
35637             if(e.button !== 0 && isSelected){
35638                 view.focusRow(rowIndex);
35639             }else if(e.ctrlKey && isSelected){
35640                 this.deselectRow(rowIndex);
35641             }else if(!isSelected){
35642                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35643                 view.focusRow(rowIndex);
35644             }
35645         }
35646         this.fireEvent("afterselectionchange", this);
35647     },
35648     // private
35649     handleDragableRowClick :  function(grid, rowIndex, e) 
35650     {
35651         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35652             this.selectRow(rowIndex, false);
35653             grid.view.focusRow(rowIndex);
35654              this.fireEvent("afterselectionchange", this);
35655         }
35656     },
35657     
35658     /**
35659      * Selects multiple rows.
35660      * @param {Array} rows Array of the indexes of the row to select
35661      * @param {Boolean} keepExisting (optional) True to keep existing selections
35662      */
35663     selectRows : function(rows, keepExisting){
35664         if(!keepExisting){
35665             this.clearSelections();
35666         }
35667         for(var i = 0, len = rows.length; i < len; i++){
35668             this.selectRow(rows[i], true);
35669         }
35670     },
35671
35672     /**
35673      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35674      * @param {Number} startRow The index of the first row in the range
35675      * @param {Number} endRow The index of the last row in the range
35676      * @param {Boolean} keepExisting (optional) True to retain existing selections
35677      */
35678     selectRange : function(startRow, endRow, keepExisting){
35679         if(this.locked) return;
35680         if(!keepExisting){
35681             this.clearSelections();
35682         }
35683         if(startRow <= endRow){
35684             for(var i = startRow; i <= endRow; i++){
35685                 this.selectRow(i, true);
35686             }
35687         }else{
35688             for(var i = startRow; i >= endRow; i--){
35689                 this.selectRow(i, true);
35690             }
35691         }
35692     },
35693
35694     /**
35695      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35696      * @param {Number} startRow The index of the first row in the range
35697      * @param {Number} endRow The index of the last row in the range
35698      */
35699     deselectRange : function(startRow, endRow, preventViewNotify){
35700         if(this.locked) return;
35701         for(var i = startRow; i <= endRow; i++){
35702             this.deselectRow(i, preventViewNotify);
35703         }
35704     },
35705
35706     /**
35707      * Selects a row.
35708      * @param {Number} row The index of the row to select
35709      * @param {Boolean} keepExisting (optional) True to keep existing selections
35710      */
35711     selectRow : function(index, keepExisting, preventViewNotify){
35712         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35713         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35714             if(!keepExisting || this.singleSelect){
35715                 this.clearSelections();
35716             }
35717             var r = this.grid.dataSource.getAt(index);
35718             this.selections.add(r);
35719             this.last = this.lastActive = index;
35720             if(!preventViewNotify){
35721                 this.grid.getView().onRowSelect(index);
35722             }
35723             this.fireEvent("rowselect", this, index, r);
35724             this.fireEvent("selectionchange", this);
35725         }
35726     },
35727
35728     /**
35729      * Deselects a row.
35730      * @param {Number} row The index of the row to deselect
35731      */
35732     deselectRow : function(index, preventViewNotify){
35733         if(this.locked) return;
35734         if(this.last == index){
35735             this.last = false;
35736         }
35737         if(this.lastActive == index){
35738             this.lastActive = false;
35739         }
35740         var r = this.grid.dataSource.getAt(index);
35741         this.selections.remove(r);
35742         if(!preventViewNotify){
35743             this.grid.getView().onRowDeselect(index);
35744         }
35745         this.fireEvent("rowdeselect", this, index);
35746         this.fireEvent("selectionchange", this);
35747     },
35748
35749     // private
35750     restoreLast : function(){
35751         if(this._last){
35752             this.last = this._last;
35753         }
35754     },
35755
35756     // private
35757     acceptsNav : function(row, col, cm){
35758         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35759     },
35760
35761     // private
35762     onEditorKey : function(field, e){
35763         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35764         if(k == e.TAB){
35765             e.stopEvent();
35766             ed.completeEdit();
35767             if(e.shiftKey){
35768                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35769             }else{
35770                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35771             }
35772         }else if(k == e.ENTER && !e.ctrlKey){
35773             e.stopEvent();
35774             ed.completeEdit();
35775             if(e.shiftKey){
35776                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35777             }else{
35778                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35779             }
35780         }else if(k == e.ESC){
35781             ed.cancelEdit();
35782         }
35783         if(newCell){
35784             g.startEditing(newCell[0], newCell[1]);
35785         }
35786     }
35787 });/*
35788  * Based on:
35789  * Ext JS Library 1.1.1
35790  * Copyright(c) 2006-2007, Ext JS, LLC.
35791  *
35792  * Originally Released Under LGPL - original licence link has changed is not relivant.
35793  *
35794  * Fork - LGPL
35795  * <script type="text/javascript">
35796  */
35797 /**
35798  * @class Roo.grid.CellSelectionModel
35799  * @extends Roo.grid.AbstractSelectionModel
35800  * This class provides the basic implementation for cell selection in a grid.
35801  * @constructor
35802  * @param {Object} config The object containing the configuration of this model.
35803  */
35804 Roo.grid.CellSelectionModel = function(config){
35805     Roo.apply(this, config);
35806
35807     this.selection = null;
35808
35809     this.addEvents({
35810         /**
35811              * @event beforerowselect
35812              * Fires before a cell is selected.
35813              * @param {SelectionModel} this
35814              * @param {Number} rowIndex The selected row index
35815              * @param {Number} colIndex The selected cell index
35816              */
35817             "beforecellselect" : true,
35818         /**
35819              * @event cellselect
35820              * Fires when a cell is selected.
35821              * @param {SelectionModel} this
35822              * @param {Number} rowIndex The selected row index
35823              * @param {Number} colIndex The selected cell index
35824              */
35825             "cellselect" : true,
35826         /**
35827              * @event selectionchange
35828              * Fires when the active selection changes.
35829              * @param {SelectionModel} this
35830              * @param {Object} selection null for no selection or an object (o) with two properties
35831                 <ul>
35832                 <li>o.record: the record object for the row the selection is in</li>
35833                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35834                 </ul>
35835              */
35836             "selectionchange" : true
35837     });
35838     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35839 };
35840
35841 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35842
35843     /** @ignore */
35844     initEvents : function(){
35845         this.grid.on("mousedown", this.handleMouseDown, this);
35846         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35847         var view = this.grid.view;
35848         view.on("refresh", this.onViewChange, this);
35849         view.on("rowupdated", this.onRowUpdated, this);
35850         view.on("beforerowremoved", this.clearSelections, this);
35851         view.on("beforerowsinserted", this.clearSelections, this);
35852         if(this.grid.isEditor){
35853             this.grid.on("beforeedit", this.beforeEdit,  this);
35854         }
35855     },
35856
35857         //private
35858     beforeEdit : function(e){
35859         this.select(e.row, e.column, false, true, e.record);
35860     },
35861
35862         //private
35863     onRowUpdated : function(v, index, r){
35864         if(this.selection && this.selection.record == r){
35865             v.onCellSelect(index, this.selection.cell[1]);
35866         }
35867     },
35868
35869         //private
35870     onViewChange : function(){
35871         this.clearSelections(true);
35872     },
35873
35874         /**
35875          * Returns the currently selected cell,.
35876          * @return {Array} The selected cell (row, column) or null if none selected.
35877          */
35878     getSelectedCell : function(){
35879         return this.selection ? this.selection.cell : null;
35880     },
35881
35882     /**
35883      * Clears all selections.
35884      * @param {Boolean} true to prevent the gridview from being notified about the change.
35885      */
35886     clearSelections : function(preventNotify){
35887         var s = this.selection;
35888         if(s){
35889             if(preventNotify !== true){
35890                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35891             }
35892             this.selection = null;
35893             this.fireEvent("selectionchange", this, null);
35894         }
35895     },
35896
35897     /**
35898      * Returns true if there is a selection.
35899      * @return {Boolean}
35900      */
35901     hasSelection : function(){
35902         return this.selection ? true : false;
35903     },
35904
35905     /** @ignore */
35906     handleMouseDown : function(e, t){
35907         var v = this.grid.getView();
35908         if(this.isLocked()){
35909             return;
35910         };
35911         var row = v.findRowIndex(t);
35912         var cell = v.findCellIndex(t);
35913         if(row !== false && cell !== false){
35914             this.select(row, cell);
35915         }
35916     },
35917
35918     /**
35919      * Selects a cell.
35920      * @param {Number} rowIndex
35921      * @param {Number} collIndex
35922      */
35923     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35924         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35925             this.clearSelections();
35926             r = r || this.grid.dataSource.getAt(rowIndex);
35927             this.selection = {
35928                 record : r,
35929                 cell : [rowIndex, colIndex]
35930             };
35931             if(!preventViewNotify){
35932                 var v = this.grid.getView();
35933                 v.onCellSelect(rowIndex, colIndex);
35934                 if(preventFocus !== true){
35935                     v.focusCell(rowIndex, colIndex);
35936                 }
35937             }
35938             this.fireEvent("cellselect", this, rowIndex, colIndex);
35939             this.fireEvent("selectionchange", this, this.selection);
35940         }
35941     },
35942
35943         //private
35944     isSelectable : function(rowIndex, colIndex, cm){
35945         return !cm.isHidden(colIndex);
35946     },
35947
35948     /** @ignore */
35949     handleKeyDown : function(e){
35950         Roo.log('Cell Sel Model handleKeyDown');
35951         if(!e.isNavKeyPress()){
35952             return;
35953         }
35954         var g = this.grid, s = this.selection;
35955         if(!s){
35956             e.stopEvent();
35957             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35958             if(cell){
35959                 this.select(cell[0], cell[1]);
35960             }
35961             return;
35962         }
35963         var sm = this;
35964         var walk = function(row, col, step){
35965             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35966         };
35967         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35968         var newCell;
35969
35970         switch(k){
35971             case e.TAB:
35972                 // handled by onEditorKey
35973                 if (g.isEditor && g.editing) {
35974                     return;
35975                 }
35976                 if(e.shiftKey){
35977                      newCell = walk(r, c-1, -1);
35978                 }else{
35979                      newCell = walk(r, c+1, 1);
35980                 }
35981              break;
35982              case e.DOWN:
35983                  newCell = walk(r+1, c, 1);
35984              break;
35985              case e.UP:
35986                  newCell = walk(r-1, c, -1);
35987              break;
35988              case e.RIGHT:
35989                  newCell = walk(r, c+1, 1);
35990              break;
35991              case e.LEFT:
35992                  newCell = walk(r, c-1, -1);
35993              break;
35994              case e.ENTER:
35995                  if(g.isEditor && !g.editing){
35996                     g.startEditing(r, c);
35997                     e.stopEvent();
35998                     return;
35999                 }
36000              break;
36001         };
36002         if(newCell){
36003             this.select(newCell[0], newCell[1]);
36004             e.stopEvent();
36005         }
36006     },
36007
36008     acceptsNav : function(row, col, cm){
36009         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36010     },
36011
36012     onEditorKey : function(field, e){
36013         
36014         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36015         ///Roo.log('onEditorKey' + k);
36016         
36017         if(k == e.TAB){
36018             if(e.shiftKey){
36019                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36020             }else{
36021                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36022             }
36023             e.stopEvent();
36024         }else if(k == e.ENTER && !e.ctrlKey){
36025             ed.completeEdit();
36026             e.stopEvent();
36027             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36028         }else if(k == e.ESC){
36029             ed.cancelEdit();
36030         }
36031         
36032         
36033         if(newCell){
36034             //Roo.log('next cell after edit');
36035             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36036         }
36037     }
36038 });/*
36039  * Based on:
36040  * Ext JS Library 1.1.1
36041  * Copyright(c) 2006-2007, Ext JS, LLC.
36042  *
36043  * Originally Released Under LGPL - original licence link has changed is not relivant.
36044  *
36045  * Fork - LGPL
36046  * <script type="text/javascript">
36047  */
36048  
36049 /**
36050  * @class Roo.grid.EditorGrid
36051  * @extends Roo.grid.Grid
36052  * Class for creating and editable grid.
36053  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36054  * The container MUST have some type of size defined for the grid to fill. The container will be 
36055  * automatically set to position relative if it isn't already.
36056  * @param {Object} dataSource The data model to bind to
36057  * @param {Object} colModel The column model with info about this grid's columns
36058  */
36059 Roo.grid.EditorGrid = function(container, config){
36060     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36061     this.getGridEl().addClass("xedit-grid");
36062
36063     if(!this.selModel){
36064         this.selModel = new Roo.grid.CellSelectionModel();
36065     }
36066
36067     this.activeEditor = null;
36068
36069         this.addEvents({
36070             /**
36071              * @event beforeedit
36072              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36073              * <ul style="padding:5px;padding-left:16px;">
36074              * <li>grid - This grid</li>
36075              * <li>record - The record being edited</li>
36076              * <li>field - The field name being edited</li>
36077              * <li>value - The value for the field being edited.</li>
36078              * <li>row - The grid row index</li>
36079              * <li>column - The grid column index</li>
36080              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36081              * </ul>
36082              * @param {Object} e An edit event (see above for description)
36083              */
36084             "beforeedit" : true,
36085             /**
36086              * @event afteredit
36087              * Fires after a cell is edited. <br />
36088              * <ul style="padding:5px;padding-left:16px;">
36089              * <li>grid - This grid</li>
36090              * <li>record - The record being edited</li>
36091              * <li>field - The field name being edited</li>
36092              * <li>value - The value being set</li>
36093              * <li>originalValue - The original value for the field, before the edit.</li>
36094              * <li>row - The grid row index</li>
36095              * <li>column - The grid column index</li>
36096              * </ul>
36097              * @param {Object} e An edit event (see above for description)
36098              */
36099             "afteredit" : true,
36100             /**
36101              * @event validateedit
36102              * Fires after a cell is edited, but before the value is set in the record. 
36103          * You can use this to modify the value being set in the field, Return false
36104              * to cancel the change. The edit event object has the following properties <br />
36105              * <ul style="padding:5px;padding-left:16px;">
36106          * <li>editor - This editor</li>
36107              * <li>grid - This grid</li>
36108              * <li>record - The record being edited</li>
36109              * <li>field - The field name being edited</li>
36110              * <li>value - The value being set</li>
36111              * <li>originalValue - The original value for the field, before the edit.</li>
36112              * <li>row - The grid row index</li>
36113              * <li>column - The grid column index</li>
36114              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36115              * </ul>
36116              * @param {Object} e An edit event (see above for description)
36117              */
36118             "validateedit" : true
36119         });
36120     this.on("bodyscroll", this.stopEditing,  this);
36121     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36122 };
36123
36124 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36125     /**
36126      * @cfg {Number} clicksToEdit
36127      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36128      */
36129     clicksToEdit: 2,
36130
36131     // private
36132     isEditor : true,
36133     // private
36134     trackMouseOver: false, // causes very odd FF errors
36135
36136     onCellDblClick : function(g, row, col){
36137         this.startEditing(row, col);
36138     },
36139
36140     onEditComplete : function(ed, value, startValue){
36141         this.editing = false;
36142         this.activeEditor = null;
36143         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36144         var r = ed.record;
36145         var field = this.colModel.getDataIndex(ed.col);
36146         var e = {
36147             grid: this,
36148             record: r,
36149             field: field,
36150             originalValue: startValue,
36151             value: value,
36152             row: ed.row,
36153             column: ed.col,
36154             cancel:false,
36155             editor: ed
36156         };
36157         if(String(value) !== String(startValue)){
36158             
36159             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36160                 r.set(field, e.value);
36161                 // if we are dealing with a combo box..
36162                 // then we also set the 'name' colum to be the displayField
36163                 if (ed.field.displayField && ed.field.name) {
36164                     r.set(ed.field.name, ed.field.el.dom.value);
36165                 }
36166                 
36167                 delete e.cancel; //?? why!!!
36168                 this.fireEvent("afteredit", e);
36169             }
36170         } else {
36171             this.fireEvent("afteredit", e); // always fire it!
36172         }
36173         this.view.focusCell(ed.row, ed.col);
36174     },
36175
36176     /**
36177      * Starts editing the specified for the specified row/column
36178      * @param {Number} rowIndex
36179      * @param {Number} colIndex
36180      */
36181     startEditing : function(row, col){
36182         this.stopEditing();
36183         if(this.colModel.isCellEditable(col, row)){
36184             this.view.ensureVisible(row, col, true);
36185             var r = this.dataSource.getAt(row);
36186             var field = this.colModel.getDataIndex(col);
36187             var e = {
36188                 grid: this,
36189                 record: r,
36190                 field: field,
36191                 value: r.data[field],
36192                 row: row,
36193                 column: col,
36194                 cancel:false
36195             };
36196             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36197                 this.editing = true;
36198                 var ed = this.colModel.getCellEditor(col, row);
36199                 
36200                 if (!ed) {
36201                     return;
36202                 }
36203                 if(!ed.rendered){
36204                     ed.render(ed.parentEl || document.body);
36205                 }
36206                 ed.field.reset();
36207                 (function(){ // complex but required for focus issues in safari, ie and opera
36208                     ed.row = row;
36209                     ed.col = col;
36210                     ed.record = r;
36211                     ed.on("complete", this.onEditComplete, this, {single: true});
36212                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36213                     this.activeEditor = ed;
36214                     var v = r.data[field];
36215                     ed.startEdit(this.view.getCell(row, col), v);
36216                     // combo's with 'displayField and name set
36217                     if (ed.field.displayField && ed.field.name) {
36218                         ed.field.el.dom.value = r.data[ed.field.name];
36219                     }
36220                     
36221                     
36222                 }).defer(50, this);
36223             }
36224         }
36225     },
36226         
36227     /**
36228      * Stops any active editing
36229      */
36230     stopEditing : function(){
36231         if(this.activeEditor){
36232             this.activeEditor.completeEdit();
36233         }
36234         this.activeEditor = null;
36235     }
36236 });/*
36237  * Based on:
36238  * Ext JS Library 1.1.1
36239  * Copyright(c) 2006-2007, Ext JS, LLC.
36240  *
36241  * Originally Released Under LGPL - original licence link has changed is not relivant.
36242  *
36243  * Fork - LGPL
36244  * <script type="text/javascript">
36245  */
36246
36247 // private - not really -- you end up using it !
36248 // This is a support class used internally by the Grid components
36249
36250 /**
36251  * @class Roo.grid.GridEditor
36252  * @extends Roo.Editor
36253  * Class for creating and editable grid elements.
36254  * @param {Object} config any settings (must include field)
36255  */
36256 Roo.grid.GridEditor = function(field, config){
36257     if (!config && field.field) {
36258         config = field;
36259         field = Roo.factory(config.field, Roo.form);
36260     }
36261     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36262     field.monitorTab = false;
36263 };
36264
36265 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36266     
36267     /**
36268      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36269      */
36270     
36271     alignment: "tl-tl",
36272     autoSize: "width",
36273     hideEl : false,
36274     cls: "x-small-editor x-grid-editor",
36275     shim:false,
36276     shadow:"frame"
36277 });/*
36278  * Based on:
36279  * Ext JS Library 1.1.1
36280  * Copyright(c) 2006-2007, Ext JS, LLC.
36281  *
36282  * Originally Released Under LGPL - original licence link has changed is not relivant.
36283  *
36284  * Fork - LGPL
36285  * <script type="text/javascript">
36286  */
36287   
36288
36289   
36290 Roo.grid.PropertyRecord = Roo.data.Record.create([
36291     {name:'name',type:'string'},  'value'
36292 ]);
36293
36294
36295 Roo.grid.PropertyStore = function(grid, source){
36296     this.grid = grid;
36297     this.store = new Roo.data.Store({
36298         recordType : Roo.grid.PropertyRecord
36299     });
36300     this.store.on('update', this.onUpdate,  this);
36301     if(source){
36302         this.setSource(source);
36303     }
36304     Roo.grid.PropertyStore.superclass.constructor.call(this);
36305 };
36306
36307
36308
36309 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36310     setSource : function(o){
36311         this.source = o;
36312         this.store.removeAll();
36313         var data = [];
36314         for(var k in o){
36315             if(this.isEditableValue(o[k])){
36316                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36317             }
36318         }
36319         this.store.loadRecords({records: data}, {}, true);
36320     },
36321
36322     onUpdate : function(ds, record, type){
36323         if(type == Roo.data.Record.EDIT){
36324             var v = record.data['value'];
36325             var oldValue = record.modified['value'];
36326             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36327                 this.source[record.id] = v;
36328                 record.commit();
36329                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36330             }else{
36331                 record.reject();
36332             }
36333         }
36334     },
36335
36336     getProperty : function(row){
36337        return this.store.getAt(row);
36338     },
36339
36340     isEditableValue: function(val){
36341         if(val && val instanceof Date){
36342             return true;
36343         }else if(typeof val == 'object' || typeof val == 'function'){
36344             return false;
36345         }
36346         return true;
36347     },
36348
36349     setValue : function(prop, value){
36350         this.source[prop] = value;
36351         this.store.getById(prop).set('value', value);
36352     },
36353
36354     getSource : function(){
36355         return this.source;
36356     }
36357 });
36358
36359 Roo.grid.PropertyColumnModel = function(grid, store){
36360     this.grid = grid;
36361     var g = Roo.grid;
36362     g.PropertyColumnModel.superclass.constructor.call(this, [
36363         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36364         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36365     ]);
36366     this.store = store;
36367     this.bselect = Roo.DomHelper.append(document.body, {
36368         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36369             {tag: 'option', value: 'true', html: 'true'},
36370             {tag: 'option', value: 'false', html: 'false'}
36371         ]
36372     });
36373     Roo.id(this.bselect);
36374     var f = Roo.form;
36375     this.editors = {
36376         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36377         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36378         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36379         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36380         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36381     };
36382     this.renderCellDelegate = this.renderCell.createDelegate(this);
36383     this.renderPropDelegate = this.renderProp.createDelegate(this);
36384 };
36385
36386 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36387     
36388     
36389     nameText : 'Name',
36390     valueText : 'Value',
36391     
36392     dateFormat : 'm/j/Y',
36393     
36394     
36395     renderDate : function(dateVal){
36396         return dateVal.dateFormat(this.dateFormat);
36397     },
36398
36399     renderBool : function(bVal){
36400         return bVal ? 'true' : 'false';
36401     },
36402
36403     isCellEditable : function(colIndex, rowIndex){
36404         return colIndex == 1;
36405     },
36406
36407     getRenderer : function(col){
36408         return col == 1 ?
36409             this.renderCellDelegate : this.renderPropDelegate;
36410     },
36411
36412     renderProp : function(v){
36413         return this.getPropertyName(v);
36414     },
36415
36416     renderCell : function(val){
36417         var rv = val;
36418         if(val instanceof Date){
36419             rv = this.renderDate(val);
36420         }else if(typeof val == 'boolean'){
36421             rv = this.renderBool(val);
36422         }
36423         return Roo.util.Format.htmlEncode(rv);
36424     },
36425
36426     getPropertyName : function(name){
36427         var pn = this.grid.propertyNames;
36428         return pn && pn[name] ? pn[name] : name;
36429     },
36430
36431     getCellEditor : function(colIndex, rowIndex){
36432         var p = this.store.getProperty(rowIndex);
36433         var n = p.data['name'], val = p.data['value'];
36434         
36435         if(typeof(this.grid.customEditors[n]) == 'string'){
36436             return this.editors[this.grid.customEditors[n]];
36437         }
36438         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36439             return this.grid.customEditors[n];
36440         }
36441         if(val instanceof Date){
36442             return this.editors['date'];
36443         }else if(typeof val == 'number'){
36444             return this.editors['number'];
36445         }else if(typeof val == 'boolean'){
36446             return this.editors['boolean'];
36447         }else{
36448             return this.editors['string'];
36449         }
36450     }
36451 });
36452
36453 /**
36454  * @class Roo.grid.PropertyGrid
36455  * @extends Roo.grid.EditorGrid
36456  * This class represents the  interface of a component based property grid control.
36457  * <br><br>Usage:<pre><code>
36458  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36459       
36460  });
36461  // set any options
36462  grid.render();
36463  * </code></pre>
36464   
36465  * @constructor
36466  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36467  * The container MUST have some type of size defined for the grid to fill. The container will be
36468  * automatically set to position relative if it isn't already.
36469  * @param {Object} config A config object that sets properties on this grid.
36470  */
36471 Roo.grid.PropertyGrid = function(container, config){
36472     config = config || {};
36473     var store = new Roo.grid.PropertyStore(this);
36474     this.store = store;
36475     var cm = new Roo.grid.PropertyColumnModel(this, store);
36476     store.store.sort('name', 'ASC');
36477     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36478         ds: store.store,
36479         cm: cm,
36480         enableColLock:false,
36481         enableColumnMove:false,
36482         stripeRows:false,
36483         trackMouseOver: false,
36484         clicksToEdit:1
36485     }, config));
36486     this.getGridEl().addClass('x-props-grid');
36487     this.lastEditRow = null;
36488     this.on('columnresize', this.onColumnResize, this);
36489     this.addEvents({
36490          /**
36491              * @event beforepropertychange
36492              * Fires before a property changes (return false to stop?)
36493              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36494              * @param {String} id Record Id
36495              * @param {String} newval New Value
36496          * @param {String} oldval Old Value
36497              */
36498         "beforepropertychange": true,
36499         /**
36500              * @event propertychange
36501              * Fires after a property changes
36502              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36503              * @param {String} id Record Id
36504              * @param {String} newval New Value
36505          * @param {String} oldval Old Value
36506              */
36507         "propertychange": true
36508     });
36509     this.customEditors = this.customEditors || {};
36510 };
36511 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36512     
36513      /**
36514      * @cfg {Object} customEditors map of colnames=> custom editors.
36515      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36516      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36517      * false disables editing of the field.
36518          */
36519     
36520       /**
36521      * @cfg {Object} propertyNames map of property Names to their displayed value
36522          */
36523     
36524     render : function(){
36525         Roo.grid.PropertyGrid.superclass.render.call(this);
36526         this.autoSize.defer(100, this);
36527     },
36528
36529     autoSize : function(){
36530         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36531         if(this.view){
36532             this.view.fitColumns();
36533         }
36534     },
36535
36536     onColumnResize : function(){
36537         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36538         this.autoSize();
36539     },
36540     /**
36541      * Sets the data for the Grid
36542      * accepts a Key => Value object of all the elements avaiable.
36543      * @param {Object} data  to appear in grid.
36544      */
36545     setSource : function(source){
36546         this.store.setSource(source);
36547         //this.autoSize();
36548     },
36549     /**
36550      * Gets all the data from the grid.
36551      * @return {Object} data  data stored in grid
36552      */
36553     getSource : function(){
36554         return this.store.getSource();
36555     }
36556 });/*
36557  * Based on:
36558  * Ext JS Library 1.1.1
36559  * Copyright(c) 2006-2007, Ext JS, LLC.
36560  *
36561  * Originally Released Under LGPL - original licence link has changed is not relivant.
36562  *
36563  * Fork - LGPL
36564  * <script type="text/javascript">
36565  */
36566  
36567 /**
36568  * @class Roo.LoadMask
36569  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36570  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36571  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36572  * element's UpdateManager load indicator and will be destroyed after the initial load.
36573  * @constructor
36574  * Create a new LoadMask
36575  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36576  * @param {Object} config The config object
36577  */
36578 Roo.LoadMask = function(el, config){
36579     this.el = Roo.get(el);
36580     Roo.apply(this, config);
36581     if(this.store){
36582         this.store.on('beforeload', this.onBeforeLoad, this);
36583         this.store.on('load', this.onLoad, this);
36584         this.store.on('loadexception', this.onLoad, this);
36585         this.removeMask = false;
36586     }else{
36587         var um = this.el.getUpdateManager();
36588         um.showLoadIndicator = false; // disable the default indicator
36589         um.on('beforeupdate', this.onBeforeLoad, this);
36590         um.on('update', this.onLoad, this);
36591         um.on('failure', this.onLoad, this);
36592         this.removeMask = true;
36593     }
36594 };
36595
36596 Roo.LoadMask.prototype = {
36597     /**
36598      * @cfg {Boolean} removeMask
36599      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36600      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36601      */
36602     /**
36603      * @cfg {String} msg
36604      * The text to display in a centered loading message box (defaults to 'Loading...')
36605      */
36606     msg : 'Loading...',
36607     /**
36608      * @cfg {String} msgCls
36609      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36610      */
36611     msgCls : 'x-mask-loading',
36612
36613     /**
36614      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36615      * @type Boolean
36616      */
36617     disabled: false,
36618
36619     /**
36620      * Disables the mask to prevent it from being displayed
36621      */
36622     disable : function(){
36623        this.disabled = true;
36624     },
36625
36626     /**
36627      * Enables the mask so that it can be displayed
36628      */
36629     enable : function(){
36630         this.disabled = false;
36631     },
36632
36633     // private
36634     onLoad : function(){
36635         this.el.unmask(this.removeMask);
36636     },
36637
36638     // private
36639     onBeforeLoad : function(){
36640         if(!this.disabled){
36641             this.el.mask(this.msg, this.msgCls);
36642         }
36643     },
36644
36645     // private
36646     destroy : function(){
36647         if(this.store){
36648             this.store.un('beforeload', this.onBeforeLoad, this);
36649             this.store.un('load', this.onLoad, this);
36650             this.store.un('loadexception', this.onLoad, this);
36651         }else{
36652             var um = this.el.getUpdateManager();
36653             um.un('beforeupdate', this.onBeforeLoad, this);
36654             um.un('update', this.onLoad, this);
36655             um.un('failure', this.onLoad, this);
36656         }
36657     }
36658 };/*
36659  * Based on:
36660  * Ext JS Library 1.1.1
36661  * Copyright(c) 2006-2007, Ext JS, LLC.
36662  *
36663  * Originally Released Under LGPL - original licence link has changed is not relivant.
36664  *
36665  * Fork - LGPL
36666  * <script type="text/javascript">
36667  */
36668 Roo.XTemplate = function(){
36669     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36670     var s = this.html;
36671
36672     s = ['<tpl>', s, '</tpl>'].join('');
36673
36674     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36675
36676     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36677     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36678     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36679     var m, id = 0;
36680     var tpls = [];
36681
36682     while(m = s.match(re)){
36683        var m2 = m[0].match(nameRe);
36684        var m3 = m[0].match(ifRe);
36685        var m4 = m[0].match(execRe);
36686        var exp = null, fn = null, exec = null;
36687        var name = m2 && m2[1] ? m2[1] : '';
36688        if(m3){
36689            exp = m3 && m3[1] ? m3[1] : null;
36690            if(exp){
36691                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36692            }
36693        }
36694        if(m4){
36695            exp = m4 && m4[1] ? m4[1] : null;
36696            if(exp){
36697                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36698            }
36699        }
36700        if(name){
36701            switch(name){
36702                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36703                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36704                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36705            }
36706        }
36707        tpls.push({
36708             id: id,
36709             target: name,
36710             exec: exec,
36711             test: fn,
36712             body: m[1]||''
36713         });
36714        s = s.replace(m[0], '{xtpl'+ id + '}');
36715        ++id;
36716     }
36717     for(var i = tpls.length-1; i >= 0; --i){
36718         this.compileTpl(tpls[i]);
36719     }
36720     this.master = tpls[tpls.length-1];
36721     this.tpls = tpls;
36722 };
36723 Roo.extend(Roo.XTemplate, Roo.Template, {
36724
36725     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36726
36727     applySubTemplate : function(id, values, parent){
36728         var t = this.tpls[id];
36729         if(t.test && !t.test.call(this, values, parent)){
36730             return '';
36731         }
36732         if(t.exec && t.exec.call(this, values, parent)){
36733             return '';
36734         }
36735         var vs = t.target ? t.target.call(this, values, parent) : values;
36736         parent = t.target ? values : parent;
36737         if(t.target && vs instanceof Array){
36738             var buf = [];
36739             for(var i = 0, len = vs.length; i < len; i++){
36740                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36741             }
36742             return buf.join('');
36743         }
36744         return t.compiled.call(this, vs, parent);
36745     },
36746
36747     compileTpl : function(tpl){
36748         var fm = Roo.util.Format;
36749         var useF = this.disableFormats !== true;
36750         var sep = Roo.isGecko ? "+" : ",";
36751         var fn = function(m, name, format, args){
36752             if(name.substr(0, 4) == 'xtpl'){
36753                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36754             }
36755             var v;
36756             if(name.indexOf('.') != -1){
36757                 v = name;
36758             }else{
36759                 v = "values['" + name + "']";
36760             }
36761             if(format && useF){
36762                 args = args ? ',' + args : "";
36763                 if(format.substr(0, 5) != "this."){
36764                     format = "fm." + format + '(';
36765                 }else{
36766                     format = 'this.call("'+ format.substr(5) + '", ';
36767                     args = ", values";
36768                 }
36769             }else{
36770                 args= ''; format = "("+v+" === undefined ? '' : ";
36771             }
36772             return "'"+ sep + format + v + args + ")"+sep+"'";
36773         };
36774         var body;
36775         // branched to use + in gecko and [].join() in others
36776         if(Roo.isGecko){
36777             body = "tpl.compiled = function(values, parent){ return '" +
36778                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36779                     "';};";
36780         }else{
36781             body = ["tpl.compiled = function(values, parent){ return ['"];
36782             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36783             body.push("'].join('');};");
36784             body = body.join('');
36785         }
36786         /** eval:var:zzzzzzz */
36787         eval(body);
36788         return this;
36789     },
36790
36791     applyTemplate : function(values){
36792         return this.master.compiled.call(this, values, {});
36793         var s = this.subs;
36794     },
36795
36796     apply : function(){
36797         return this.applyTemplate.apply(this, arguments);
36798     },
36799
36800     compile : function(){return this;}
36801 });
36802
36803 Roo.XTemplate.from = function(el){
36804     el = Roo.getDom(el);
36805     return new Roo.XTemplate(el.value || el.innerHTML);
36806 };/*
36807  * Original code for Roojs - LGPL
36808  * <script type="text/javascript">
36809  */
36810  
36811 /**
36812  * @class Roo.XComponent
36813  * A delayed Element creator...
36814  * Or a way to group chunks of interface together.
36815  * 
36816  * Mypart.xyx = new Roo.XComponent({
36817
36818     parent : 'Mypart.xyz', // empty == document.element.!!
36819     order : '001',
36820     name : 'xxxx'
36821     region : 'xxxx'
36822     disabled : function() {} 
36823      
36824     tree : function() { // return an tree of xtype declared components
36825         var MODULE = this;
36826         return 
36827         {
36828             xtype : 'NestedLayoutPanel',
36829             // technicall
36830         }
36831      ]
36832  *})
36833  *
36834  *
36835  * It can be used to build a big heiracy, with parent etc.
36836  * or you can just use this to render a single compoent to a dom element
36837  * MYPART.render(Roo.Element | String(id) | dom_element )
36838  * 
36839  * @extends Roo.util.Observable
36840  * @constructor
36841  * @param cfg {Object} configuration of component
36842  * 
36843  */
36844 Roo.XComponent = function(cfg) {
36845     Roo.apply(this, cfg);
36846     this.addEvents({ 
36847         /**
36848              * @event built
36849              * Fires when this the componnt is built
36850              * @param {Roo.XComponent} c the component
36851              */
36852         'built' : true,
36853         /**
36854              * @event buildcomplete
36855              * Fires on the top level element when all elements have been built
36856              * @param {Roo.XComponent} c the top level component.
36857          */
36858         'buildcomplete' : true
36859         
36860     });
36861     this.region = this.region || 'center'; // default..
36862     Roo.XComponent.register(this);
36863     this.modules = false;
36864     this.el = false; // where the layout goes..
36865     
36866     
36867 }
36868 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36869     /**
36870      * @property el
36871      * The created element (with Roo.factory())
36872      * @type {Roo.Layout}
36873      */
36874     el  : false,
36875     
36876     /**
36877      * @property el
36878      * for BC  - use el in new code
36879      * @type {Roo.Layout}
36880      */
36881     panel : false,
36882     
36883     /**
36884      * @property layout
36885      * for BC  - use el in new code
36886      * @type {Roo.Layout}
36887      */
36888     layout : false,
36889     
36890      /**
36891      * @cfg {Function|boolean} disabled
36892      * If this module is disabled by some rule, return true from the funtion
36893      */
36894     disabled : false,
36895     
36896     /**
36897      * @cfg {String} parent 
36898      * Name of parent element which it get xtype added to..
36899      */
36900     parent: false,
36901     
36902     /**
36903      * @cfg {String} order
36904      * Used to set the order in which elements are created (usefull for multiple tabs)
36905      */
36906     
36907     order : false,
36908     /**
36909      * @cfg {String} name
36910      * String to display while loading.
36911      */
36912     name : false,
36913     /**
36914      * @cfg {String} region
36915      * Region to render component to (defaults to center)
36916      */
36917     region : 'center',
36918     
36919     /**
36920      * @cfg {Array} items
36921      * A single item array - the first element is the root of the tree..
36922      * It's done this way to stay compatible with the Xtype system...
36923      */
36924     items : false,
36925     
36926     
36927      /**
36928      * render
36929      * render element to dom or tree
36930      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36931      */
36932     
36933     render : function(el)
36934     {
36935         
36936         el = el || false;
36937         
36938         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
36939             // if parent is a '#.....' string, then let's use that..
36940             var ename = this.parent.substr(1)
36941             this.parent = false;
36942             el = Roo.get(ename);
36943             if (!el) {
36944                 Roo.log("Warning - element can not be found :#" + ename );
36945                 return;
36946             }
36947         }
36948         if (!this.parent) {
36949             
36950             el = el ? Roo.get(el) : false;
36951             
36952             // it's a top level one..
36953             this.parent =  {
36954                 el : new Roo.BorderLayout(el || document.body, {
36955                 
36956                      center: {
36957                          titlebar: false,
36958                          autoScroll:false,
36959                          closeOnTab: true,
36960                          tabPosition: 'top',
36961                           //resizeTabs: true,
36962                          alwaysShowTabs: el ? false :  true,
36963                          hideTabs: el ? true :  false,
36964                          minTabWidth: 140
36965                      }
36966                  })
36967             }
36968         }
36969         
36970         
36971             
36972         var tree = this.tree();
36973         tree.region = tree.region || this.region;
36974         this.el = this.parent.el.addxtype(tree);
36975         this.fireEvent('built', this);
36976         
36977         this.panel = this.el;
36978         this.layout = this.panel.layout;    
36979          
36980     }
36981     
36982 });
36983
36984 Roo.apply(Roo.XComponent, {
36985     
36986     /**
36987      * @property  buildCompleted
36988      * True when the builder has completed building the interface.
36989      * @type Boolean
36990      */
36991     buildCompleted : false,
36992      
36993     /**
36994      * @property  topModule
36995      * the upper most module - uses document.element as it's constructor.
36996      * @type Object
36997      */
36998      
36999     topModule  : false,
37000       
37001     /**
37002      * @property  modules
37003      * array of modules to be created by registration system.
37004      * @type {Array} of Roo.XComponent
37005      */
37006     
37007     modules : [],
37008     /**
37009      * @property  elmodules
37010      * array of modules to be created by which use #ID 
37011      * @type {Array} of Roo.XComponent
37012      */
37013      
37014     elmodules : [],
37015
37016     
37017     /**
37018      * Register components to be built later.
37019      *
37020      * This solves the following issues
37021      * - Building is not done on page load, but after an authentication process has occured.
37022      * - Interface elements are registered on page load
37023      * - Parent Interface elements may not be loaded before child, so this handles that..
37024      * 
37025      *
37026      * example:
37027      * 
37028      * MyApp.register({
37029           order : '000001',
37030           module : 'Pman.Tab.projectMgr',
37031           region : 'center',
37032           parent : 'Pman.layout',
37033           disabled : false,  // or use a function..
37034         })
37035      
37036      * * @param {Object} details about module
37037      */
37038     register : function(obj) {
37039         this.modules.push(obj);
37040          
37041     },
37042     /**
37043      * convert a string to an object..
37044      * eg. 'AAA.BBB' -> finds AAA.BBB
37045
37046      */
37047     
37048     toObject : function(str)
37049     {
37050         if (!str || typeof(str) == 'object') {
37051             return str;
37052         }
37053         if (str[0]=='#') {
37054             return str;
37055         }
37056
37057         var ar = str.split('.');
37058         var rt, o;
37059         rt = ar.shift();
37060             /** eval:var:o */
37061         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37062         if (o === false) {
37063             throw "Module not found : " + str;
37064         }
37065         Roo.each(ar, function(e) {
37066             if (typeof(o[e]) == 'undefined') {
37067                 throw "Module not found : " + str;
37068             }
37069             o = o[e];
37070         });
37071         return o;
37072         
37073     },
37074     
37075     
37076     /**
37077      * move modules into their correct place in the tree..
37078      * 
37079      */
37080     preBuild : function ()
37081     {
37082         
37083         Roo.each(this.modules , function (obj)
37084         {
37085             obj.parent = this.toObject(obj.parent);
37086             
37087             if (!obj.parent) {
37088                 this.topModule = obj;
37089                 return;
37090             }
37091             if (typeof(obj.parent) == 'string') {
37092                 this.elmodules.push(obj);
37093                 return;
37094             }
37095             
37096             if (!obj.parent.modules) {
37097                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37098                     function(o) { return o.order + '' }
37099                 );
37100             }
37101             
37102             obj.parent.modules.add(obj);
37103         }, this);
37104     },
37105     
37106      /**
37107      * make a list of modules to build.
37108      * @return {Array} list of modules. 
37109      */ 
37110     
37111     buildOrder : function()
37112     {
37113         var _this = this;
37114         var cmp = function(a,b) {   
37115             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37116         };
37117         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37118             throw "No top level modules to build";
37119         }
37120         
37121         // make a flat list in order of modules to build.
37122         var mods = this.topModule ? [ this.topModule ] : [];
37123         Roo.each(this.elmodules,function(e) { mods.push(e) });
37124
37125         
37126         // add modules to their parents..
37127         var addMod = function(m) {
37128            // Roo.debug && Roo.log(m.modKey);
37129             
37130             mods.push(m);
37131             if (m.modules) {
37132                 m.modules.keySort('ASC',  cmp );
37133                 m.modules.each(addMod);
37134             }
37135             // not sure if this is used any more..
37136             if (m.finalize) {
37137                 m.finalize.name = m.name + " (clean up) ";
37138                 mods.push(m.finalize);
37139             }
37140             
37141         }
37142         if (this.topModule) { 
37143             this.topModule.modules.keySort('ASC',  cmp );
37144             this.topModule.modules.each(addMod);
37145         }
37146         return mods;
37147     },
37148     
37149      /**
37150      * Build the registered modules.
37151      * @param {Object} parent element.
37152      * @param {Function} optional method to call after module has been added.
37153      * 
37154      */ 
37155    
37156     build : function() 
37157     {
37158         
37159         this.preBuild();
37160         var mods = this.buildOrder();
37161       
37162         //this.allmods = mods;
37163         //Roo.debug && Roo.log(mods);
37164         //return;
37165         if (!mods.length) { // should not happen
37166             throw "NO modules!!!";
37167         }
37168         
37169         
37170         
37171         // flash it up as modal - so we store the mask!?
37172         Roo.MessageBox.show({ title: 'loading' });
37173         Roo.MessageBox.show({
37174            title: "Please wait...",
37175            msg: "Building Interface...",
37176            width:450,
37177            progress:true,
37178            closable:false,
37179            modal: false
37180           
37181         });
37182         var total = mods.length;
37183         
37184         var _this = this;
37185         var progressRun = function() {
37186             if (!mods.length) {
37187                 Roo.debug && Roo.log('hide?');
37188                 Roo.MessageBox.hide();
37189                 if (_this.topModule) { 
37190                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37191                 }
37192                 // THE END...
37193                 return false;   
37194             }
37195             
37196             var m = mods.shift();
37197             
37198             
37199             Roo.debug && Roo.log(m);
37200             // not sure if this is supported any more.. - modules that are are just function
37201             if (typeof(m) == 'function') { 
37202                 m.call(this);
37203                 return progressRun.defer(10, _this);
37204             } 
37205             
37206             
37207             
37208             Roo.MessageBox.updateProgress(
37209                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37210                     " of " + total + 
37211                     (m.name ? (' - ' + m.name) : '')
37212                     );
37213             
37214          
37215             // is the module disabled?
37216             var disabled = (typeof(m.disabled) == 'function') ?
37217                 m.disabled.call(m.module.disabled) : m.disabled;    
37218             
37219             
37220             if (disabled) {
37221                 return progressRun(); // we do not update the display!
37222             }
37223             
37224             // now build 
37225             
37226             m.render();
37227             // it's 10 on top level, and 1 on others??? why...
37228             return progressRun.defer(10, _this);
37229              
37230         }
37231         progressRun.defer(1, _this);
37232      
37233         
37234         
37235     }
37236     
37237      
37238    
37239     
37240     
37241 });
37242  //<script type="text/javascript">
37243
37244
37245 /**
37246  * @class Roo.Login
37247  * @extends Roo.LayoutDialog
37248  * A generic Login Dialog..... - only one needed in theory!?!?
37249  *
37250  * Fires XComponent builder on success...
37251  * 
37252  * Sends 
37253  *    username,password, lang = for login actions.
37254  *    check = 1 for periodic checking that sesion is valid.
37255  *    passwordRequest = email request password
37256  *    logout = 1 = to logout
37257  * 
37258  * Affects: (this id="????" elements)
37259  *   loading  (removed) (used to indicate application is loading)
37260  *   loading-mask (hides) (used to hide application when it's building loading)
37261  *   
37262  * 
37263  * Usage: 
37264  *    
37265  * 
37266  * Myapp.login = Roo.Login({
37267      url: xxxx,
37268    
37269      realm : 'Myapp', 
37270      
37271      
37272      method : 'POST',
37273      
37274      
37275      * 
37276  })
37277  * 
37278  * 
37279  * 
37280  **/
37281  
37282 Roo.Login = function(cfg)
37283 {
37284     this.addEvents({
37285         'refreshed' : true
37286     });
37287     
37288     Roo.apply(this,cfg);
37289     
37290     Roo.onReady(function() {
37291         this.onLoad();
37292     }, this);
37293     // call parent..
37294     
37295    
37296     Roo.Login.superclass.constructor.call(this, this);
37297     //this.addxtype(this.items[0]);
37298     
37299     
37300 }
37301
37302
37303 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37304     
37305     /**
37306      * @cfg {String} method
37307      * Method used to query for login details.
37308      */
37309     
37310     method : 'POST',
37311     /**
37312      * @cfg {String} url
37313      * URL to query login data. - eg. baseURL + '/Login.php'
37314      */
37315     url : '',
37316     
37317     /**
37318      * @property user
37319      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37320      * @type {Object} 
37321      */
37322     user : false,
37323     /**
37324      * @property checkFails
37325      * Number of times we have attempted to get authentication check, and failed.
37326      * @type {Number} 
37327      */
37328     checkFails : 0,
37329       /**
37330      * @property intervalID
37331      * The window interval that does the constant login checking.
37332      * @type {Number} 
37333      */
37334     intervalID : 0,
37335     
37336     
37337     onLoad : function() // called on page load...
37338     {
37339         // load 
37340          
37341         if (Roo.get('loading')) { // clear any loading indicator..
37342             Roo.get('loading').remove();
37343         }
37344         
37345         //this.switchLang('en'); // set the language to english..
37346        
37347         this.check({
37348             success:  function(response, opts)  {  // check successfull...
37349             
37350                 var res = this.processResponse(response);
37351                 this.checkFails =0;
37352                 if (!res.success) { // error!
37353                     this.checkFails = 5;
37354                     //console.log('call failure');
37355                     return this.failure(response,opts);
37356                 }
37357                 
37358                 if (!res.data.id) { // id=0 == login failure.
37359                     return this.show();
37360                 }
37361                 
37362                               
37363                         //console.log(success);
37364                 this.fillAuth(res.data);   
37365                 this.checkFails =0;
37366                 Roo.XComponent.build();
37367             },
37368             failure : this.show
37369         });
37370         
37371     }, 
37372     
37373     
37374     check: function(cfg) // called every so often to refresh cookie etc..
37375     {
37376         if (cfg.again) { // could be undefined..
37377             this.checkFails++;
37378         } else {
37379             this.checkFails = 0;
37380         }
37381         var _this = this;
37382         if (this.sending) {
37383             if ( this.checkFails > 4) {
37384                 Roo.MessageBox.alert("Error",  
37385                     "Error getting authentication status. - try reloading, or wait a while", function() {
37386                         _this.sending = false;
37387                     }); 
37388                 return;
37389             }
37390             cfg.again = true;
37391             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37392             return;
37393         }
37394         this.sending = true;
37395         
37396         Roo.Ajax.request({  
37397             url: this.url,
37398             params: {
37399                 getAuthUser: true
37400             },  
37401             method: this.method,
37402             success:  cfg.success || this.success,
37403             failure : cfg.failure || this.failure,
37404             scope : this,
37405             callCfg : cfg
37406               
37407         });  
37408     }, 
37409     
37410     
37411     logout: function()
37412     {
37413         window.onbeforeunload = function() { }; // false does not work for IE..
37414         this.user = false;
37415         var _this = this;
37416         
37417         Roo.Ajax.request({  
37418             url: this.url,
37419             params: {
37420                 logout: 1
37421             },  
37422             method: 'GET',
37423             failure : function() {
37424                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37425                     document.location = document.location.toString() + '?ts=' + Math.random();
37426                 });
37427                 
37428             },
37429             success : function() {
37430                 _this.user = false;
37431                 this.checkFails =0;
37432                 // fixme..
37433                 document.location = document.location.toString() + '?ts=' + Math.random();
37434             }
37435               
37436               
37437         }); 
37438     },
37439     
37440     processResponse : function (response)
37441     {
37442         var res = '';
37443         try {
37444             res = Roo.decode(response.responseText);
37445             // oops...
37446             if (typeof(res) != 'object') {
37447                 res = { success : false, errorMsg : res, errors : true };
37448             }
37449             if (typeof(res.success) == 'undefined') {
37450                 res.success = false;
37451             }
37452             
37453         } catch(e) {
37454             res = { success : false,  errorMsg : response.responseText, errors : true };
37455         }
37456         return res;
37457     },
37458     
37459     success : function(response, opts)  // check successfull...
37460     {  
37461         this.sending = false;
37462         var res = this.processResponse(response);
37463         if (!res.success) {
37464             return this.failure(response, opts);
37465         }
37466         if (!res.data || !res.data.id) {
37467             return this.failure(response,opts);
37468         }
37469         //console.log(res);
37470         this.fillAuth(res.data);
37471         
37472         this.checkFails =0;
37473         
37474     },
37475     
37476     
37477     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37478     {
37479         this.authUser = -1;
37480         this.sending = false;
37481         var res = this.processResponse(response);
37482         //console.log(res);
37483         if ( this.checkFails > 2) {
37484         
37485             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37486                 "Error getting authentication status. - try reloading"); 
37487             return;
37488         }
37489         opts.callCfg.again = true;
37490         this.check.defer(1000, this, [ opts.callCfg ]);
37491         return;  
37492     },
37493     
37494     
37495     
37496     fillAuth: function(au) {
37497         this.startAuthCheck();
37498         this.authUserId = au.id;
37499         this.authUser = au;
37500         this.lastChecked = new Date();
37501         this.fireEvent('refreshed', au);
37502         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37503         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37504         au.lang = au.lang || 'en';
37505         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37506         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37507         this.switchLang(au.lang );
37508         
37509      
37510         // open system... - -on setyp..
37511         if (this.authUserId  < 0) {
37512             Roo.MessageBox.alert("Warning", 
37513                 "This is an open system - please set up a admin user with a password.");  
37514         }
37515          
37516         //Pman.onload(); // which should do nothing if it's a re-auth result...
37517         
37518              
37519     },
37520     
37521     startAuthCheck : function() // starter for timeout checking..
37522     {
37523         if (this.intervalID) { // timer already in place...
37524             return false;
37525         }
37526         var _this = this;
37527         this.intervalID =  window.setInterval(function() {
37528               _this.check(false);
37529             }, 120000); // every 120 secs = 2mins..
37530         
37531         
37532     },
37533          
37534     
37535     switchLang : function (lang) 
37536     {
37537         _T = typeof(_T) == 'undefined' ? false : _T;
37538           if (!_T || !lang.length) {
37539             return;
37540         }
37541         
37542         if (!_T && lang != 'en') {
37543             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37544             return;
37545         }
37546         
37547         if (typeof(_T.en) == 'undefined') {
37548             _T.en = {};
37549             Roo.apply(_T.en, _T);
37550         }
37551         
37552         if (typeof(_T[lang]) == 'undefined') {
37553             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37554             return;
37555         }
37556         
37557         
37558         Roo.apply(_T, _T[lang]);
37559         // just need to set the text values for everything...
37560         var _this = this;
37561         /* this will not work ...
37562         if (this.form) { 
37563             
37564                
37565             function formLabel(name, val) {
37566                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37567             }
37568             
37569             formLabel('password', "Password"+':');
37570             formLabel('username', "Email Address"+':');
37571             formLabel('lang', "Language"+':');
37572             this.dialog.setTitle("Login");
37573             this.dialog.buttons[0].setText("Forgot Password");
37574             this.dialog.buttons[1].setText("Login");
37575         }
37576         */
37577         
37578         
37579     },
37580     
37581     
37582     title: "Login",
37583     modal: true,
37584     width:  350,
37585     //height: 230,
37586     height: 180,
37587     shadow: true,
37588     minWidth:200,
37589     minHeight:180,
37590     //proxyDrag: true,
37591     closable: false,
37592     draggable: false,
37593     collapsible: false,
37594     resizable: false,
37595     center: {  // needed??
37596         autoScroll:false,
37597         titlebar: false,
37598        // tabPosition: 'top',
37599         hideTabs: true,
37600         closeOnTab: true,
37601         alwaysShowTabs: false
37602     } ,
37603     listeners : {
37604         
37605         show  : function(dlg)
37606         {
37607             //console.log(this);
37608             this.form = this.layout.getRegion('center').activePanel.form;
37609             this.form.dialog = dlg;
37610             this.buttons[0].form = this.form;
37611             this.buttons[0].dialog = dlg;
37612             this.buttons[1].form = this.form;
37613             this.buttons[1].dialog = dlg;
37614            
37615            //this.resizeToLogo.defer(1000,this);
37616             // this is all related to resizing for logos..
37617             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37618            //// if (!sz) {
37619              //   this.resizeToLogo.defer(1000,this);
37620              //   return;
37621            // }
37622             //var w = Ext.lib.Dom.getViewWidth() - 100;
37623             //var h = Ext.lib.Dom.getViewHeight() - 100;
37624             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37625             //this.center();
37626             if (this.disabled) {
37627                 this.hide();
37628                 return;
37629             }
37630             
37631             if (this.user.id < 0) { // used for inital setup situations.
37632                 return;
37633             }
37634             
37635             if (this.intervalID) {
37636                 // remove the timer
37637                 window.clearInterval(this.intervalID);
37638                 this.intervalID = false;
37639             }
37640             
37641             
37642             if (Roo.get('loading')) {
37643                 Roo.get('loading').remove();
37644             }
37645             if (Roo.get('loading-mask')) {
37646                 Roo.get('loading-mask').hide();
37647             }
37648             
37649             //incomming._node = tnode;
37650             this.form.reset();
37651             //this.dialog.modal = !modal;
37652             //this.dialog.show();
37653             this.el.unmask(); 
37654             
37655             
37656             this.form.setValues({
37657                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37658                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37659             });
37660             
37661             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37662             if (this.form.findField('username').getValue().length > 0 ){
37663                 this.form.findField('password').focus();
37664             } else {
37665                this.form.findField('username').focus();
37666             }
37667     
37668         }
37669     },
37670     items : [
37671          {
37672        
37673             xtype : 'ContentPanel',
37674             xns : Roo,
37675             region: 'center',
37676             fitToFrame : true,
37677             
37678             items : [
37679     
37680                 {
37681                
37682                     xtype : 'Form',
37683                     xns : Roo.form,
37684                     labelWidth: 100,
37685                     style : 'margin: 10px;',
37686                     
37687                     listeners : {
37688                         actionfailed : function(f, act) {
37689                             // form can return { errors: .... }
37690                                 
37691                             //act.result.errors // invalid form element list...
37692                             //act.result.errorMsg// invalid form element list...
37693                             
37694                             this.dialog.el.unmask();
37695                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37696                                         "Login failed - communication error - try again.");
37697                                       
37698                         },
37699                         actioncomplete: function(re, act) {
37700                              
37701                             Roo.state.Manager.set(
37702                                 this.dialog.realm + '.username',  
37703                                     this.findField('username').getValue()
37704                             );
37705                             Roo.state.Manager.set(
37706                                 this.dialog.realm + '.lang',  
37707                                 this.findField('lang').getValue() 
37708                             );
37709                             
37710                             this.dialog.fillAuth(act.result.data);
37711                               
37712                             this.dialog.hide();
37713                             
37714                             if (Roo.get('loading-mask')) {
37715                                 Roo.get('loading-mask').show();
37716                             }
37717                             Roo.XComponent.build();
37718                             
37719                              
37720                             
37721                         }
37722                     },
37723                     items : [
37724                         {
37725                             xtype : 'TextField',
37726                             xns : Roo.form,
37727                             fieldLabel: "Email Address",
37728                             name: 'username',
37729                             width:200,
37730                             autoCreate : {tag: "input", type: "text", size: "20"}
37731                         },
37732                         {
37733                             xtype : 'TextField',
37734                             xns : Roo.form,
37735                             fieldLabel: "Password",
37736                             inputType: 'password',
37737                             name: 'password',
37738                             width:200,
37739                             autoCreate : {tag: "input", type: "text", size: "20"},
37740                             listeners : {
37741                                 specialkey : function(e,ev) {
37742                                     if (ev.keyCode == 13) {
37743                                         this.form.dialog.el.mask("Logging in");
37744                                         this.form.doAction('submit', {
37745                                             url: this.form.dialog.url,
37746                                             method: this.form.dialog.method
37747                                         });
37748                                     }
37749                                 }
37750                             }  
37751                         },
37752                         {
37753                             xtype : 'ComboBox',
37754                             xns : Roo.form,
37755                             fieldLabel: "Language",
37756                             name : 'langdisp',
37757                             store: {
37758                                 xtype : 'SimpleStore',
37759                                 fields: ['lang', 'ldisp'],
37760                                 data : [
37761                                     [ 'en', 'English' ],
37762                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37763                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37764                                 ]
37765                             },
37766                             
37767                             valueField : 'lang',
37768                             hiddenName:  'lang',
37769                             width: 200,
37770                             displayField:'ldisp',
37771                             typeAhead: false,
37772                             editable: false,
37773                             mode: 'local',
37774                             triggerAction: 'all',
37775                             emptyText:'Select a Language...',
37776                             selectOnFocus:true,
37777                             listeners : {
37778                                 select :  function(cb, rec, ix) {
37779                                     this.form.switchLang(rec.data.lang);
37780                                 }
37781                             }
37782                         
37783                         }
37784                     ]
37785                 }
37786                   
37787                 
37788             ]
37789         }
37790     ],
37791     buttons : [
37792         {
37793             xtype : 'Button',
37794             xns : 'Roo',
37795             text : "Forgot Password",
37796             listeners : {
37797                 click : function() {
37798                     //console.log(this);
37799                     var n = this.form.findField('username').getValue();
37800                     if (!n.length) {
37801                         Roo.MessageBox.alert("Error", "Fill in your email address");
37802                         return;
37803                     }
37804                     Roo.Ajax.request({
37805                         url: this.dialog.url,
37806                         params: {
37807                             passwordRequest: n
37808                         },
37809                         method: this.dialog.method,
37810                         success:  function(response, opts)  {  // check successfull...
37811                         
37812                             var res = this.dialog.processResponse(response);
37813                             if (!res.success) { // error!
37814                                Roo.MessageBox.alert("Error" ,
37815                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37816                                return;
37817                             }
37818                             Roo.MessageBox.alert("Notice" ,
37819                                 "Please check you email for the Password Reset message");
37820                         },
37821                         failure : function() {
37822                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37823                         }
37824                         
37825                     });
37826                 }
37827             }
37828         },
37829         {
37830             xtype : 'Button',
37831             xns : 'Roo',
37832             text : "Login",
37833             listeners : {
37834                 
37835                 click : function () {
37836                         
37837                     this.dialog.el.mask("Logging in");
37838                     this.form.doAction('submit', {
37839                             url: this.dialog.url,
37840                             method: this.dialog.method
37841                     });
37842                 }
37843             }
37844         }
37845     ]
37846   
37847   
37848 })
37849  
37850
37851
37852