roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data)
4019     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5064             return;
5065         }
5066         var r = o.records, t = o.totalRecords || r.length;
5067         if(!options || options.add !== true){
5068             if(this.pruneModifiedRecords){
5069                 this.modified = [];
5070             }
5071             for(var i = 0, len = r.length; i < len; i++){
5072                 r[i].join(this);
5073             }
5074             if(this.snapshot){
5075                 this.data = this.snapshot;
5076                 delete this.snapshot;
5077             }
5078             this.data.clear();
5079             this.data.addAll(r);
5080             this.totalLength = t;
5081             this.applySort();
5082             this.fireEvent("datachanged", this);
5083         }else{
5084             this.totalLength = Math.max(t, this.data.length+r.length);
5085             this.add(r);
5086         }
5087         this.fireEvent("load", this, r, options);
5088         if(options.callback){
5089             options.callback.call(options.scope || this, r, options, true);
5090         }
5091     },
5092
5093     /**
5094      * Loads data from a passed data block. A Reader which understands the format of the data
5095      * must have been configured in the constructor.
5096      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5097      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5098      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5099      */
5100     loadData : function(o, append){
5101         var r = this.reader.readRecords(o);
5102         this.loadRecords(r, {add: append}, true);
5103     },
5104
5105     /**
5106      * Gets the number of cached records.
5107      * <p>
5108      * <em>If using paging, this may not be the total size of the dataset. If the data object
5109      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5110      * the data set size</em>
5111      */
5112     getCount : function(){
5113         return this.data.length || 0;
5114     },
5115
5116     /**
5117      * Gets the total number of records in the dataset as returned by the server.
5118      * <p>
5119      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5120      * the dataset size</em>
5121      */
5122     getTotalCount : function(){
5123         return this.totalLength || 0;
5124     },
5125
5126     /**
5127      * Returns the sort state of the Store as an object with two properties:
5128      * <pre><code>
5129  field {String} The name of the field by which the Records are sorted
5130  direction {String} The sort order, "ASC" or "DESC"
5131      * </code></pre>
5132      */
5133     getSortState : function(){
5134         return this.sortInfo;
5135     },
5136
5137     // private
5138     applySort : function(){
5139         if(this.sortInfo && !this.remoteSort){
5140             var s = this.sortInfo, f = s.field;
5141             var st = this.fields.get(f).sortType;
5142             var fn = function(r1, r2){
5143                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5144                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5145             };
5146             this.data.sort(s.direction, fn);
5147             if(this.snapshot && this.snapshot != this.data){
5148                 this.snapshot.sort(s.direction, fn);
5149             }
5150         }
5151     },
5152
5153     /**
5154      * Sets the default sort column and order to be used by the next load operation.
5155      * @param {String} fieldName The name of the field to sort by.
5156      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5157      */
5158     setDefaultSort : function(field, dir){
5159         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5160     },
5161
5162     /**
5163      * Sort the Records.
5164      * If remote sorting is used, the sort is performed on the server, and the cache is
5165      * reloaded. If local sorting is used, the cache is sorted internally.
5166      * @param {String} fieldName The name of the field to sort by.
5167      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5168      */
5169     sort : function(fieldName, dir){
5170         var f = this.fields.get(fieldName);
5171         if(!dir){
5172             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5173             
5174             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5175                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5176             }else{
5177                 dir = f.sortDir;
5178             }
5179         }
5180         this.sortToggle[f.name] = dir;
5181         this.sortInfo = {field: f.name, direction: dir};
5182         if(!this.remoteSort){
5183             this.applySort();
5184             this.fireEvent("datachanged", this);
5185         }else{
5186             this.load(this.lastOptions);
5187         }
5188     },
5189
5190     /**
5191      * Calls the specified function for each of the Records in the cache.
5192      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5193      * Returning <em>false</em> aborts and exits the iteration.
5194      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5195      */
5196     each : function(fn, scope){
5197         this.data.each(fn, scope);
5198     },
5199
5200     /**
5201      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5202      * (e.g., during paging).
5203      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5204      */
5205     getModifiedRecords : function(){
5206         return this.modified;
5207     },
5208
5209     // private
5210     createFilterFn : function(property, value, anyMatch){
5211         if(!value.exec){ // not a regex
5212             value = String(value);
5213             if(value.length == 0){
5214                 return false;
5215             }
5216             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5217         }
5218         return function(r){
5219             return value.test(r.data[property]);
5220         };
5221     },
5222
5223     /**
5224      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5225      * @param {String} property A field on your records
5226      * @param {Number} start The record index to start at (defaults to 0)
5227      * @param {Number} end The last record index to include (defaults to length - 1)
5228      * @return {Number} The sum
5229      */
5230     sum : function(property, start, end){
5231         var rs = this.data.items, v = 0;
5232         start = start || 0;
5233         end = (end || end === 0) ? end : rs.length-1;
5234
5235         for(var i = start; i <= end; i++){
5236             v += (rs[i].data[property] || 0);
5237         }
5238         return v;
5239     },
5240
5241     /**
5242      * Filter the records by a specified property.
5243      * @param {String} field A field on your records
5244      * @param {String/RegExp} value Either a string that the field
5245      * should start with or a RegExp to test against the field
5246      * @param {Boolean} anyMatch True to match any part not just the beginning
5247      */
5248     filter : function(property, value, anyMatch){
5249         var fn = this.createFilterFn(property, value, anyMatch);
5250         return fn ? this.filterBy(fn) : this.clearFilter();
5251     },
5252
5253     /**
5254      * Filter by a function. The specified function will be called with each
5255      * record in this data source. If the function returns true the record is included,
5256      * otherwise it is filtered.
5257      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5258      * @param {Object} scope (optional) The scope of the function (defaults to this)
5259      */
5260     filterBy : function(fn, scope){
5261         this.snapshot = this.snapshot || this.data;
5262         this.data = this.queryBy(fn, scope||this);
5263         this.fireEvent("datachanged", this);
5264     },
5265
5266     /**
5267      * Query the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5273      */
5274     query : function(property, value, anyMatch){
5275         var fn = this.createFilterFn(property, value, anyMatch);
5276         return fn ? this.queryBy(fn) : this.data.clone();
5277     },
5278
5279     /**
5280      * Query by a function. The specified function will be called with each
5281      * record in this data source. If the function returns true the record is included
5282      * in the results.
5283      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5284      * @param {Object} scope (optional) The scope of the function (defaults to this)
5285       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5286      **/
5287     queryBy : function(fn, scope){
5288         var data = this.snapshot || this.data;
5289         return data.filterBy(fn, scope||this);
5290     },
5291
5292     /**
5293      * Collects unique values for a particular dataIndex from this store.
5294      * @param {String} dataIndex The property to collect
5295      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5296      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5297      * @return {Array} An array of the unique values
5298      **/
5299     collect : function(dataIndex, allowNull, bypassFilter){
5300         var d = (bypassFilter === true && this.snapshot) ?
5301                 this.snapshot.items : this.data.items;
5302         var v, sv, r = [], l = {};
5303         for(var i = 0, len = d.length; i < len; i++){
5304             v = d[i].data[dataIndex];
5305             sv = String(v);
5306             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5307                 l[sv] = true;
5308                 r[r.length] = v;
5309             }
5310         }
5311         return r;
5312     },
5313
5314     /**
5315      * Revert to a view of the Record cache with no filtering applied.
5316      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5317      */
5318     clearFilter : function(suppressEvent){
5319         if(this.snapshot && this.snapshot != this.data){
5320             this.data = this.snapshot;
5321             delete this.snapshot;
5322             if(suppressEvent !== true){
5323                 this.fireEvent("datachanged", this);
5324             }
5325         }
5326     },
5327
5328     // private
5329     afterEdit : function(record){
5330         if(this.modified.indexOf(record) == -1){
5331             this.modified.push(record);
5332         }
5333         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5334     },
5335
5336     // private
5337     afterReject : function(record){
5338         this.modified.remove(record);
5339         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5340     },
5341
5342     // private
5343     afterCommit : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5346     },
5347
5348     /**
5349      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5350      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5351      */
5352     commitChanges : function(){
5353         var m = this.modified.slice(0);
5354         this.modified = [];
5355         for(var i = 0, len = m.length; i < len; i++){
5356             m[i].commit();
5357         }
5358     },
5359
5360     /**
5361      * Cancel outstanding changes on all changed records.
5362      */
5363     rejectChanges : function(){
5364         var m = this.modified.slice(0);
5365         this.modified = [];
5366         for(var i = 0, len = m.length; i < len; i++){
5367             m[i].reject();
5368         }
5369     },
5370
5371     onMetaChange : function(meta, rtype, o){
5372         this.recordType = rtype;
5373         this.fields = rtype.prototype.fields;
5374         delete this.snapshot;
5375         this.sortInfo = meta.sortInfo || this.sortInfo;
5376         this.modified = [];
5377         this.fireEvent('metachange', this, this.reader.meta);
5378     }
5379 });/*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389
5390 /**
5391  * @class Roo.data.SimpleStore
5392  * @extends Roo.data.Store
5393  * Small helper class to make creating Stores from Array data easier.
5394  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5395  * @cfg {Array} fields An array of field definition objects, or field name strings.
5396  * @cfg {Array} data The multi-dimensional array of data
5397  * @constructor
5398  * @param {Object} config
5399  */
5400 Roo.data.SimpleStore = function(config){
5401     Roo.data.SimpleStore.superclass.constructor.call(this, {
5402         isLocal : true,
5403         reader: new Roo.data.ArrayReader({
5404                 id: config.id
5405             },
5406             Roo.data.Record.create(config.fields)
5407         ),
5408         proxy : new Roo.data.MemoryProxy(config.data)
5409     });
5410     this.load();
5411 };
5412 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5413  * Based on:
5414  * Ext JS Library 1.1.1
5415  * Copyright(c) 2006-2007, Ext JS, LLC.
5416  *
5417  * Originally Released Under LGPL - original licence link has changed is not relivant.
5418  *
5419  * Fork - LGPL
5420  * <script type="text/javascript">
5421  */
5422
5423 /**
5424 /**
5425  * @extends Roo.data.Store
5426  * @class Roo.data.JsonStore
5427  * Small helper class to make creating Stores for JSON data easier. <br/>
5428 <pre><code>
5429 var store = new Roo.data.JsonStore({
5430     url: 'get-images.php',
5431     root: 'images',
5432     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5433 });
5434 </code></pre>
5435  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5436  * JsonReader and HttpProxy (unless inline data is provided).</b>
5437  * @cfg {Array} fields An array of field definition objects, or field name strings.
5438  * @constructor
5439  * @param {Object} config
5440  */
5441 Roo.data.JsonStore = function(c){
5442     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5443         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5444         reader: new Roo.data.JsonReader(c, c.fields)
5445     }));
5446 };
5447 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5448  * Based on:
5449  * Ext JS Library 1.1.1
5450  * Copyright(c) 2006-2007, Ext JS, LLC.
5451  *
5452  * Originally Released Under LGPL - original licence link has changed is not relivant.
5453  *
5454  * Fork - LGPL
5455  * <script type="text/javascript">
5456  */
5457
5458  
5459 Roo.data.Field = function(config){
5460     if(typeof config == "string"){
5461         config = {name: config};
5462     }
5463     Roo.apply(this, config);
5464     
5465     if(!this.type){
5466         this.type = "auto";
5467     }
5468     
5469     var st = Roo.data.SortTypes;
5470     // named sortTypes are supported, here we look them up
5471     if(typeof this.sortType == "string"){
5472         this.sortType = st[this.sortType];
5473     }
5474     
5475     // set default sortType for strings and dates
5476     if(!this.sortType){
5477         switch(this.type){
5478             case "string":
5479                 this.sortType = st.asUCString;
5480                 break;
5481             case "date":
5482                 this.sortType = st.asDate;
5483                 break;
5484             default:
5485                 this.sortType = st.none;
5486         }
5487     }
5488
5489     // define once
5490     var stripRe = /[\$,%]/g;
5491
5492     // prebuilt conversion function for this field, instead of
5493     // switching every time we're reading a value
5494     if(!this.convert){
5495         var cv, dateFormat = this.dateFormat;
5496         switch(this.type){
5497             case "":
5498             case "auto":
5499             case undefined:
5500                 cv = function(v){ return v; };
5501                 break;
5502             case "string":
5503                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5504                 break;
5505             case "int":
5506                 cv = function(v){
5507                     return v !== undefined && v !== null && v !== '' ?
5508                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5509                     };
5510                 break;
5511             case "float":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5515                     };
5516                 break;
5517             case "bool":
5518             case "boolean":
5519                 cv = function(v){ return v === true || v === "true" || v == 1; };
5520                 break;
5521             case "date":
5522                 cv = function(v){
5523                     if(!v){
5524                         return '';
5525                     }
5526                     if(v instanceof Date){
5527                         return v;
5528                     }
5529                     if(dateFormat){
5530                         if(dateFormat == "timestamp"){
5531                             return new Date(v*1000);
5532                         }
5533                         return Date.parseDate(v, dateFormat);
5534                     }
5535                     var parsed = Date.parse(v);
5536                     return parsed ? new Date(parsed) : null;
5537                 };
5538              break;
5539             
5540         }
5541         this.convert = cv;
5542     }
5543 };
5544
5545 Roo.data.Field.prototype = {
5546     dateFormat: null,
5547     defaultValue: "",
5548     mapping: null,
5549     sortType : null,
5550     sortDir : "ASC"
5551 };/*
5552  * Based on:
5553  * Ext JS Library 1.1.1
5554  * Copyright(c) 2006-2007, Ext JS, LLC.
5555  *
5556  * Originally Released Under LGPL - original licence link has changed is not relivant.
5557  *
5558  * Fork - LGPL
5559  * <script type="text/javascript">
5560  */
5561  
5562 // Base class for reading structured data from a data source.  This class is intended to be
5563 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5564
5565 /**
5566  * @class Roo.data.DataReader
5567  * Base class for reading structured data from a data source.  This class is intended to be
5568  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5569  */
5570
5571 Roo.data.DataReader = function(meta, recordType){
5572     
5573     this.meta = meta;
5574     
5575     this.recordType = recordType instanceof Array ? 
5576         Roo.data.Record.create(recordType) : recordType;
5577 };
5578
5579 Roo.data.DataReader.prototype = {
5580      /**
5581      * Create an empty record
5582      * @param {Object} data (optional) - overlay some values
5583      * @return {Roo.data.Record} record created.
5584      */
5585     newRow :  function(d) {
5586         var da =  {};
5587         this.recordType.prototype.fields.each(function(c) {
5588             switch( c.type) {
5589                 case 'int' : da[c.name] = 0; break;
5590                 case 'date' : da[c.name] = new Date(); break;
5591                 case 'float' : da[c.name] = 0.0; break;
5592                 case 'boolean' : da[c.name] = false; break;
5593                 default : da[c.name] = ""; break;
5594             }
5595             
5596         });
5597         return new this.recordType(Roo.apply(da, d));
5598     }
5599     
5600 };/*
5601  * Based on:
5602  * Ext JS Library 1.1.1
5603  * Copyright(c) 2006-2007, Ext JS, LLC.
5604  *
5605  * Originally Released Under LGPL - original licence link has changed is not relivant.
5606  *
5607  * Fork - LGPL
5608  * <script type="text/javascript">
5609  */
5610
5611 /**
5612  * @class Roo.data.DataProxy
5613  * @extends Roo.data.Observable
5614  * This class is an abstract base class for implementations which provide retrieval of
5615  * unformatted data objects.<br>
5616  * <p>
5617  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5618  * (of the appropriate type which knows how to parse the data object) to provide a block of
5619  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5620  * <p>
5621  * Custom implementations must implement the load method as described in
5622  * {@link Roo.data.HttpProxy#load}.
5623  */
5624 Roo.data.DataProxy = function(){
5625     this.addEvents({
5626         /**
5627          * @event beforeload
5628          * Fires before a network request is made to retrieve a data object.
5629          * @param {Object} This DataProxy object.
5630          * @param {Object} params The params parameter to the load function.
5631          */
5632         beforeload : true,
5633         /**
5634          * @event load
5635          * Fires before the load method's callback is called.
5636          * @param {Object} This DataProxy object.
5637          * @param {Object} o The data object.
5638          * @param {Object} arg The callback argument object passed to the load function.
5639          */
5640         load : true,
5641         /**
5642          * @event loadexception
5643          * Fires if an Exception occurs during data retrieval.
5644          * @param {Object} This DataProxy object.
5645          * @param {Object} o The data object.
5646          * @param {Object} arg The callback argument object passed to the load function.
5647          * @param {Object} e The Exception.
5648          */
5649         loadexception : true
5650     });
5651     Roo.data.DataProxy.superclass.constructor.call(this);
5652 };
5653
5654 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5655
5656     /**
5657      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5658      */
5659 /*
5660  * Based on:
5661  * Ext JS Library 1.1.1
5662  * Copyright(c) 2006-2007, Ext JS, LLC.
5663  *
5664  * Originally Released Under LGPL - original licence link has changed is not relivant.
5665  *
5666  * Fork - LGPL
5667  * <script type="text/javascript">
5668  */
5669 /**
5670  * @class Roo.data.MemoryProxy
5671  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5672  * to the Reader when its load method is called.
5673  * @constructor
5674  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5675  */
5676 Roo.data.MemoryProxy = function(data){
5677     if (data.data) {
5678         data = data.data;
5679     }
5680     Roo.data.MemoryProxy.superclass.constructor.call(this);
5681     this.data = data;
5682 };
5683
5684 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5685     /**
5686      * Load data from the requested source (in this case an in-memory
5687      * data object passed to the constructor), read the data object into
5688      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5689      * process that block using the passed callback.
5690      * @param {Object} params This parameter is not used by the MemoryProxy class.
5691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5692      * object into a block of Roo.data.Records.
5693      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5694      * The function must be passed <ul>
5695      * <li>The Record block object</li>
5696      * <li>The "arg" argument from the load function</li>
5697      * <li>A boolean success indicator</li>
5698      * </ul>
5699      * @param {Object} scope The scope in which to call the callback
5700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5701      */
5702     load : function(params, reader, callback, scope, arg){
5703         params = params || {};
5704         var result;
5705         try {
5706             result = reader.readRecords(this.data);
5707         }catch(e){
5708             this.fireEvent("loadexception", this, arg, null, e);
5709             callback.call(scope, null, arg, false);
5710             return;
5711         }
5712         callback.call(scope, result, arg, true);
5713     },
5714     
5715     // private
5716     update : function(params, records){
5717         
5718     }
5719 });/*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729 /**
5730  * @class Roo.data.HttpProxy
5731  * @extends Roo.data.DataProxy
5732  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5733  * configured to reference a certain URL.<br><br>
5734  * <p>
5735  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5736  * from which the running page was served.<br><br>
5737  * <p>
5738  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5739  * <p>
5740  * Be aware that to enable the browser to parse an XML document, the server must set
5741  * the Content-Type header in the HTTP response to "text/xml".
5742  * @constructor
5743  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5744  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5745  * will be used to make the request.
5746  */
5747 Roo.data.HttpProxy = function(conn){
5748     Roo.data.HttpProxy.superclass.constructor.call(this);
5749     // is conn a conn config or a real conn?
5750     this.conn = conn;
5751     this.useAjax = !conn || !conn.events;
5752   
5753 };
5754
5755 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5756     // thse are take from connection...
5757     
5758     /**
5759      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5760      */
5761     /**
5762      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5763      * extra parameters to each request made by this object. (defaults to undefined)
5764      */
5765     /**
5766      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5767      *  to each request made by this object. (defaults to undefined)
5768      */
5769     /**
5770      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5771      */
5772     /**
5773      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5774      */
5775      /**
5776      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5777      * @type Boolean
5778      */
5779   
5780
5781     /**
5782      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5783      * @type Boolean
5784      */
5785     /**
5786      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5787      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5788      * a finer-grained basis than the DataProxy events.
5789      */
5790     getConnection : function(){
5791         return this.useAjax ? Roo.Ajax : this.conn;
5792     },
5793
5794     /**
5795      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5796      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5797      * process that block using the passed callback.
5798      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5799      * for the request to the remote server.
5800      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5801      * object into a block of Roo.data.Records.
5802      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5803      * The function must be passed <ul>
5804      * <li>The Record block object</li>
5805      * <li>The "arg" argument from the load function</li>
5806      * <li>A boolean success indicator</li>
5807      * </ul>
5808      * @param {Object} scope The scope in which to call the callback
5809      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5810      */
5811     load : function(params, reader, callback, scope, arg){
5812         if(this.fireEvent("beforeload", this, params) !== false){
5813             var  o = {
5814                 params : params || {},
5815                 request: {
5816                     callback : callback,
5817                     scope : scope,
5818                     arg : arg
5819                 },
5820                 reader: reader,
5821                 callback : this.loadResponse,
5822                 scope: this
5823             };
5824             if(this.useAjax){
5825                 Roo.applyIf(o, this.conn);
5826                 if(this.activeRequest){
5827                     Roo.Ajax.abort(this.activeRequest);
5828                 }
5829                 this.activeRequest = Roo.Ajax.request(o);
5830             }else{
5831                 this.conn.request(o);
5832             }
5833         }else{
5834             callback.call(scope||this, null, arg, false);
5835         }
5836     },
5837
5838     // private
5839     loadResponse : function(o, success, response){
5840         delete this.activeRequest;
5841         if(!success){
5842             this.fireEvent("loadexception", this, o, response);
5843             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5844             return;
5845         }
5846         var result;
5847         try {
5848             result = o.reader.read(response);
5849         }catch(e){
5850             this.fireEvent("loadexception", this, o, response, e);
5851             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5852             return;
5853         }
5854         
5855         this.fireEvent("load", this, o, o.request.arg);
5856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5857     },
5858
5859     // private
5860     update : function(dataSet){
5861
5862     },
5863
5864     // private
5865     updateResponse : function(dataSet){
5866
5867     }
5868 });/*
5869  * Based on:
5870  * Ext JS Library 1.1.1
5871  * Copyright(c) 2006-2007, Ext JS, LLC.
5872  *
5873  * Originally Released Under LGPL - original licence link has changed is not relivant.
5874  *
5875  * Fork - LGPL
5876  * <script type="text/javascript">
5877  */
5878
5879 /**
5880  * @class Roo.data.ScriptTagProxy
5881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5882  * other than the originating domain of the running page.<br><br>
5883  * <p>
5884  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5886  * <p>
5887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5888  * source code that is used as the source inside a &lt;script> tag.<br><br>
5889  * <p>
5890  * In order for the browser to process the returned data, the server must wrap the data object
5891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5893  * depending on whether the callback name was passed:
5894  * <p>
5895  * <pre><code>
5896 boolean scriptTag = false;
5897 String cb = request.getParameter("callback");
5898 if (cb != null) {
5899     scriptTag = true;
5900     response.setContentType("text/javascript");
5901 } else {
5902     response.setContentType("application/x-json");
5903 }
5904 Writer out = response.getWriter();
5905 if (scriptTag) {
5906     out.write(cb + "(");
5907 }
5908 out.print(dataBlock.toJsonString());
5909 if (scriptTag) {
5910     out.write(");");
5911 }
5912 </pre></code>
5913  *
5914  * @constructor
5915  * @param {Object} config A configuration object.
5916  */
5917 Roo.data.ScriptTagProxy = function(config){
5918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5919     Roo.apply(this, config);
5920     this.head = document.getElementsByTagName("head")[0];
5921 };
5922
5923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5924
5925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5926     /**
5927      * @cfg {String} url The URL from which to request the data object.
5928      */
5929     /**
5930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5931      */
5932     timeout : 30000,
5933     /**
5934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5935      * the server the name of the callback function set up by the load call to process the returned data object.
5936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5937      * javascript output which calls this named function passing the data object as its only parameter.
5938      */
5939     callbackParam : "callback",
5940     /**
5941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5942      * name to the request.
5943      */
5944     nocache : true,
5945
5946     /**
5947      * Load data from the configured URL, read the data object into
5948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5949      * process that block using the passed callback.
5950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5951      * for the request to the remote server.
5952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5953      * object into a block of Roo.data.Records.
5954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5955      * The function must be passed <ul>
5956      * <li>The Record block object</li>
5957      * <li>The "arg" argument from the load function</li>
5958      * <li>A boolean success indicator</li>
5959      * </ul>
5960      * @param {Object} scope The scope in which to call the callback
5961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5962      */
5963     load : function(params, reader, callback, scope, arg){
5964         if(this.fireEvent("beforeload", this, params) !== false){
5965
5966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5967
5968             var url = this.url;
5969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5970             if(this.nocache){
5971                 url += "&_dc=" + (new Date().getTime());
5972             }
5973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5974             var trans = {
5975                 id : transId,
5976                 cb : "stcCallback"+transId,
5977                 scriptId : "stcScript"+transId,
5978                 params : params,
5979                 arg : arg,
5980                 url : url,
5981                 callback : callback,
5982                 scope : scope,
5983                 reader : reader
5984             };
5985             var conn = this;
5986
5987             window[trans.cb] = function(o){
5988                 conn.handleResponse(o, trans);
5989             };
5990
5991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5992
5993             if(this.autoAbort !== false){
5994                 this.abort();
5995             }
5996
5997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5998
5999             var script = document.createElement("script");
6000             script.setAttribute("src", url);
6001             script.setAttribute("type", "text/javascript");
6002             script.setAttribute("id", trans.scriptId);
6003             this.head.appendChild(script);
6004
6005             this.trans = trans;
6006         }else{
6007             callback.call(scope||this, null, arg, false);
6008         }
6009     },
6010
6011     // private
6012     isLoading : function(){
6013         return this.trans ? true : false;
6014     },
6015
6016     /**
6017      * Abort the current server request.
6018      */
6019     abort : function(){
6020         if(this.isLoading()){
6021             this.destroyTrans(this.trans);
6022         }
6023     },
6024
6025     // private
6026     destroyTrans : function(trans, isLoaded){
6027         this.head.removeChild(document.getElementById(trans.scriptId));
6028         clearTimeout(trans.timeoutId);
6029         if(isLoaded){
6030             window[trans.cb] = undefined;
6031             try{
6032                 delete window[trans.cb];
6033             }catch(e){}
6034         }else{
6035             // if hasn't been loaded, wait for load to remove it to prevent script error
6036             window[trans.cb] = function(){
6037                 window[trans.cb] = undefined;
6038                 try{
6039                     delete window[trans.cb];
6040                 }catch(e){}
6041             };
6042         }
6043     },
6044
6045     // private
6046     handleResponse : function(o, trans){
6047         this.trans = false;
6048         this.destroyTrans(trans, true);
6049         var result;
6050         try {
6051             result = trans.reader.readRecords(o);
6052         }catch(e){
6053             this.fireEvent("loadexception", this, o, trans.arg, e);
6054             trans.callback.call(trans.scope||window, null, trans.arg, false);
6055             return;
6056         }
6057         this.fireEvent("load", this, o, trans.arg);
6058         trans.callback.call(trans.scope||window, result, trans.arg, true);
6059     },
6060
6061     // private
6062     handleFailure : function(trans){
6063         this.trans = false;
6064         this.destroyTrans(trans, false);
6065         this.fireEvent("loadexception", this, null, trans.arg);
6066         trans.callback.call(trans.scope||window, null, trans.arg, false);
6067     }
6068 });/*
6069  * Based on:
6070  * Ext JS Library 1.1.1
6071  * Copyright(c) 2006-2007, Ext JS, LLC.
6072  *
6073  * Originally Released Under LGPL - original licence link has changed is not relivant.
6074  *
6075  * Fork - LGPL
6076  * <script type="text/javascript">
6077  */
6078
6079 /**
6080  * @class Roo.data.JsonReader
6081  * @extends Roo.data.DataReader
6082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6083  * based on mappings in a provided Roo.data.Record constructor.
6084  * 
6085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6086  * in the reply previously. 
6087  * 
6088  * <p>
6089  * Example code:
6090  * <pre><code>
6091 var RecordDef = Roo.data.Record.create([
6092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6094 ]);
6095 var myReader = new Roo.data.JsonReader({
6096     totalProperty: "results",    // The property which contains the total dataset size (optional)
6097     root: "rows",                // The property which contains an Array of row objects
6098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6099 }, RecordDef);
6100 </code></pre>
6101  * <p>
6102  * This would consume a JSON file like this:
6103  * <pre><code>
6104 { 'results': 2, 'rows': [
6105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6107 }
6108 </code></pre>
6109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6111  * paged from the remote server.
6112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6113  * @cfg {String} root name of the property which contains the Array of row objects.
6114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6115  * @constructor
6116  * Create a new JsonReader
6117  * @param {Object} meta Metadata configuration options
6118  * @param {Object} recordType Either an Array of field definition objects,
6119  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6120  */
6121 Roo.data.JsonReader = function(meta, recordType){
6122     
6123     meta = meta || {};
6124     // set some defaults:
6125     Roo.applyIf(meta, {
6126         totalProperty: 'total',
6127         successProperty : 'success',
6128         root : 'data',
6129         id : 'id'
6130     });
6131     
6132     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6133 };
6134 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6135     
6136     /**
6137      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6138      * Used by Store query builder to append _requestMeta to params.
6139      * 
6140      */
6141     metaFromRemote : false,
6142     /**
6143      * This method is only used by a DataProxy which has retrieved data from a remote server.
6144      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6145      * @return {Object} data A data block which is used by an Roo.data.Store object as
6146      * a cache of Roo.data.Records.
6147      */
6148     read : function(response){
6149         var json = response.responseText;
6150        
6151         var o = /* eval:var:o */ eval("("+json+")");
6152         if(!o) {
6153             throw {message: "JsonReader.read: Json object not found"};
6154         }
6155         
6156         if(o.metaData){
6157             
6158             delete this.ef;
6159             this.metaFromRemote = true;
6160             this.meta = o.metaData;
6161             this.recordType = Roo.data.Record.create(o.metaData.fields);
6162             this.onMetaChange(this.meta, this.recordType, o);
6163         }
6164         return this.readRecords(o);
6165     },
6166
6167     // private function a store will implement
6168     onMetaChange : function(meta, recordType, o){
6169
6170     },
6171
6172     /**
6173          * @ignore
6174          */
6175     simpleAccess: function(obj, subsc) {
6176         return obj[subsc];
6177     },
6178
6179         /**
6180          * @ignore
6181          */
6182     getJsonAccessor: function(){
6183         var re = /[\[\.]/;
6184         return function(expr) {
6185             try {
6186                 return(re.test(expr))
6187                     ? new Function("obj", "return obj." + expr)
6188                     : function(obj){
6189                         return obj[expr];
6190                     };
6191             } catch(e){}
6192             return Roo.emptyFn;
6193         };
6194     }(),
6195
6196     /**
6197      * Create a data block containing Roo.data.Records from an XML document.
6198      * @param {Object} o An object which contains an Array of row objects in the property specified
6199      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6200      * which contains the total size of the dataset.
6201      * @return {Object} data A data block which is used by an Roo.data.Store object as
6202      * a cache of Roo.data.Records.
6203      */
6204     readRecords : function(o){
6205         /**
6206          * After any data loads, the raw JSON data is available for further custom processing.
6207          * @type Object
6208          */
6209         this.jsonData = o;
6210         var s = this.meta, Record = this.recordType,
6211             f = Record.prototype.fields, fi = f.items, fl = f.length;
6212
6213 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6214         if (!this.ef) {
6215             if(s.totalProperty) {
6216                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6217                 }
6218                 if(s.successProperty) {
6219                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6220                 }
6221                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6222                 if (s.id) {
6223                         var g = this.getJsonAccessor(s.id);
6224                         this.getId = function(rec) {
6225                                 var r = g(rec);
6226                                 return (r === undefined || r === "") ? null : r;
6227                         };
6228                 } else {
6229                         this.getId = function(){return null;};
6230                 }
6231             this.ef = [];
6232             for(var jj = 0; jj < fl; jj++){
6233                 f = fi[jj];
6234                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6235                 this.ef[jj] = this.getJsonAccessor(map);
6236             }
6237         }
6238
6239         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6240         if(s.totalProperty){
6241             var vt = parseInt(this.getTotal(o), 10);
6242             if(!isNaN(vt)){
6243                 totalRecords = vt;
6244             }
6245         }
6246         if(s.successProperty){
6247             var vs = this.getSuccess(o);
6248             if(vs === false || vs === 'false'){
6249                 success = false;
6250             }
6251         }
6252         var records = [];
6253             for(var i = 0; i < c; i++){
6254                     var n = root[i];
6255                 var values = {};
6256                 var id = this.getId(n);
6257                 for(var j = 0; j < fl; j++){
6258                     f = fi[j];
6259                 var v = this.ef[j](n);
6260                 if (!f.convert) {
6261                     Roo.log('missing convert for ' + f.name);
6262                     Roo.log(f);
6263                     continue;
6264                 }
6265                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6266                 }
6267                 var record = new Record(values, id);
6268                 record.json = n;
6269                 records[i] = record;
6270             }
6271             return {
6272                 success : success,
6273                 records : records,
6274                 totalRecords : totalRecords
6275             };
6276     }
6277 });/*
6278  * Based on:
6279  * Ext JS Library 1.1.1
6280  * Copyright(c) 2006-2007, Ext JS, LLC.
6281  *
6282  * Originally Released Under LGPL - original licence link has changed is not relivant.
6283  *
6284  * Fork - LGPL
6285  * <script type="text/javascript">
6286  */
6287
6288 /**
6289  * @class Roo.data.XmlReader
6290  * @extends Roo.data.DataReader
6291  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6292  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6293  * <p>
6294  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6295  * header in the HTTP response must be set to "text/xml".</em>
6296  * <p>
6297  * Example code:
6298  * <pre><code>
6299 var RecordDef = Roo.data.Record.create([
6300    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6301    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6302 ]);
6303 var myReader = new Roo.data.XmlReader({
6304    totalRecords: "results", // The element which contains the total dataset size (optional)
6305    record: "row",           // The repeated element which contains row information
6306    id: "id"                 // The element within the row that provides an ID for the record (optional)
6307 }, RecordDef);
6308 </code></pre>
6309  * <p>
6310  * This would consume an XML file like this:
6311  * <pre><code>
6312 &lt;?xml?>
6313 &lt;dataset>
6314  &lt;results>2&lt;/results>
6315  &lt;row>
6316    &lt;id>1&lt;/id>
6317    &lt;name>Bill&lt;/name>
6318    &lt;occupation>Gardener&lt;/occupation>
6319  &lt;/row>
6320  &lt;row>
6321    &lt;id>2&lt;/id>
6322    &lt;name>Ben&lt;/name>
6323    &lt;occupation>Horticulturalist&lt;/occupation>
6324  &lt;/row>
6325 &lt;/dataset>
6326 </code></pre>
6327  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6328  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6329  * paged from the remote server.
6330  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6331  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6332  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6333  * a record identifier value.
6334  * @constructor
6335  * Create a new XmlReader
6336  * @param {Object} meta Metadata configuration options
6337  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6338  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6339  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6340  */
6341 Roo.data.XmlReader = function(meta, recordType){
6342     meta = meta || {};
6343     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6344 };
6345 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6346     /**
6347      * This method is only used by a DataProxy which has retrieved data from a remote server.
6348          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6349          * to contain a method called 'responseXML' that returns an XML document object.
6350      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6351      * a cache of Roo.data.Records.
6352      */
6353     read : function(response){
6354         var doc = response.responseXML;
6355         if(!doc) {
6356             throw {message: "XmlReader.read: XML Document not available"};
6357         }
6358         return this.readRecords(doc);
6359     },
6360
6361     /**
6362      * Create a data block containing Roo.data.Records from an XML document.
6363          * @param {Object} doc A parsed XML document.
6364      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6365      * a cache of Roo.data.Records.
6366      */
6367     readRecords : function(doc){
6368         /**
6369          * After any data loads/reads, the raw XML Document is available for further custom processing.
6370          * @type XMLDocument
6371          */
6372         this.xmlData = doc;
6373         var root = doc.documentElement || doc;
6374         var q = Roo.DomQuery;
6375         var recordType = this.recordType, fields = recordType.prototype.fields;
6376         var sid = this.meta.id;
6377         var totalRecords = 0, success = true;
6378         if(this.meta.totalRecords){
6379             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6380         }
6381         
6382         if(this.meta.success){
6383             var sv = q.selectValue(this.meta.success, root, true);
6384             success = sv !== false && sv !== 'false';
6385         }
6386         var records = [];
6387         var ns = q.select(this.meta.record, root);
6388         for(var i = 0, len = ns.length; i < len; i++) {
6389                 var n = ns[i];
6390                 var values = {};
6391                 var id = sid ? q.selectValue(sid, n) : undefined;
6392                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6393                     var f = fields.items[j];
6394                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6395                     v = f.convert(v);
6396                     values[f.name] = v;
6397                 }
6398                 var record = new recordType(values, id);
6399                 record.node = n;
6400                 records[records.length] = record;
6401             }
6402
6403             return {
6404                 success : success,
6405                 records : records,
6406                 totalRecords : totalRecords || records.length
6407             };
6408     }
6409 });/*
6410  * Based on:
6411  * Ext JS Library 1.1.1
6412  * Copyright(c) 2006-2007, Ext JS, LLC.
6413  *
6414  * Originally Released Under LGPL - original licence link has changed is not relivant.
6415  *
6416  * Fork - LGPL
6417  * <script type="text/javascript">
6418  */
6419
6420 /**
6421  * @class Roo.data.ArrayReader
6422  * @extends Roo.data.DataReader
6423  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6424  * Each element of that Array represents a row of data fields. The
6425  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6426  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6427  * <p>
6428  * Example code:.
6429  * <pre><code>
6430 var RecordDef = Roo.data.Record.create([
6431     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6432     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6433 ]);
6434 var myReader = new Roo.data.ArrayReader({
6435     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6436 }, RecordDef);
6437 </code></pre>
6438  * <p>
6439  * This would consume an Array like this:
6440  * <pre><code>
6441 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6442   </code></pre>
6443  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6444  * @constructor
6445  * Create a new JsonReader
6446  * @param {Object} meta Metadata configuration options.
6447  * @param {Object} recordType Either an Array of field definition objects
6448  * as specified to {@link Roo.data.Record#create},
6449  * or an {@link Roo.data.Record} object
6450  * created using {@link Roo.data.Record#create}.
6451  */
6452 Roo.data.ArrayReader = function(meta, recordType){
6453     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6454 };
6455
6456 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6457     /**
6458      * Create a data block containing Roo.data.Records from an XML document.
6459      * @param {Object} o An Array of row objects which represents the dataset.
6460      * @return {Object} data A data block which is used by an Roo.data.Store object as
6461      * a cache of Roo.data.Records.
6462      */
6463     readRecords : function(o){
6464         var sid = this.meta ? this.meta.id : null;
6465         var recordType = this.recordType, fields = recordType.prototype.fields;
6466         var records = [];
6467         var root = o;
6468             for(var i = 0; i < root.length; i++){
6469                     var n = root[i];
6470                 var values = {};
6471                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6472                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6473                 var f = fields.items[j];
6474                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6475                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6476                 v = f.convert(v);
6477                 values[f.name] = v;
6478             }
6479                 var record = new recordType(values, id);
6480                 record.json = n;
6481                 records[records.length] = record;
6482             }
6483             return {
6484                 records : records,
6485                 totalRecords : records.length
6486             };
6487     }
6488 });/*
6489  * Based on:
6490  * Ext JS Library 1.1.1
6491  * Copyright(c) 2006-2007, Ext JS, LLC.
6492  *
6493  * Originally Released Under LGPL - original licence link has changed is not relivant.
6494  *
6495  * Fork - LGPL
6496  * <script type="text/javascript">
6497  */
6498
6499
6500 /**
6501  * @class Roo.data.Tree
6502  * @extends Roo.util.Observable
6503  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6504  * in the tree have most standard DOM functionality.
6505  * @constructor
6506  * @param {Node} root (optional) The root node
6507  */
6508 Roo.data.Tree = function(root){
6509    this.nodeHash = {};
6510    /**
6511     * The root node for this tree
6512     * @type Node
6513     */
6514    this.root = null;
6515    if(root){
6516        this.setRootNode(root);
6517    }
6518    this.addEvents({
6519        /**
6520         * @event append
6521         * Fires when a new child node is appended to a node in this tree.
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} parent The parent node
6524         * @param {Node} node The newly appended node
6525         * @param {Number} index The index of the newly appended node
6526         */
6527        "append" : true,
6528        /**
6529         * @event remove
6530         * Fires when a child node is removed from a node in this tree.
6531         * @param {Tree} tree The owner tree
6532         * @param {Node} parent The parent node
6533         * @param {Node} node The child node removed
6534         */
6535        "remove" : true,
6536        /**
6537         * @event move
6538         * Fires when a node is moved to a new location in the tree
6539         * @param {Tree} tree The owner tree
6540         * @param {Node} node The node moved
6541         * @param {Node} oldParent The old parent of this node
6542         * @param {Node} newParent The new parent of this node
6543         * @param {Number} index The index it was moved to
6544         */
6545        "move" : true,
6546        /**
6547         * @event insert
6548         * Fires when a new child node is inserted in a node in this tree.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node inserted
6552         * @param {Node} refNode The child node the node was inserted before
6553         */
6554        "insert" : true,
6555        /**
6556         * @event beforeappend
6557         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6558         * @param {Tree} tree The owner tree
6559         * @param {Node} parent The parent node
6560         * @param {Node} node The child node to be appended
6561         */
6562        "beforeappend" : true,
6563        /**
6564         * @event beforeremove
6565         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6566         * @param {Tree} tree The owner tree
6567         * @param {Node} parent The parent node
6568         * @param {Node} node The child node to be removed
6569         */
6570        "beforeremove" : true,
6571        /**
6572         * @event beforemove
6573         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6574         * @param {Tree} tree The owner tree
6575         * @param {Node} node The node being moved
6576         * @param {Node} oldParent The parent of the node
6577         * @param {Node} newParent The new parent the node is moving to
6578         * @param {Number} index The index it is being moved to
6579         */
6580        "beforemove" : true,
6581        /**
6582         * @event beforeinsert
6583         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be inserted
6587         * @param {Node} refNode The child node the node is being inserted before
6588         */
6589        "beforeinsert" : true
6590    });
6591
6592     Roo.data.Tree.superclass.constructor.call(this);
6593 };
6594
6595 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6596     pathSeparator: "/",
6597
6598     proxyNodeEvent : function(){
6599         return this.fireEvent.apply(this, arguments);
6600     },
6601
6602     /**
6603      * Returns the root node for this tree.
6604      * @return {Node}
6605      */
6606     getRootNode : function(){
6607         return this.root;
6608     },
6609
6610     /**
6611      * Sets the root node for this tree.
6612      * @param {Node} node
6613      * @return {Node}
6614      */
6615     setRootNode : function(node){
6616         this.root = node;
6617         node.ownerTree = this;
6618         node.isRoot = true;
6619         this.registerNode(node);
6620         return node;
6621     },
6622
6623     /**
6624      * Gets a node in this tree by its id.
6625      * @param {String} id
6626      * @return {Node}
6627      */
6628     getNodeById : function(id){
6629         return this.nodeHash[id];
6630     },
6631
6632     registerNode : function(node){
6633         this.nodeHash[node.id] = node;
6634     },
6635
6636     unregisterNode : function(node){
6637         delete this.nodeHash[node.id];
6638     },
6639
6640     toString : function(){
6641         return "[Tree"+(this.id?" "+this.id:"")+"]";
6642     }
6643 });
6644
6645 /**
6646  * @class Roo.data.Node
6647  * @extends Roo.util.Observable
6648  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6649  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6650  * @constructor
6651  * @param {Object} attributes The attributes/config for the node
6652  */
6653 Roo.data.Node = function(attributes){
6654     /**
6655      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6656      * @type {Object}
6657      */
6658     this.attributes = attributes || {};
6659     this.leaf = this.attributes.leaf;
6660     /**
6661      * The node id. @type String
6662      */
6663     this.id = this.attributes.id;
6664     if(!this.id){
6665         this.id = Roo.id(null, "ynode-");
6666         this.attributes.id = this.id;
6667     }
6668     /**
6669      * All child nodes of this node. @type Array
6670      */
6671     this.childNodes = [];
6672     if(!this.childNodes.indexOf){ // indexOf is a must
6673         this.childNodes.indexOf = function(o){
6674             for(var i = 0, len = this.length; i < len; i++){
6675                 if(this[i] == o) {
6676                     return i;
6677                 }
6678             }
6679             return -1;
6680         };
6681     }
6682     /**
6683      * The parent node for this node. @type Node
6684      */
6685     this.parentNode = null;
6686     /**
6687      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6688      */
6689     this.firstChild = null;
6690     /**
6691      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6692      */
6693     this.lastChild = null;
6694     /**
6695      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6696      */
6697     this.previousSibling = null;
6698     /**
6699      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6700      */
6701     this.nextSibling = null;
6702
6703     this.addEvents({
6704        /**
6705         * @event append
6706         * Fires when a new child node is appended
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} node The newly appended node
6710         * @param {Number} index The index of the newly appended node
6711         */
6712        "append" : true,
6713        /**
6714         * @event remove
6715         * Fires when a child node is removed
6716         * @param {Tree} tree The owner tree
6717         * @param {Node} this This node
6718         * @param {Node} node The removed node
6719         */
6720        "remove" : true,
6721        /**
6722         * @event move
6723         * Fires when this node is moved to a new location in the tree
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} oldParent The old parent of this node
6727         * @param {Node} newParent The new parent of this node
6728         * @param {Number} index The index it was moved to
6729         */
6730        "move" : true,
6731        /**
6732         * @event insert
6733         * Fires when a new child node is inserted.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node inserted
6737         * @param {Node} refNode The child node the node was inserted before
6738         */
6739        "insert" : true,
6740        /**
6741         * @event beforeappend
6742         * Fires before a new child is appended, return false to cancel the append.
6743         * @param {Tree} tree The owner tree
6744         * @param {Node} this This node
6745         * @param {Node} node The child node to be appended
6746         */
6747        "beforeappend" : true,
6748        /**
6749         * @event beforeremove
6750         * Fires before a child is removed, return false to cancel the remove.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be removed
6754         */
6755        "beforeremove" : true,
6756        /**
6757         * @event beforemove
6758         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} oldParent The parent of this node
6762         * @param {Node} newParent The new parent this node is moving to
6763         * @param {Number} index The index it is being moved to
6764         */
6765        "beforemove" : true,
6766        /**
6767         * @event beforeinsert
6768         * Fires before a new child is inserted, return false to cancel the insert.
6769         * @param {Tree} tree The owner tree
6770         * @param {Node} this This node
6771         * @param {Node} node The child node to be inserted
6772         * @param {Node} refNode The child node the node is being inserted before
6773         */
6774        "beforeinsert" : true
6775    });
6776     this.listeners = this.attributes.listeners;
6777     Roo.data.Node.superclass.constructor.call(this);
6778 };
6779
6780 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6781     fireEvent : function(evtName){
6782         // first do standard event for this node
6783         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6784             return false;
6785         }
6786         // then bubble it up to the tree if the event wasn't cancelled
6787         var ot = this.getOwnerTree();
6788         if(ot){
6789             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6790                 return false;
6791             }
6792         }
6793         return true;
6794     },
6795
6796     /**
6797      * Returns true if this node is a leaf
6798      * @return {Boolean}
6799      */
6800     isLeaf : function(){
6801         return this.leaf === true;
6802     },
6803
6804     // private
6805     setFirstChild : function(node){
6806         this.firstChild = node;
6807     },
6808
6809     //private
6810     setLastChild : function(node){
6811         this.lastChild = node;
6812     },
6813
6814
6815     /**
6816      * Returns true if this node is the last child of its parent
6817      * @return {Boolean}
6818      */
6819     isLast : function(){
6820        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6821     },
6822
6823     /**
6824      * Returns true if this node is the first child of its parent
6825      * @return {Boolean}
6826      */
6827     isFirst : function(){
6828        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6829     },
6830
6831     hasChildNodes : function(){
6832         return !this.isLeaf() && this.childNodes.length > 0;
6833     },
6834
6835     /**
6836      * Insert node(s) as the last child node of this node.
6837      * @param {Node/Array} node The node or Array of nodes to append
6838      * @return {Node} The appended node if single append, or null if an array was passed
6839      */
6840     appendChild : function(node){
6841         var multi = false;
6842         if(node instanceof Array){
6843             multi = node;
6844         }else if(arguments.length > 1){
6845             multi = arguments;
6846         }
6847         // if passed an array or multiple args do them one by one
6848         if(multi){
6849             for(var i = 0, len = multi.length; i < len; i++) {
6850                 this.appendChild(multi[i]);
6851             }
6852         }else{
6853             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6854                 return false;
6855             }
6856             var index = this.childNodes.length;
6857             var oldParent = node.parentNode;
6858             // it's a move, make sure we move it cleanly
6859             if(oldParent){
6860                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6861                     return false;
6862                 }
6863                 oldParent.removeChild(node);
6864             }
6865             index = this.childNodes.length;
6866             if(index == 0){
6867                 this.setFirstChild(node);
6868             }
6869             this.childNodes.push(node);
6870             node.parentNode = this;
6871             var ps = this.childNodes[index-1];
6872             if(ps){
6873                 node.previousSibling = ps;
6874                 ps.nextSibling = node;
6875             }else{
6876                 node.previousSibling = null;
6877             }
6878             node.nextSibling = null;
6879             this.setLastChild(node);
6880             node.setOwnerTree(this.getOwnerTree());
6881             this.fireEvent("append", this.ownerTree, this, node, index);
6882             if(oldParent){
6883                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6884             }
6885             return node;
6886         }
6887     },
6888
6889     /**
6890      * Removes a child node from this node.
6891      * @param {Node} node The node to remove
6892      * @return {Node} The removed node
6893      */
6894     removeChild : function(node){
6895         var index = this.childNodes.indexOf(node);
6896         if(index == -1){
6897             return false;
6898         }
6899         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6900             return false;
6901         }
6902
6903         // remove it from childNodes collection
6904         this.childNodes.splice(index, 1);
6905
6906         // update siblings
6907         if(node.previousSibling){
6908             node.previousSibling.nextSibling = node.nextSibling;
6909         }
6910         if(node.nextSibling){
6911             node.nextSibling.previousSibling = node.previousSibling;
6912         }
6913
6914         // update child refs
6915         if(this.firstChild == node){
6916             this.setFirstChild(node.nextSibling);
6917         }
6918         if(this.lastChild == node){
6919             this.setLastChild(node.previousSibling);
6920         }
6921
6922         node.setOwnerTree(null);
6923         // clear any references from the node
6924         node.parentNode = null;
6925         node.previousSibling = null;
6926         node.nextSibling = null;
6927         this.fireEvent("remove", this.ownerTree, this, node);
6928         return node;
6929     },
6930
6931     /**
6932      * Inserts the first node before the second node in this nodes childNodes collection.
6933      * @param {Node} node The node to insert
6934      * @param {Node} refNode The node to insert before (if null the node is appended)
6935      * @return {Node} The inserted node
6936      */
6937     insertBefore : function(node, refNode){
6938         if(!refNode){ // like standard Dom, refNode can be null for append
6939             return this.appendChild(node);
6940         }
6941         // nothing to do
6942         if(node == refNode){
6943             return false;
6944         }
6945
6946         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6947             return false;
6948         }
6949         var index = this.childNodes.indexOf(refNode);
6950         var oldParent = node.parentNode;
6951         var refIndex = index;
6952
6953         // when moving internally, indexes will change after remove
6954         if(oldParent == this && this.childNodes.indexOf(node) < index){
6955             refIndex--;
6956         }
6957
6958         // it's a move, make sure we move it cleanly
6959         if(oldParent){
6960             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6961                 return false;
6962             }
6963             oldParent.removeChild(node);
6964         }
6965         if(refIndex == 0){
6966             this.setFirstChild(node);
6967         }
6968         this.childNodes.splice(refIndex, 0, node);
6969         node.parentNode = this;
6970         var ps = this.childNodes[refIndex-1];
6971         if(ps){
6972             node.previousSibling = ps;
6973             ps.nextSibling = node;
6974         }else{
6975             node.previousSibling = null;
6976         }
6977         node.nextSibling = refNode;
6978         refNode.previousSibling = node;
6979         node.setOwnerTree(this.getOwnerTree());
6980         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6981         if(oldParent){
6982             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6983         }
6984         return node;
6985     },
6986
6987     /**
6988      * Returns the child node at the specified index.
6989      * @param {Number} index
6990      * @return {Node}
6991      */
6992     item : function(index){
6993         return this.childNodes[index];
6994     },
6995
6996     /**
6997      * Replaces one child node in this node with another.
6998      * @param {Node} newChild The replacement node
6999      * @param {Node} oldChild The node to replace
7000      * @return {Node} The replaced node
7001      */
7002     replaceChild : function(newChild, oldChild){
7003         this.insertBefore(newChild, oldChild);
7004         this.removeChild(oldChild);
7005         return oldChild;
7006     },
7007
7008     /**
7009      * Returns the index of a child node
7010      * @param {Node} node
7011      * @return {Number} The index of the node or -1 if it was not found
7012      */
7013     indexOf : function(child){
7014         return this.childNodes.indexOf(child);
7015     },
7016
7017     /**
7018      * Returns the tree this node is in.
7019      * @return {Tree}
7020      */
7021     getOwnerTree : function(){
7022         // if it doesn't have one, look for one
7023         if(!this.ownerTree){
7024             var p = this;
7025             while(p){
7026                 if(p.ownerTree){
7027                     this.ownerTree = p.ownerTree;
7028                     break;
7029                 }
7030                 p = p.parentNode;
7031             }
7032         }
7033         return this.ownerTree;
7034     },
7035
7036     /**
7037      * Returns depth of this node (the root node has a depth of 0)
7038      * @return {Number}
7039      */
7040     getDepth : function(){
7041         var depth = 0;
7042         var p = this;
7043         while(p.parentNode){
7044             ++depth;
7045             p = p.parentNode;
7046         }
7047         return depth;
7048     },
7049
7050     // private
7051     setOwnerTree : function(tree){
7052         // if it's move, we need to update everyone
7053         if(tree != this.ownerTree){
7054             if(this.ownerTree){
7055                 this.ownerTree.unregisterNode(this);
7056             }
7057             this.ownerTree = tree;
7058             var cs = this.childNodes;
7059             for(var i = 0, len = cs.length; i < len; i++) {
7060                 cs[i].setOwnerTree(tree);
7061             }
7062             if(tree){
7063                 tree.registerNode(this);
7064             }
7065         }
7066     },
7067
7068     /**
7069      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7070      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7071      * @return {String} The path
7072      */
7073     getPath : function(attr){
7074         attr = attr || "id";
7075         var p = this.parentNode;
7076         var b = [this.attributes[attr]];
7077         while(p){
7078             b.unshift(p.attributes[attr]);
7079             p = p.parentNode;
7080         }
7081         var sep = this.getOwnerTree().pathSeparator;
7082         return sep + b.join(sep);
7083     },
7084
7085     /**
7086      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7087      * function call will be the scope provided or the current node. The arguments to the function
7088      * will be the args provided or the current node. If the function returns false at any point,
7089      * the bubble is stopped.
7090      * @param {Function} fn The function to call
7091      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7092      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7093      */
7094     bubble : function(fn, scope, args){
7095         var p = this;
7096         while(p){
7097             if(fn.call(scope || p, args || p) === false){
7098                 break;
7099             }
7100             p = p.parentNode;
7101         }
7102     },
7103
7104     /**
7105      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7106      * function call will be the scope provided or the current node. The arguments to the function
7107      * will be the args provided or the current node. If the function returns false at any point,
7108      * the cascade is stopped on that branch.
7109      * @param {Function} fn The function to call
7110      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7111      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7112      */
7113     cascade : function(fn, scope, args){
7114         if(fn.call(scope || this, args || this) !== false){
7115             var cs = this.childNodes;
7116             for(var i = 0, len = cs.length; i < len; i++) {
7117                 cs[i].cascade(fn, scope, args);
7118             }
7119         }
7120     },
7121
7122     /**
7123      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7124      * function call will be the scope provided or the current node. The arguments to the function
7125      * will be the args provided or the current node. If the function returns false at any point,
7126      * the iteration stops.
7127      * @param {Function} fn The function to call
7128      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7129      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7130      */
7131     eachChild : function(fn, scope, args){
7132         var cs = this.childNodes;
7133         for(var i = 0, len = cs.length; i < len; i++) {
7134                 if(fn.call(scope || this, args || cs[i]) === false){
7135                     break;
7136                 }
7137         }
7138     },
7139
7140     /**
7141      * Finds the first child that has the attribute with the specified value.
7142      * @param {String} attribute The attribute name
7143      * @param {Mixed} value The value to search for
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChild : function(attribute, value){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(cs[i].attributes[attribute] == value){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Finds the first child by a custom function. The child matches if the function passed
7158      * returns true.
7159      * @param {Function} fn
7160      * @param {Object} scope (optional)
7161      * @return {Node} The found child or null if none was found
7162      */
7163     findChildBy : function(fn, scope){
7164         var cs = this.childNodes;
7165         for(var i = 0, len = cs.length; i < len; i++) {
7166                 if(fn.call(scope||cs[i], cs[i]) === true){
7167                     return cs[i];
7168                 }
7169         }
7170         return null;
7171     },
7172
7173     /**
7174      * Sorts this nodes children using the supplied sort function
7175      * @param {Function} fn
7176      * @param {Object} scope (optional)
7177      */
7178     sort : function(fn, scope){
7179         var cs = this.childNodes;
7180         var len = cs.length;
7181         if(len > 0){
7182             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7183             cs.sort(sortFn);
7184             for(var i = 0; i < len; i++){
7185                 var n = cs[i];
7186                 n.previousSibling = cs[i-1];
7187                 n.nextSibling = cs[i+1];
7188                 if(i == 0){
7189                     this.setFirstChild(n);
7190                 }
7191                 if(i == len-1){
7192                     this.setLastChild(n);
7193                 }
7194             }
7195         }
7196     },
7197
7198     /**
7199      * Returns true if this node is an ancestor (at any point) of the passed node.
7200      * @param {Node} node
7201      * @return {Boolean}
7202      */
7203     contains : function(node){
7204         return node.isAncestor(this);
7205     },
7206
7207     /**
7208      * Returns true if the passed node is an ancestor (at any point) of this node.
7209      * @param {Node} node
7210      * @return {Boolean}
7211      */
7212     isAncestor : function(node){
7213         var p = this.parentNode;
7214         while(p){
7215             if(p == node){
7216                 return true;
7217             }
7218             p = p.parentNode;
7219         }
7220         return false;
7221     },
7222
7223     toString : function(){
7224         return "[Node"+(this.id?" "+this.id:"")+"]";
7225     }
7226 });/*
7227  * Based on:
7228  * Ext JS Library 1.1.1
7229  * Copyright(c) 2006-2007, Ext JS, LLC.
7230  *
7231  * Originally Released Under LGPL - original licence link has changed is not relivant.
7232  *
7233  * Fork - LGPL
7234  * <script type="text/javascript">
7235  */
7236  
7237
7238 /**
7239  * @class Roo.ComponentMgr
7240  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7241  * @singleton
7242  */
7243 Roo.ComponentMgr = function(){
7244     var all = new Roo.util.MixedCollection();
7245
7246     return {
7247         /**
7248          * Registers a component.
7249          * @param {Roo.Component} c The component
7250          */
7251         register : function(c){
7252             all.add(c);
7253         },
7254
7255         /**
7256          * Unregisters a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         unregister : function(c){
7260             all.remove(c);
7261         },
7262
7263         /**
7264          * Returns a component by id
7265          * @param {String} id The component id
7266          */
7267         get : function(id){
7268             return all.get(id);
7269         },
7270
7271         /**
7272          * Registers a function that will be called when a specified component is added to ComponentMgr
7273          * @param {String} id The component id
7274          * @param {Funtction} fn The callback function
7275          * @param {Object} scope The scope of the callback
7276          */
7277         onAvailable : function(id, fn, scope){
7278             all.on("add", function(index, o){
7279                 if(o.id == id){
7280                     fn.call(scope || o, o);
7281                     all.un("add", fn, scope);
7282                 }
7283             });
7284         }
7285     };
7286 }();/*
7287  * Based on:
7288  * Ext JS Library 1.1.1
7289  * Copyright(c) 2006-2007, Ext JS, LLC.
7290  *
7291  * Originally Released Under LGPL - original licence link has changed is not relivant.
7292  *
7293  * Fork - LGPL
7294  * <script type="text/javascript">
7295  */
7296  
7297 /**
7298  * @class Roo.Component
7299  * @extends Roo.util.Observable
7300  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7301  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7302  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7303  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7304  * All visual components (widgets) that require rendering into a layout should subclass Component.
7305  * @constructor
7306  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7307  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7308  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7309  */
7310 Roo.Component = function(config){
7311     config = config || {};
7312     if(config.tagName || config.dom || typeof config == "string"){ // element object
7313         config = {el: config, id: config.id || config};
7314     }
7315     this.initialConfig = config;
7316
7317     Roo.apply(this, config);
7318     this.addEvents({
7319         /**
7320          * @event disable
7321          * Fires after the component is disabled.
7322              * @param {Roo.Component} this
7323              */
7324         disable : true,
7325         /**
7326          * @event enable
7327          * Fires after the component is enabled.
7328              * @param {Roo.Component} this
7329              */
7330         enable : true,
7331         /**
7332          * @event beforeshow
7333          * Fires before the component is shown.  Return false to stop the show.
7334              * @param {Roo.Component} this
7335              */
7336         beforeshow : true,
7337         /**
7338          * @event show
7339          * Fires after the component is shown.
7340              * @param {Roo.Component} this
7341              */
7342         show : true,
7343         /**
7344          * @event beforehide
7345          * Fires before the component is hidden. Return false to stop the hide.
7346              * @param {Roo.Component} this
7347              */
7348         beforehide : true,
7349         /**
7350          * @event hide
7351          * Fires after the component is hidden.
7352              * @param {Roo.Component} this
7353              */
7354         hide : true,
7355         /**
7356          * @event beforerender
7357          * Fires before the component is rendered. Return false to stop the render.
7358              * @param {Roo.Component} this
7359              */
7360         beforerender : true,
7361         /**
7362          * @event render
7363          * Fires after the component is rendered.
7364              * @param {Roo.Component} this
7365              */
7366         render : true,
7367         /**
7368          * @event beforedestroy
7369          * Fires before the component is destroyed. Return false to stop the destroy.
7370              * @param {Roo.Component} this
7371              */
7372         beforedestroy : true,
7373         /**
7374          * @event destroy
7375          * Fires after the component is destroyed.
7376              * @param {Roo.Component} this
7377              */
7378         destroy : true
7379     });
7380     if(!this.id){
7381         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7382     }
7383     Roo.ComponentMgr.register(this);
7384     Roo.Component.superclass.constructor.call(this);
7385     this.initComponent();
7386     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7387         this.render(this.renderTo);
7388         delete this.renderTo;
7389     }
7390 };
7391
7392 // private
7393 Roo.Component.AUTO_ID = 1000;
7394
7395 Roo.extend(Roo.Component, Roo.util.Observable, {
7396     /**
7397      * @property {Boolean} hidden
7398      * true if this component is hidden. Read-only.
7399      */
7400     hidden : false,
7401     /**
7402      * true if this component is disabled. Read-only.
7403      */
7404     disabled : false,
7405     /**
7406      * true if this component has been rendered. Read-only.
7407      */
7408     rendered : false,
7409     
7410     /** @cfg {String} disableClass
7411      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7412      */
7413     disabledClass : "x-item-disabled",
7414         /** @cfg {Boolean} allowDomMove
7415          * Whether the component can move the Dom node when rendering (defaults to true).
7416          */
7417     allowDomMove : true,
7418     /** @cfg {String} hideMode
7419      * How this component should hidden. Supported values are
7420      * "visibility" (css visibility), "offsets" (negative offset position) and
7421      * "display" (css display) - defaults to "display".
7422      */
7423     hideMode: 'display',
7424
7425     // private
7426     ctype : "Roo.Component",
7427
7428     /** @cfg {String} actionMode 
7429      * which property holds the element that used for  hide() / show() / disable() / enable()
7430      * default is 'el' 
7431      */
7432     actionMode : "el",
7433
7434     // private
7435     getActionEl : function(){
7436         return this[this.actionMode];
7437     },
7438
7439     initComponent : Roo.emptyFn,
7440     /**
7441      * If this is a lazy rendering component, render it to its container element.
7442      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7443      */
7444     render : function(container, position){
7445         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7446             if(!container && this.el){
7447                 this.el = Roo.get(this.el);
7448                 container = this.el.dom.parentNode;
7449                 this.allowDomMove = false;
7450             }
7451             this.container = Roo.get(container);
7452             this.rendered = true;
7453             if(position !== undefined){
7454                 if(typeof position == 'number'){
7455                     position = this.container.dom.childNodes[position];
7456                 }else{
7457                     position = Roo.getDom(position);
7458                 }
7459             }
7460             this.onRender(this.container, position || null);
7461             if(this.cls){
7462                 this.el.addClass(this.cls);
7463                 delete this.cls;
7464             }
7465             if(this.style){
7466                 this.el.applyStyles(this.style);
7467                 delete this.style;
7468             }
7469             this.fireEvent("render", this);
7470             this.afterRender(this.container);
7471             if(this.hidden){
7472                 this.hide();
7473             }
7474             if(this.disabled){
7475                 this.disable();
7476             }
7477         }
7478         return this;
7479     },
7480
7481     // private
7482     // default function is not really useful
7483     onRender : function(ct, position){
7484         if(this.el){
7485             this.el = Roo.get(this.el);
7486             if(this.allowDomMove !== false){
7487                 ct.dom.insertBefore(this.el.dom, position);
7488             }
7489         }
7490     },
7491
7492     // private
7493     getAutoCreate : function(){
7494         var cfg = typeof this.autoCreate == "object" ?
7495                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7496         if(this.id && !cfg.id){
7497             cfg.id = this.id;
7498         }
7499         return cfg;
7500     },
7501
7502     // private
7503     afterRender : Roo.emptyFn,
7504
7505     /**
7506      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7507      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7508      */
7509     destroy : function(){
7510         if(this.fireEvent("beforedestroy", this) !== false){
7511             this.purgeListeners();
7512             this.beforeDestroy();
7513             if(this.rendered){
7514                 this.el.removeAllListeners();
7515                 this.el.remove();
7516                 if(this.actionMode == "container"){
7517                     this.container.remove();
7518                 }
7519             }
7520             this.onDestroy();
7521             Roo.ComponentMgr.unregister(this);
7522             this.fireEvent("destroy", this);
7523         }
7524     },
7525
7526         // private
7527     beforeDestroy : function(){
7528
7529     },
7530
7531         // private
7532         onDestroy : function(){
7533
7534     },
7535
7536     /**
7537      * Returns the underlying {@link Roo.Element}.
7538      * @return {Roo.Element} The element
7539      */
7540     getEl : function(){
7541         return this.el;
7542     },
7543
7544     /**
7545      * Returns the id of this component.
7546      * @return {String}
7547      */
7548     getId : function(){
7549         return this.id;
7550     },
7551
7552     /**
7553      * Try to focus this component.
7554      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7555      * @return {Roo.Component} this
7556      */
7557     focus : function(selectText){
7558         if(this.rendered){
7559             this.el.focus();
7560             if(selectText === true){
7561                 this.el.dom.select();
7562             }
7563         }
7564         return this;
7565     },
7566
7567     // private
7568     blur : function(){
7569         if(this.rendered){
7570             this.el.blur();
7571         }
7572         return this;
7573     },
7574
7575     /**
7576      * Disable this component.
7577      * @return {Roo.Component} this
7578      */
7579     disable : function(){
7580         if(this.rendered){
7581             this.onDisable();
7582         }
7583         this.disabled = true;
7584         this.fireEvent("disable", this);
7585         return this;
7586     },
7587
7588         // private
7589     onDisable : function(){
7590         this.getActionEl().addClass(this.disabledClass);
7591         this.el.dom.disabled = true;
7592     },
7593
7594     /**
7595      * Enable this component.
7596      * @return {Roo.Component} this
7597      */
7598     enable : function(){
7599         if(this.rendered){
7600             this.onEnable();
7601         }
7602         this.disabled = false;
7603         this.fireEvent("enable", this);
7604         return this;
7605     },
7606
7607         // private
7608     onEnable : function(){
7609         this.getActionEl().removeClass(this.disabledClass);
7610         this.el.dom.disabled = false;
7611     },
7612
7613     /**
7614      * Convenience function for setting disabled/enabled by boolean.
7615      * @param {Boolean} disabled
7616      */
7617     setDisabled : function(disabled){
7618         this[disabled ? "disable" : "enable"]();
7619     },
7620
7621     /**
7622      * Show this component.
7623      * @return {Roo.Component} this
7624      */
7625     show: function(){
7626         if(this.fireEvent("beforeshow", this) !== false){
7627             this.hidden = false;
7628             if(this.rendered){
7629                 this.onShow();
7630             }
7631             this.fireEvent("show", this);
7632         }
7633         return this;
7634     },
7635
7636     // private
7637     onShow : function(){
7638         var ae = this.getActionEl();
7639         if(this.hideMode == 'visibility'){
7640             ae.dom.style.visibility = "visible";
7641         }else if(this.hideMode == 'offsets'){
7642             ae.removeClass('x-hidden');
7643         }else{
7644             ae.dom.style.display = "";
7645         }
7646     },
7647
7648     /**
7649      * Hide this component.
7650      * @return {Roo.Component} this
7651      */
7652     hide: function(){
7653         if(this.fireEvent("beforehide", this) !== false){
7654             this.hidden = true;
7655             if(this.rendered){
7656                 this.onHide();
7657             }
7658             this.fireEvent("hide", this);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     onHide : function(){
7665         var ae = this.getActionEl();
7666         if(this.hideMode == 'visibility'){
7667             ae.dom.style.visibility = "hidden";
7668         }else if(this.hideMode == 'offsets'){
7669             ae.addClass('x-hidden');
7670         }else{
7671             ae.dom.style.display = "none";
7672         }
7673     },
7674
7675     /**
7676      * Convenience function to hide or show this component by boolean.
7677      * @param {Boolean} visible True to show, false to hide
7678      * @return {Roo.Component} this
7679      */
7680     setVisible: function(visible){
7681         if(visible) {
7682             this.show();
7683         }else{
7684             this.hide();
7685         }
7686         return this;
7687     },
7688
7689     /**
7690      * Returns true if this component is visible.
7691      */
7692     isVisible : function(){
7693         return this.getActionEl().isVisible();
7694     },
7695
7696     cloneConfig : function(overrides){
7697         overrides = overrides || {};
7698         var id = overrides.id || Roo.id();
7699         var cfg = Roo.applyIf(overrides, this.initialConfig);
7700         cfg.id = id; // prevent dup id
7701         return new this.constructor(cfg);
7702     }
7703 });/*
7704  * Based on:
7705  * Ext JS Library 1.1.1
7706  * Copyright(c) 2006-2007, Ext JS, LLC.
7707  *
7708  * Originally Released Under LGPL - original licence link has changed is not relivant.
7709  *
7710  * Fork - LGPL
7711  * <script type="text/javascript">
7712  */
7713  (function(){ 
7714 /**
7715  * @class Roo.Layer
7716  * @extends Roo.Element
7717  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7718  * automatic maintaining of shadow/shim positions.
7719  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7720  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7721  * you can pass a string with a CSS class name. False turns off the shadow.
7722  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7723  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7724  * @cfg {String} cls CSS class to add to the element
7725  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7726  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7727  * @constructor
7728  * @param {Object} config An object with config options.
7729  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7730  */
7731
7732 Roo.Layer = function(config, existingEl){
7733     config = config || {};
7734     var dh = Roo.DomHelper;
7735     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7736     if(existingEl){
7737         this.dom = Roo.getDom(existingEl);
7738     }
7739     if(!this.dom){
7740         var o = config.dh || {tag: "div", cls: "x-layer"};
7741         this.dom = dh.append(pel, o);
7742     }
7743     if(config.cls){
7744         this.addClass(config.cls);
7745     }
7746     this.constrain = config.constrain !== false;
7747     this.visibilityMode = Roo.Element.VISIBILITY;
7748     if(config.id){
7749         this.id = this.dom.id = config.id;
7750     }else{
7751         this.id = Roo.id(this.dom);
7752     }
7753     this.zindex = config.zindex || this.getZIndex();
7754     this.position("absolute", this.zindex);
7755     if(config.shadow){
7756         this.shadowOffset = config.shadowOffset || 4;
7757         this.shadow = new Roo.Shadow({
7758             offset : this.shadowOffset,
7759             mode : config.shadow
7760         });
7761     }else{
7762         this.shadowOffset = 0;
7763     }
7764     this.useShim = config.shim !== false && Roo.useShims;
7765     this.useDisplay = config.useDisplay;
7766     this.hide();
7767 };
7768
7769 var supr = Roo.Element.prototype;
7770
7771 // shims are shared among layer to keep from having 100 iframes
7772 var shims = [];
7773
7774 Roo.extend(Roo.Layer, Roo.Element, {
7775
7776     getZIndex : function(){
7777         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7778     },
7779
7780     getShim : function(){
7781         if(!this.useShim){
7782             return null;
7783         }
7784         if(this.shim){
7785             return this.shim;
7786         }
7787         var shim = shims.shift();
7788         if(!shim){
7789             shim = this.createShim();
7790             shim.enableDisplayMode('block');
7791             shim.dom.style.display = 'none';
7792             shim.dom.style.visibility = 'visible';
7793         }
7794         var pn = this.dom.parentNode;
7795         if(shim.dom.parentNode != pn){
7796             pn.insertBefore(shim.dom, this.dom);
7797         }
7798         shim.setStyle('z-index', this.getZIndex()-2);
7799         this.shim = shim;
7800         return shim;
7801     },
7802
7803     hideShim : function(){
7804         if(this.shim){
7805             this.shim.setDisplayed(false);
7806             shims.push(this.shim);
7807             delete this.shim;
7808         }
7809     },
7810
7811     disableShadow : function(){
7812         if(this.shadow){
7813             this.shadowDisabled = true;
7814             this.shadow.hide();
7815             this.lastShadowOffset = this.shadowOffset;
7816             this.shadowOffset = 0;
7817         }
7818     },
7819
7820     enableShadow : function(show){
7821         if(this.shadow){
7822             this.shadowDisabled = false;
7823             this.shadowOffset = this.lastShadowOffset;
7824             delete this.lastShadowOffset;
7825             if(show){
7826                 this.sync(true);
7827             }
7828         }
7829     },
7830
7831     // private
7832     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7833     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7834     sync : function(doShow){
7835         var sw = this.shadow;
7836         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7837             var sh = this.getShim();
7838
7839             var w = this.getWidth(),
7840                 h = this.getHeight();
7841
7842             var l = this.getLeft(true),
7843                 t = this.getTop(true);
7844
7845             if(sw && !this.shadowDisabled){
7846                 if(doShow && !sw.isVisible()){
7847                     sw.show(this);
7848                 }else{
7849                     sw.realign(l, t, w, h);
7850                 }
7851                 if(sh){
7852                     if(doShow){
7853                        sh.show();
7854                     }
7855                     // fit the shim behind the shadow, so it is shimmed too
7856                     var a = sw.adjusts, s = sh.dom.style;
7857                     s.left = (Math.min(l, l+a.l))+"px";
7858                     s.top = (Math.min(t, t+a.t))+"px";
7859                     s.width = (w+a.w)+"px";
7860                     s.height = (h+a.h)+"px";
7861                 }
7862             }else if(sh){
7863                 if(doShow){
7864                    sh.show();
7865                 }
7866                 sh.setSize(w, h);
7867                 sh.setLeftTop(l, t);
7868             }
7869             
7870         }
7871     },
7872
7873     // private
7874     destroy : function(){
7875         this.hideShim();
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.removeAllListeners();
7880         var pn = this.dom.parentNode;
7881         if(pn){
7882             pn.removeChild(this.dom);
7883         }
7884         Roo.Element.uncache(this.id);
7885     },
7886
7887     remove : function(){
7888         this.destroy();
7889     },
7890
7891     // private
7892     beginUpdate : function(){
7893         this.updating = true;
7894     },
7895
7896     // private
7897     endUpdate : function(){
7898         this.updating = false;
7899         this.sync(true);
7900     },
7901
7902     // private
7903     hideUnders : function(negOffset){
7904         if(this.shadow){
7905             this.shadow.hide();
7906         }
7907         this.hideShim();
7908     },
7909
7910     // private
7911     constrainXY : function(){
7912         if(this.constrain){
7913             var vw = Roo.lib.Dom.getViewWidth(),
7914                 vh = Roo.lib.Dom.getViewHeight();
7915             var s = Roo.get(document).getScroll();
7916
7917             var xy = this.getXY();
7918             var x = xy[0], y = xy[1];   
7919             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7920             // only move it if it needs it
7921             var moved = false;
7922             // first validate right/bottom
7923             if((x + w) > vw+s.left){
7924                 x = vw - w - this.shadowOffset;
7925                 moved = true;
7926             }
7927             if((y + h) > vh+s.top){
7928                 y = vh - h - this.shadowOffset;
7929                 moved = true;
7930             }
7931             // then make sure top/left isn't negative
7932             if(x < s.left){
7933                 x = s.left;
7934                 moved = true;
7935             }
7936             if(y < s.top){
7937                 y = s.top;
7938                 moved = true;
7939             }
7940             if(moved){
7941                 if(this.avoidY){
7942                     var ay = this.avoidY;
7943                     if(y <= ay && (y+h) >= ay){
7944                         y = ay-h-5;   
7945                     }
7946                 }
7947                 xy = [x, y];
7948                 this.storeXY(xy);
7949                 supr.setXY.call(this, xy);
7950                 this.sync();
7951             }
7952         }
7953     },
7954
7955     isVisible : function(){
7956         return this.visible;    
7957     },
7958
7959     // private
7960     showAction : function(){
7961         this.visible = true; // track visibility to prevent getStyle calls
7962         if(this.useDisplay === true){
7963             this.setDisplayed("");
7964         }else if(this.lastXY){
7965             supr.setXY.call(this, this.lastXY);
7966         }else if(this.lastLT){
7967             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7968         }
7969     },
7970
7971     // private
7972     hideAction : function(){
7973         this.visible = false;
7974         if(this.useDisplay === true){
7975             this.setDisplayed(false);
7976         }else{
7977             this.setLeftTop(-10000,-10000);
7978         }
7979     },
7980
7981     // overridden Element method
7982     setVisible : function(v, a, d, c, e){
7983         if(v){
7984             this.showAction();
7985         }
7986         if(a && v){
7987             var cb = function(){
7988                 this.sync(true);
7989                 if(c){
7990                     c();
7991                 }
7992             }.createDelegate(this);
7993             supr.setVisible.call(this, true, true, d, cb, e);
7994         }else{
7995             if(!v){
7996                 this.hideUnders(true);
7997             }
7998             var cb = c;
7999             if(a){
8000                 cb = function(){
8001                     this.hideAction();
8002                     if(c){
8003                         c();
8004                     }
8005                 }.createDelegate(this);
8006             }
8007             supr.setVisible.call(this, v, a, d, cb, e);
8008             if(v){
8009                 this.sync(true);
8010             }else if(!a){
8011                 this.hideAction();
8012             }
8013         }
8014     },
8015
8016     storeXY : function(xy){
8017         delete this.lastLT;
8018         this.lastXY = xy;
8019     },
8020
8021     storeLeftTop : function(left, top){
8022         delete this.lastXY;
8023         this.lastLT = [left, top];
8024     },
8025
8026     // private
8027     beforeFx : function(){
8028         this.beforeAction();
8029         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8030     },
8031
8032     // private
8033     afterFx : function(){
8034         Roo.Layer.superclass.afterFx.apply(this, arguments);
8035         this.sync(this.isVisible());
8036     },
8037
8038     // private
8039     beforeAction : function(){
8040         if(!this.updating && this.shadow){
8041             this.shadow.hide();
8042         }
8043     },
8044
8045     // overridden Element method
8046     setLeft : function(left){
8047         this.storeLeftTop(left, this.getTop(true));
8048         supr.setLeft.apply(this, arguments);
8049         this.sync();
8050     },
8051
8052     setTop : function(top){
8053         this.storeLeftTop(this.getLeft(true), top);
8054         supr.setTop.apply(this, arguments);
8055         this.sync();
8056     },
8057
8058     setLeftTop : function(left, top){
8059         this.storeLeftTop(left, top);
8060         supr.setLeftTop.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setXY : function(xy, a, d, c, e){
8065         this.fixDisplay();
8066         this.beforeAction();
8067         this.storeXY(xy);
8068         var cb = this.createCB(c);
8069         supr.setXY.call(this, xy, a, d, cb, e);
8070         if(!a){
8071             cb();
8072         }
8073     },
8074
8075     // private
8076     createCB : function(c){
8077         var el = this;
8078         return function(){
8079             el.constrainXY();
8080             el.sync(true);
8081             if(c){
8082                 c();
8083             }
8084         };
8085     },
8086
8087     // overridden Element method
8088     setX : function(x, a, d, c, e){
8089         this.setXY([x, this.getY()], a, d, c, e);
8090     },
8091
8092     // overridden Element method
8093     setY : function(y, a, d, c, e){
8094         this.setXY([this.getX(), y], a, d, c, e);
8095     },
8096
8097     // overridden Element method
8098     setSize : function(w, h, a, d, c, e){
8099         this.beforeAction();
8100         var cb = this.createCB(c);
8101         supr.setSize.call(this, w, h, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // overridden Element method
8108     setWidth : function(w, a, d, c, e){
8109         this.beforeAction();
8110         var cb = this.createCB(c);
8111         supr.setWidth.call(this, w, a, d, cb, e);
8112         if(!a){
8113             cb();
8114         }
8115     },
8116
8117     // overridden Element method
8118     setHeight : function(h, a, d, c, e){
8119         this.beforeAction();
8120         var cb = this.createCB(c);
8121         supr.setHeight.call(this, h, a, d, cb, e);
8122         if(!a){
8123             cb();
8124         }
8125     },
8126
8127     // overridden Element method
8128     setBounds : function(x, y, w, h, a, d, c, e){
8129         this.beforeAction();
8130         var cb = this.createCB(c);
8131         if(!a){
8132             this.storeXY([x, y]);
8133             supr.setXY.call(this, [x, y]);
8134             supr.setSize.call(this, w, h, a, d, cb, e);
8135             cb();
8136         }else{
8137             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8138         }
8139         return this;
8140     },
8141     
8142     /**
8143      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8144      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8145      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8146      * @param {Number} zindex The new z-index to set
8147      * @return {this} The Layer
8148      */
8149     setZIndex : function(zindex){
8150         this.zindex = zindex;
8151         this.setStyle("z-index", zindex + 2);
8152         if(this.shadow){
8153             this.shadow.setZIndex(zindex + 1);
8154         }
8155         if(this.shim){
8156             this.shim.setStyle("z-index", zindex);
8157         }
8158     }
8159 });
8160 })();/*
8161  * Based on:
8162  * Ext JS Library 1.1.1
8163  * Copyright(c) 2006-2007, Ext JS, LLC.
8164  *
8165  * Originally Released Under LGPL - original licence link has changed is not relivant.
8166  *
8167  * Fork - LGPL
8168  * <script type="text/javascript">
8169  */
8170
8171
8172 /**
8173  * @class Roo.Shadow
8174  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8175  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8176  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8177  * @constructor
8178  * Create a new Shadow
8179  * @param {Object} config The config object
8180  */
8181 Roo.Shadow = function(config){
8182     Roo.apply(this, config);
8183     if(typeof this.mode != "string"){
8184         this.mode = this.defaultMode;
8185     }
8186     var o = this.offset, a = {h: 0};
8187     var rad = Math.floor(this.offset/2);
8188     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8189         case "drop":
8190             a.w = 0;
8191             a.l = a.t = o;
8192             a.t -= 1;
8193             if(Roo.isIE){
8194                 a.l -= this.offset + rad;
8195                 a.t -= this.offset + rad;
8196                 a.w -= rad;
8197                 a.h -= rad;
8198                 a.t += 1;
8199             }
8200         break;
8201         case "sides":
8202             a.w = (o*2);
8203             a.l = -o;
8204             a.t = o-1;
8205             if(Roo.isIE){
8206                 a.l -= (this.offset - rad);
8207                 a.t -= this.offset + rad;
8208                 a.l += 1;
8209                 a.w -= (this.offset - rad)*2;
8210                 a.w -= rad + 1;
8211                 a.h -= 1;
8212             }
8213         break;
8214         case "frame":
8215             a.w = a.h = (o*2);
8216             a.l = a.t = -o;
8217             a.t += 1;
8218             a.h -= 2;
8219             if(Roo.isIE){
8220                 a.l -= (this.offset - rad);
8221                 a.t -= (this.offset - rad);
8222                 a.l += 1;
8223                 a.w -= (this.offset + rad + 1);
8224                 a.h -= (this.offset + rad);
8225                 a.h += 1;
8226             }
8227         break;
8228     };
8229
8230     this.adjusts = a;
8231 };
8232
8233 Roo.Shadow.prototype = {
8234     /**
8235      * @cfg {String} mode
8236      * The shadow display mode.  Supports the following options:<br />
8237      * sides: Shadow displays on both sides and bottom only<br />
8238      * frame: Shadow displays equally on all four sides<br />
8239      * drop: Traditional bottom-right drop shadow (default)
8240      */
8241     /**
8242      * @cfg {String} offset
8243      * The number of pixels to offset the shadow from the element (defaults to 4)
8244      */
8245     offset: 4,
8246
8247     // private
8248     defaultMode: "drop",
8249
8250     /**
8251      * Displays the shadow under the target element
8252      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8253      */
8254     show : function(target){
8255         target = Roo.get(target);
8256         if(!this.el){
8257             this.el = Roo.Shadow.Pool.pull();
8258             if(this.el.dom.nextSibling != target.dom){
8259                 this.el.insertBefore(target);
8260             }
8261         }
8262         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8263         if(Roo.isIE){
8264             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8265         }
8266         this.realign(
8267             target.getLeft(true),
8268             target.getTop(true),
8269             target.getWidth(),
8270             target.getHeight()
8271         );
8272         this.el.dom.style.display = "block";
8273     },
8274
8275     /**
8276      * Returns true if the shadow is visible, else false
8277      */
8278     isVisible : function(){
8279         return this.el ? true : false;  
8280     },
8281
8282     /**
8283      * Direct alignment when values are already available. Show must be called at least once before
8284      * calling this method to ensure it is initialized.
8285      * @param {Number} left The target element left position
8286      * @param {Number} top The target element top position
8287      * @param {Number} width The target element width
8288      * @param {Number} height The target element height
8289      */
8290     realign : function(l, t, w, h){
8291         if(!this.el){
8292             return;
8293         }
8294         var a = this.adjusts, d = this.el.dom, s = d.style;
8295         var iea = 0;
8296         s.left = (l+a.l)+"px";
8297         s.top = (t+a.t)+"px";
8298         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8299  
8300         if(s.width != sws || s.height != shs){
8301             s.width = sws;
8302             s.height = shs;
8303             if(!Roo.isIE){
8304                 var cn = d.childNodes;
8305                 var sww = Math.max(0, (sw-12))+"px";
8306                 cn[0].childNodes[1].style.width = sww;
8307                 cn[1].childNodes[1].style.width = sww;
8308                 cn[2].childNodes[1].style.width = sww;
8309                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8310             }
8311         }
8312     },
8313
8314     /**
8315      * Hides this shadow
8316      */
8317     hide : function(){
8318         if(this.el){
8319             this.el.dom.style.display = "none";
8320             Roo.Shadow.Pool.push(this.el);
8321             delete this.el;
8322         }
8323     },
8324
8325     /**
8326      * Adjust the z-index of this shadow
8327      * @param {Number} zindex The new z-index
8328      */
8329     setZIndex : function(z){
8330         this.zIndex = z;
8331         if(this.el){
8332             this.el.setStyle("z-index", z);
8333         }
8334     }
8335 };
8336
8337 // Private utility class that manages the internal Shadow cache
8338 Roo.Shadow.Pool = function(){
8339     var p = [];
8340     var markup = Roo.isIE ?
8341                  '<div class="x-ie-shadow"></div>' :
8342                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8343     return {
8344         pull : function(){
8345             var sh = p.shift();
8346             if(!sh){
8347                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8348                 sh.autoBoxAdjust = false;
8349             }
8350             return sh;
8351         },
8352
8353         push : function(sh){
8354             p.push(sh);
8355         }
8356     };
8357 }();/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367
8368 /**
8369  * @class Roo.BoxComponent
8370  * @extends Roo.Component
8371  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8372  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8373  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8374  * layout containers.
8375  * @constructor
8376  * @param {Roo.Element/String/Object} config The configuration options.
8377  */
8378 Roo.BoxComponent = function(config){
8379     Roo.Component.call(this, config);
8380     this.addEvents({
8381         /**
8382          * @event resize
8383          * Fires after the component is resized.
8384              * @param {Roo.Component} this
8385              * @param {Number} adjWidth The box-adjusted width that was set
8386              * @param {Number} adjHeight The box-adjusted height that was set
8387              * @param {Number} rawWidth The width that was originally specified
8388              * @param {Number} rawHeight The height that was originally specified
8389              */
8390         resize : true,
8391         /**
8392          * @event move
8393          * Fires after the component is moved.
8394              * @param {Roo.Component} this
8395              * @param {Number} x The new x position
8396              * @param {Number} y The new y position
8397              */
8398         move : true
8399     });
8400 };
8401
8402 Roo.extend(Roo.BoxComponent, Roo.Component, {
8403     // private, set in afterRender to signify that the component has been rendered
8404     boxReady : false,
8405     // private, used to defer height settings to subclasses
8406     deferHeight: false,
8407     /** @cfg {Number} width
8408      * width (optional) size of component
8409      */
8410      /** @cfg {Number} height
8411      * height (optional) size of component
8412      */
8413      
8414     /**
8415      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8416      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8417      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8418      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8419      * @return {Roo.BoxComponent} this
8420      */
8421     setSize : function(w, h){
8422         // support for standard size objects
8423         if(typeof w == 'object'){
8424             h = w.height;
8425             w = w.width;
8426         }
8427         // not rendered
8428         if(!this.boxReady){
8429             this.width = w;
8430             this.height = h;
8431             return this;
8432         }
8433
8434         // prevent recalcs when not needed
8435         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8436             return this;
8437         }
8438         this.lastSize = {width: w, height: h};
8439
8440         var adj = this.adjustSize(w, h);
8441         var aw = adj.width, ah = adj.height;
8442         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8443             var rz = this.getResizeEl();
8444             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8445                 rz.setSize(aw, ah);
8446             }else if(!this.deferHeight && ah !== undefined){
8447                 rz.setHeight(ah);
8448             }else if(aw !== undefined){
8449                 rz.setWidth(aw);
8450             }
8451             this.onResize(aw, ah, w, h);
8452             this.fireEvent('resize', this, aw, ah, w, h);
8453         }
8454         return this;
8455     },
8456
8457     /**
8458      * Gets the current size of the component's underlying element.
8459      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8460      */
8461     getSize : function(){
8462         return this.el.getSize();
8463     },
8464
8465     /**
8466      * Gets the current XY position of the component's underlying element.
8467      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8468      * @return {Array} The XY position of the element (e.g., [100, 200])
8469      */
8470     getPosition : function(local){
8471         if(local === true){
8472             return [this.el.getLeft(true), this.el.getTop(true)];
8473         }
8474         return this.xy || this.el.getXY();
8475     },
8476
8477     /**
8478      * Gets the current box measurements of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @returns {Object} box An object in the format {x, y, width, height}
8481      */
8482     getBox : function(local){
8483         var s = this.el.getSize();
8484         if(local){
8485             s.x = this.el.getLeft(true);
8486             s.y = this.el.getTop(true);
8487         }else{
8488             var xy = this.xy || this.el.getXY();
8489             s.x = xy[0];
8490             s.y = xy[1];
8491         }
8492         return s;
8493     },
8494
8495     /**
8496      * Sets the current box measurements of the component's underlying element.
8497      * @param {Object} box An object in the format {x, y, width, height}
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     updateBox : function(box){
8501         this.setSize(box.width, box.height);
8502         this.setPagePosition(box.x, box.y);
8503         return this;
8504     },
8505
8506     // protected
8507     getResizeEl : function(){
8508         return this.resizeEl || this.el;
8509     },
8510
8511     // protected
8512     getPositionEl : function(){
8513         return this.positionEl || this.el;
8514     },
8515
8516     /**
8517      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8518      * This method fires the move event.
8519      * @param {Number} left The new left
8520      * @param {Number} top The new top
8521      * @returns {Roo.BoxComponent} this
8522      */
8523     setPosition : function(x, y){
8524         this.x = x;
8525         this.y = y;
8526         if(!this.boxReady){
8527             return this;
8528         }
8529         var adj = this.adjustPosition(x, y);
8530         var ax = adj.x, ay = adj.y;
8531
8532         var el = this.getPositionEl();
8533         if(ax !== undefined || ay !== undefined){
8534             if(ax !== undefined && ay !== undefined){
8535                 el.setLeftTop(ax, ay);
8536             }else if(ax !== undefined){
8537                 el.setLeft(ax);
8538             }else if(ay !== undefined){
8539                 el.setTop(ay);
8540             }
8541             this.onPosition(ax, ay);
8542             this.fireEvent('move', this, ax, ay);
8543         }
8544         return this;
8545     },
8546
8547     /**
8548      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8549      * This method fires the move event.
8550      * @param {Number} x The new x position
8551      * @param {Number} y The new y position
8552      * @returns {Roo.BoxComponent} this
8553      */
8554     setPagePosition : function(x, y){
8555         this.pageX = x;
8556         this.pageY = y;
8557         if(!this.boxReady){
8558             return;
8559         }
8560         if(x === undefined || y === undefined){ // cannot translate undefined points
8561             return;
8562         }
8563         var p = this.el.translatePoints(x, y);
8564         this.setPosition(p.left, p.top);
8565         return this;
8566     },
8567
8568     // private
8569     onRender : function(ct, position){
8570         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8571         if(this.resizeEl){
8572             this.resizeEl = Roo.get(this.resizeEl);
8573         }
8574         if(this.positionEl){
8575             this.positionEl = Roo.get(this.positionEl);
8576         }
8577     },
8578
8579     // private
8580     afterRender : function(){
8581         Roo.BoxComponent.superclass.afterRender.call(this);
8582         this.boxReady = true;
8583         this.setSize(this.width, this.height);
8584         if(this.x || this.y){
8585             this.setPosition(this.x, this.y);
8586         }
8587         if(this.pageX || this.pageY){
8588             this.setPagePosition(this.pageX, this.pageY);
8589         }
8590     },
8591
8592     /**
8593      * Force the component's size to recalculate based on the underlying element's current height and width.
8594      * @returns {Roo.BoxComponent} this
8595      */
8596     syncSize : function(){
8597         delete this.lastSize;
8598         this.setSize(this.el.getWidth(), this.el.getHeight());
8599         return this;
8600     },
8601
8602     /**
8603      * Called after the component is resized, this method is empty by default but can be implemented by any
8604      * subclass that needs to perform custom logic after a resize occurs.
8605      * @param {Number} adjWidth The box-adjusted width that was set
8606      * @param {Number} adjHeight The box-adjusted height that was set
8607      * @param {Number} rawWidth The width that was originally specified
8608      * @param {Number} rawHeight The height that was originally specified
8609      */
8610     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8611
8612     },
8613
8614     /**
8615      * Called after the component is moved, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a move occurs.
8617      * @param {Number} x The new x position
8618      * @param {Number} y The new y position
8619      */
8620     onPosition : function(x, y){
8621
8622     },
8623
8624     // private
8625     adjustSize : function(w, h){
8626         if(this.autoWidth){
8627             w = 'auto';
8628         }
8629         if(this.autoHeight){
8630             h = 'auto';
8631         }
8632         return {width : w, height: h};
8633     },
8634
8635     // private
8636     adjustPosition : function(x, y){
8637         return {x : x, y: y};
8638     }
8639 });/*
8640  * Based on:
8641  * Ext JS Library 1.1.1
8642  * Copyright(c) 2006-2007, Ext JS, LLC.
8643  *
8644  * Originally Released Under LGPL - original licence link has changed is not relivant.
8645  *
8646  * Fork - LGPL
8647  * <script type="text/javascript">
8648  */
8649
8650
8651 /**
8652  * @class Roo.SplitBar
8653  * @extends Roo.util.Observable
8654  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8655  * <br><br>
8656  * Usage:
8657  * <pre><code>
8658 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8659                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8660 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8661 split.minSize = 100;
8662 split.maxSize = 600;
8663 split.animate = true;
8664 split.on('moved', splitterMoved);
8665 </code></pre>
8666  * @constructor
8667  * Create a new SplitBar
8668  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8669  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8670  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8671  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8672                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8673                         position of the SplitBar).
8674  */
8675 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8676     
8677     /** @private */
8678     this.el = Roo.get(dragElement, true);
8679     this.el.dom.unselectable = "on";
8680     /** @private */
8681     this.resizingEl = Roo.get(resizingElement, true);
8682
8683     /**
8684      * @private
8685      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8686      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8687      * @type Number
8688      */
8689     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8690     
8691     /**
8692      * The minimum size of the resizing element. (Defaults to 0)
8693      * @type Number
8694      */
8695     this.minSize = 0;
8696     
8697     /**
8698      * The maximum size of the resizing element. (Defaults to 2000)
8699      * @type Number
8700      */
8701     this.maxSize = 2000;
8702     
8703     /**
8704      * Whether to animate the transition to the new size
8705      * @type Boolean
8706      */
8707     this.animate = false;
8708     
8709     /**
8710      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8711      * @type Boolean
8712      */
8713     this.useShim = false;
8714     
8715     /** @private */
8716     this.shim = null;
8717     
8718     if(!existingProxy){
8719         /** @private */
8720         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8721     }else{
8722         this.proxy = Roo.get(existingProxy).dom;
8723     }
8724     /** @private */
8725     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8726     
8727     /** @private */
8728     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8729     
8730     /** @private */
8731     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8732     
8733     /** @private */
8734     this.dragSpecs = {};
8735     
8736     /**
8737      * @private The adapter to use to positon and resize elements
8738      */
8739     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8740     this.adapter.init(this);
8741     
8742     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8743         /** @private */
8744         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8745         this.el.addClass("x-splitbar-h");
8746     }else{
8747         /** @private */
8748         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8749         this.el.addClass("x-splitbar-v");
8750     }
8751     
8752     this.addEvents({
8753         /**
8754          * @event resize
8755          * Fires when the splitter is moved (alias for {@link #event-moved})
8756          * @param {Roo.SplitBar} this
8757          * @param {Number} newSize the new width or height
8758          */
8759         "resize" : true,
8760         /**
8761          * @event moved
8762          * Fires when the splitter is moved
8763          * @param {Roo.SplitBar} this
8764          * @param {Number} newSize the new width or height
8765          */
8766         "moved" : true,
8767         /**
8768          * @event beforeresize
8769          * Fires before the splitter is dragged
8770          * @param {Roo.SplitBar} this
8771          */
8772         "beforeresize" : true,
8773
8774         "beforeapply" : true
8775     });
8776
8777     Roo.util.Observable.call(this);
8778 };
8779
8780 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8781     onStartProxyDrag : function(x, y){
8782         this.fireEvent("beforeresize", this);
8783         if(!this.overlay){
8784             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8785             o.unselectable();
8786             o.enableDisplayMode("block");
8787             // all splitbars share the same overlay
8788             Roo.SplitBar.prototype.overlay = o;
8789         }
8790         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8791         this.overlay.show();
8792         Roo.get(this.proxy).setDisplayed("block");
8793         var size = this.adapter.getElementSize(this);
8794         this.activeMinSize = this.getMinimumSize();;
8795         this.activeMaxSize = this.getMaximumSize();;
8796         var c1 = size - this.activeMinSize;
8797         var c2 = Math.max(this.activeMaxSize - size, 0);
8798         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8799             this.dd.resetConstraints();
8800             this.dd.setXConstraint(
8801                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8802                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8803             );
8804             this.dd.setYConstraint(0, 0);
8805         }else{
8806             this.dd.resetConstraints();
8807             this.dd.setXConstraint(0, 0);
8808             this.dd.setYConstraint(
8809                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8810                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8811             );
8812          }
8813         this.dragSpecs.startSize = size;
8814         this.dragSpecs.startPoint = [x, y];
8815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8816     },
8817     
8818     /** 
8819      * @private Called after the drag operation by the DDProxy
8820      */
8821     onEndProxyDrag : function(e){
8822         Roo.get(this.proxy).setDisplayed(false);
8823         var endPoint = Roo.lib.Event.getXY(e);
8824         if(this.overlay){
8825             this.overlay.hide();
8826         }
8827         var newSize;
8828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8829             newSize = this.dragSpecs.startSize + 
8830                 (this.placement == Roo.SplitBar.LEFT ?
8831                     endPoint[0] - this.dragSpecs.startPoint[0] :
8832                     this.dragSpecs.startPoint[0] - endPoint[0]
8833                 );
8834         }else{
8835             newSize = this.dragSpecs.startSize + 
8836                 (this.placement == Roo.SplitBar.TOP ?
8837                     endPoint[1] - this.dragSpecs.startPoint[1] :
8838                     this.dragSpecs.startPoint[1] - endPoint[1]
8839                 );
8840         }
8841         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8842         if(newSize != this.dragSpecs.startSize){
8843             if(this.fireEvent('beforeapply', this, newSize) !== false){
8844                 this.adapter.setElementSize(this, newSize);
8845                 this.fireEvent("moved", this, newSize);
8846                 this.fireEvent("resize", this, newSize);
8847             }
8848         }
8849     },
8850     
8851     /**
8852      * Get the adapter this SplitBar uses
8853      * @return The adapter object
8854      */
8855     getAdapter : function(){
8856         return this.adapter;
8857     },
8858     
8859     /**
8860      * Set the adapter this SplitBar uses
8861      * @param {Object} adapter A SplitBar adapter object
8862      */
8863     setAdapter : function(adapter){
8864         this.adapter = adapter;
8865         this.adapter.init(this);
8866     },
8867     
8868     /**
8869      * Gets the minimum size for the resizing element
8870      * @return {Number} The minimum size
8871      */
8872     getMinimumSize : function(){
8873         return this.minSize;
8874     },
8875     
8876     /**
8877      * Sets the minimum size for the resizing element
8878      * @param {Number} minSize The minimum size
8879      */
8880     setMinimumSize : function(minSize){
8881         this.minSize = minSize;
8882     },
8883     
8884     /**
8885      * Gets the maximum size for the resizing element
8886      * @return {Number} The maximum size
8887      */
8888     getMaximumSize : function(){
8889         return this.maxSize;
8890     },
8891     
8892     /**
8893      * Sets the maximum size for the resizing element
8894      * @param {Number} maxSize The maximum size
8895      */
8896     setMaximumSize : function(maxSize){
8897         this.maxSize = maxSize;
8898     },
8899     
8900     /**
8901      * Sets the initialize size for the resizing element
8902      * @param {Number} size The initial size
8903      */
8904     setCurrentSize : function(size){
8905         var oldAnimate = this.animate;
8906         this.animate = false;
8907         this.adapter.setElementSize(this, size);
8908         this.animate = oldAnimate;
8909     },
8910     
8911     /**
8912      * Destroy this splitbar. 
8913      * @param {Boolean} removeEl True to remove the element
8914      */
8915     destroy : function(removeEl){
8916         if(this.shim){
8917             this.shim.remove();
8918         }
8919         this.dd.unreg();
8920         this.proxy.parentNode.removeChild(this.proxy);
8921         if(removeEl){
8922             this.el.remove();
8923         }
8924     }
8925 });
8926
8927 /**
8928  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8929  */
8930 Roo.SplitBar.createProxy = function(dir){
8931     var proxy = new Roo.Element(document.createElement("div"));
8932     proxy.unselectable();
8933     var cls = 'x-splitbar-proxy';
8934     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8935     document.body.appendChild(proxy.dom);
8936     return proxy.dom;
8937 };
8938
8939 /** 
8940  * @class Roo.SplitBar.BasicLayoutAdapter
8941  * Default Adapter. It assumes the splitter and resizing element are not positioned
8942  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8943  */
8944 Roo.SplitBar.BasicLayoutAdapter = function(){
8945 };
8946
8947 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8948     // do nothing for now
8949     init : function(s){
8950     
8951     },
8952     /**
8953      * Called before drag operations to get the current size of the resizing element. 
8954      * @param {Roo.SplitBar} s The SplitBar using this adapter
8955      */
8956      getElementSize : function(s){
8957         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8958             return s.resizingEl.getWidth();
8959         }else{
8960             return s.resizingEl.getHeight();
8961         }
8962     },
8963     
8964     /**
8965      * Called after drag operations to set the size of the resizing element.
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      * @param {Number} newSize The new size to set
8968      * @param {Function} onComplete A function to be invoked when resizing is complete
8969      */
8970     setElementSize : function(s, newSize, onComplete){
8971         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8972             if(!s.animate){
8973                 s.resizingEl.setWidth(newSize);
8974                 if(onComplete){
8975                     onComplete(s, newSize);
8976                 }
8977             }else{
8978                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8979             }
8980         }else{
8981             
8982             if(!s.animate){
8983                 s.resizingEl.setHeight(newSize);
8984                 if(onComplete){
8985                     onComplete(s, newSize);
8986                 }
8987             }else{
8988                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8989             }
8990         }
8991     }
8992 };
8993
8994 /** 
8995  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8996  * @extends Roo.SplitBar.BasicLayoutAdapter
8997  * Adapter that  moves the splitter element to align with the resized sizing element. 
8998  * Used with an absolute positioned SplitBar.
8999  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9000  * document.body, make sure you assign an id to the body element.
9001  */
9002 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9003     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9004     this.container = Roo.get(container);
9005 };
9006
9007 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9008     init : function(s){
9009         this.basic.init(s);
9010     },
9011     
9012     getElementSize : function(s){
9013         return this.basic.getElementSize(s);
9014     },
9015     
9016     setElementSize : function(s, newSize, onComplete){
9017         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9018     },
9019     
9020     moveSplitter : function(s){
9021         var yes = Roo.SplitBar;
9022         switch(s.placement){
9023             case yes.LEFT:
9024                 s.el.setX(s.resizingEl.getRight());
9025                 break;
9026             case yes.RIGHT:
9027                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9028                 break;
9029             case yes.TOP:
9030                 s.el.setY(s.resizingEl.getBottom());
9031                 break;
9032             case yes.BOTTOM:
9033                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9034                 break;
9035         }
9036     }
9037 };
9038
9039 /**
9040  * Orientation constant - Create a vertical SplitBar
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.VERTICAL = 1;
9045
9046 /**
9047  * Orientation constant - Create a horizontal SplitBar
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.HORIZONTAL = 2;
9052
9053 /**
9054  * Placement constant - The resizing element is to the left of the splitter element
9055  * @static
9056  * @type Number
9057  */
9058 Roo.SplitBar.LEFT = 1;
9059
9060 /**
9061  * Placement constant - The resizing element is to the right of the splitter element
9062  * @static
9063  * @type Number
9064  */
9065 Roo.SplitBar.RIGHT = 2;
9066
9067 /**
9068  * Placement constant - The resizing element is positioned above the splitter element
9069  * @static
9070  * @type Number
9071  */
9072 Roo.SplitBar.TOP = 3;
9073
9074 /**
9075  * Placement constant - The resizing element is positioned under splitter element
9076  * @static
9077  * @type Number
9078  */
9079 Roo.SplitBar.BOTTOM = 4;
9080 /*
9081  * Based on:
9082  * Ext JS Library 1.1.1
9083  * Copyright(c) 2006-2007, Ext JS, LLC.
9084  *
9085  * Originally Released Under LGPL - original licence link has changed is not relivant.
9086  *
9087  * Fork - LGPL
9088  * <script type="text/javascript">
9089  */
9090
9091 /**
9092  * @class Roo.View
9093  * @extends Roo.util.Observable
9094  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9095  * This class also supports single and multi selection modes. <br>
9096  * Create a data model bound view:
9097  <pre><code>
9098  var store = new Roo.data.Store(...);
9099
9100  var view = new Roo.View({
9101     el : "my-element",
9102     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9103  
9104     singleSelect: true,
9105     selectedClass: "ydataview-selected",
9106     store: store
9107  });
9108
9109  // listen for node click?
9110  view.on("click", function(vw, index, node, e){
9111  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9112  });
9113
9114  // load XML data
9115  dataModel.load("foobar.xml");
9116  </code></pre>
9117  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9118  * <br><br>
9119  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9120  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9121  * 
9122  * Note: old style constructor is still suported (container, template, config)
9123  * 
9124  * @constructor
9125  * Create a new View
9126  * @param {Object} config The config object
9127  * 
9128  */
9129 Roo.View = function(config, depreciated_tpl, depreciated_config){
9130     
9131     if (typeof(depreciated_tpl) == 'undefined') {
9132         // new way.. - universal constructor.
9133         Roo.apply(this, config);
9134         this.el  = Roo.get(this.el);
9135     } else {
9136         // old format..
9137         this.el  = Roo.get(config);
9138         this.tpl = depreciated_tpl;
9139         Roo.apply(this, depreciated_config);
9140     }
9141      
9142     
9143     if(typeof(this.tpl) == "string"){
9144         this.tpl = new Roo.Template(this.tpl);
9145     } else {
9146         // support xtype ctors..
9147         this.tpl = new Roo.factory(this.tpl, Roo);
9148     }
9149     
9150     
9151     this.tpl.compile();
9152    
9153
9154      
9155     /** @private */
9156     this.addEvents({
9157     /**
9158      * @event beforeclick
9159      * Fires before a click is processed. Returns false to cancel the default action.
9160      * @param {Roo.View} this
9161      * @param {Number} index The index of the target node
9162      * @param {HTMLElement} node The target node
9163      * @param {Roo.EventObject} e The raw event object
9164      */
9165         "beforeclick" : true,
9166     /**
9167      * @event click
9168      * Fires when a template node is clicked.
9169      * @param {Roo.View} this
9170      * @param {Number} index The index of the target node
9171      * @param {HTMLElement} node The target node
9172      * @param {Roo.EventObject} e The raw event object
9173      */
9174         "click" : true,
9175     /**
9176      * @event dblclick
9177      * Fires when a template node is double clicked.
9178      * @param {Roo.View} this
9179      * @param {Number} index The index of the target node
9180      * @param {HTMLElement} node The target node
9181      * @param {Roo.EventObject} e The raw event object
9182      */
9183         "dblclick" : true,
9184     /**
9185      * @event contextmenu
9186      * Fires when a template node is right clicked.
9187      * @param {Roo.View} this
9188      * @param {Number} index The index of the target node
9189      * @param {HTMLElement} node The target node
9190      * @param {Roo.EventObject} e The raw event object
9191      */
9192         "contextmenu" : true,
9193     /**
9194      * @event selectionchange
9195      * Fires when the selected nodes change.
9196      * @param {Roo.View} this
9197      * @param {Array} selections Array of the selected nodes
9198      */
9199         "selectionchange" : true,
9200
9201     /**
9202      * @event beforeselect
9203      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9204      * @param {Roo.View} this
9205      * @param {HTMLElement} node The node to be selected
9206      * @param {Array} selections Array of currently selected nodes
9207      */
9208         "beforeselect" : true
9209     });
9210
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     // returns the "tr" element.
11416     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11417         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11418         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11419     return strip.firstChild.firstChild.firstChild.firstChild;
11420 };
11421 /** @private */
11422 Roo.TabPanel.prototype.createBody = function(container){
11423     var body = document.createElement("div");
11424     Roo.id(body, "tab-body");
11425     Roo.fly(body).addClass("x-tabs-body");
11426     container.appendChild(body);
11427     return body;
11428 };
11429 /** @private */
11430 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11431     var body = Roo.getDom(id);
11432     if(!body){
11433         body = document.createElement("div");
11434         body.id = id;
11435     }
11436     Roo.fly(body).addClass("x-tabs-item-body");
11437     bodyEl.insertBefore(body, bodyEl.firstChild);
11438     return body;
11439 };
11440 /** @private */
11441 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11442     var td = document.createElement("td");
11443     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11444     //stripEl.appendChild(td);
11445     if(closable){
11446         td.className = "x-tabs-closable";
11447         if(!this.closeTpl){
11448             this.closeTpl = new Roo.Template(
11449                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11450                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11451                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11452             );
11453         }
11454         var el = this.closeTpl.overwrite(td, {"text": text});
11455         var close = el.getElementsByTagName("div")[0];
11456         var inner = el.getElementsByTagName("em")[0];
11457         return {"el": el, "close": close, "inner": inner};
11458     } else {
11459         if(!this.tabTpl){
11460             this.tabTpl = new Roo.Template(
11461                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11462                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11463             );
11464         }
11465         var el = this.tabTpl.overwrite(td, {"text": text});
11466         var inner = el.getElementsByTagName("em")[0];
11467         return {"el": el, "inner": inner};
11468     }
11469 };/*
11470  * Based on:
11471  * Ext JS Library 1.1.1
11472  * Copyright(c) 2006-2007, Ext JS, LLC.
11473  *
11474  * Originally Released Under LGPL - original licence link has changed is not relivant.
11475  *
11476  * Fork - LGPL
11477  * <script type="text/javascript">
11478  */
11479
11480 /**
11481  * @class Roo.Button
11482  * @extends Roo.util.Observable
11483  * Simple Button class
11484  * @cfg {String} text The button text
11485  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11486  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11487  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11488  * @cfg {Object} scope The scope of the handler
11489  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11490  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11491  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11492  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11493  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11494  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11495    applies if enableToggle = true)
11496  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11497  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11498   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11499  * @constructor
11500  * Create a new button
11501  * @param {Object} config The config object
11502  */
11503 Roo.Button = function(renderTo, config)
11504 {
11505     if (!config) {
11506         config = renderTo;
11507         renderTo = config.renderTo || false;
11508     }
11509     
11510     Roo.apply(this, config);
11511     this.addEvents({
11512         /**
11513              * @event click
11514              * Fires when this button is clicked
11515              * @param {Button} this
11516              * @param {EventObject} e The click event
11517              */
11518             "click" : true,
11519         /**
11520              * @event toggle
11521              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11522              * @param {Button} this
11523              * @param {Boolean} pressed
11524              */
11525             "toggle" : true,
11526         /**
11527              * @event mouseover
11528              * Fires when the mouse hovers over the button
11529              * @param {Button} this
11530              * @param {Event} e The event object
11531              */
11532         'mouseover' : true,
11533         /**
11534              * @event mouseout
11535              * Fires when the mouse exits the button
11536              * @param {Button} this
11537              * @param {Event} e The event object
11538              */
11539         'mouseout': true,
11540          /**
11541              * @event render
11542              * Fires when the button is rendered
11543              * @param {Button} this
11544              */
11545         'render': true
11546     });
11547     if(this.menu){
11548         this.menu = Roo.menu.MenuMgr.get(this.menu);
11549     }
11550     // register listeners first!!  - so render can be captured..
11551     Roo.util.Observable.call(this);
11552     if(renderTo){
11553         this.render(renderTo);
11554     }
11555     
11556   
11557 };
11558
11559 Roo.extend(Roo.Button, Roo.util.Observable, {
11560     /**
11561      * 
11562      */
11563     
11564     /**
11565      * Read-only. True if this button is hidden
11566      * @type Boolean
11567      */
11568     hidden : false,
11569     /**
11570      * Read-only. True if this button is disabled
11571      * @type Boolean
11572      */
11573     disabled : false,
11574     /**
11575      * Read-only. True if this button is pressed (only if enableToggle = true)
11576      * @type Boolean
11577      */
11578     pressed : false,
11579
11580     /**
11581      * @cfg {Number} tabIndex 
11582      * The DOM tabIndex for this button (defaults to undefined)
11583      */
11584     tabIndex : undefined,
11585
11586     /**
11587      * @cfg {Boolean} enableToggle
11588      * True to enable pressed/not pressed toggling (defaults to false)
11589      */
11590     enableToggle: false,
11591     /**
11592      * @cfg {Mixed} menu
11593      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11594      */
11595     menu : undefined,
11596     /**
11597      * @cfg {String} menuAlign
11598      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11599      */
11600     menuAlign : "tl-bl?",
11601
11602     /**
11603      * @cfg {String} iconCls
11604      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11605      */
11606     iconCls : undefined,
11607     /**
11608      * @cfg {String} type
11609      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11610      */
11611     type : 'button',
11612
11613     // private
11614     menuClassTarget: 'tr',
11615
11616     /**
11617      * @cfg {String} clickEvent
11618      * The type of event to map to the button's event handler (defaults to 'click')
11619      */
11620     clickEvent : 'click',
11621
11622     /**
11623      * @cfg {Boolean} handleMouseEvents
11624      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11625      */
11626     handleMouseEvents : true,
11627
11628     /**
11629      * @cfg {String} tooltipType
11630      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11631      */
11632     tooltipType : 'qtip',
11633
11634     /**
11635      * @cfg {String} cls
11636      * A CSS class to apply to the button's main element.
11637      */
11638     
11639     /**
11640      * @cfg {Roo.Template} template (Optional)
11641      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11642      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11643      * require code modifications if required elements (e.g. a button) aren't present.
11644      */
11645
11646     // private
11647     render : function(renderTo){
11648         var btn;
11649         if(this.hideParent){
11650             this.parentEl = Roo.get(renderTo);
11651         }
11652         if(!this.dhconfig){
11653             if(!this.template){
11654                 if(!Roo.Button.buttonTemplate){
11655                     // hideous table template
11656                     Roo.Button.buttonTemplate = new Roo.Template(
11657                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11658                         '<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>',
11659                         "</tr></tbody></table>");
11660                 }
11661                 this.template = Roo.Button.buttonTemplate;
11662             }
11663             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11664             var btnEl = btn.child("button:first");
11665             btnEl.on('focus', this.onFocus, this);
11666             btnEl.on('blur', this.onBlur, this);
11667             if(this.cls){
11668                 btn.addClass(this.cls);
11669             }
11670             if(this.icon){
11671                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11672             }
11673             if(this.iconCls){
11674                 btnEl.addClass(this.iconCls);
11675                 if(!this.cls){
11676                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11677                 }
11678             }
11679             if(this.tabIndex !== undefined){
11680                 btnEl.dom.tabIndex = this.tabIndex;
11681             }
11682             if(this.tooltip){
11683                 if(typeof this.tooltip == 'object'){
11684                     Roo.QuickTips.tips(Roo.apply({
11685                           target: btnEl.id
11686                     }, this.tooltip));
11687                 } else {
11688                     btnEl.dom[this.tooltipType] = this.tooltip;
11689                 }
11690             }
11691         }else{
11692             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11693         }
11694         this.el = btn;
11695         if(this.id){
11696             this.el.dom.id = this.el.id = this.id;
11697         }
11698         if(this.menu){
11699             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11700             this.menu.on("show", this.onMenuShow, this);
11701             this.menu.on("hide", this.onMenuHide, this);
11702         }
11703         btn.addClass("x-btn");
11704         if(Roo.isIE && !Roo.isIE7){
11705             this.autoWidth.defer(1, this);
11706         }else{
11707             this.autoWidth();
11708         }
11709         if(this.handleMouseEvents){
11710             btn.on("mouseover", this.onMouseOver, this);
11711             btn.on("mouseout", this.onMouseOut, this);
11712             btn.on("mousedown", this.onMouseDown, this);
11713         }
11714         btn.on(this.clickEvent, this.onClick, this);
11715         //btn.on("mouseup", this.onMouseUp, this);
11716         if(this.hidden){
11717             this.hide();
11718         }
11719         if(this.disabled){
11720             this.disable();
11721         }
11722         Roo.ButtonToggleMgr.register(this);
11723         if(this.pressed){
11724             this.el.addClass("x-btn-pressed");
11725         }
11726         if(this.repeat){
11727             var repeater = new Roo.util.ClickRepeater(btn,
11728                 typeof this.repeat == "object" ? this.repeat : {}
11729             );
11730             repeater.on("click", this.onClick,  this);
11731         }
11732         
11733         this.fireEvent('render', this);
11734         
11735     },
11736     /**
11737      * Returns the button's underlying element
11738      * @return {Roo.Element} The element
11739      */
11740     getEl : function(){
11741         return this.el;  
11742     },
11743     
11744     /**
11745      * Destroys this Button and removes any listeners.
11746      */
11747     destroy : function(){
11748         Roo.ButtonToggleMgr.unregister(this);
11749         this.el.removeAllListeners();
11750         this.purgeListeners();
11751         this.el.remove();
11752     },
11753
11754     // private
11755     autoWidth : function(){
11756         if(this.el){
11757             this.el.setWidth("auto");
11758             if(Roo.isIE7 && Roo.isStrict){
11759                 var ib = this.el.child('button');
11760                 if(ib && ib.getWidth() > 20){
11761                     ib.clip();
11762                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11763                 }
11764             }
11765             if(this.minWidth){
11766                 if(this.hidden){
11767                     this.el.beginMeasure();
11768                 }
11769                 if(this.el.getWidth() < this.minWidth){
11770                     this.el.setWidth(this.minWidth);
11771                 }
11772                 if(this.hidden){
11773                     this.el.endMeasure();
11774                 }
11775             }
11776         }
11777     },
11778
11779     /**
11780      * Assigns this button's click handler
11781      * @param {Function} handler The function to call when the button is clicked
11782      * @param {Object} scope (optional) Scope for the function passed in
11783      */
11784     setHandler : function(handler, scope){
11785         this.handler = handler;
11786         this.scope = scope;  
11787     },
11788     
11789     /**
11790      * Sets this button's text
11791      * @param {String} text The button text
11792      */
11793     setText : function(text){
11794         this.text = text;
11795         if(this.el){
11796             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11797         }
11798         this.autoWidth();
11799     },
11800     
11801     /**
11802      * Gets the text for this button
11803      * @return {String} The button text
11804      */
11805     getText : function(){
11806         return this.text;  
11807     },
11808     
11809     /**
11810      * Show this button
11811      */
11812     show: function(){
11813         this.hidden = false;
11814         if(this.el){
11815             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11816         }
11817     },
11818     
11819     /**
11820      * Hide this button
11821      */
11822     hide: function(){
11823         this.hidden = true;
11824         if(this.el){
11825             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11826         }
11827     },
11828     
11829     /**
11830      * Convenience function for boolean show/hide
11831      * @param {Boolean} visible True to show, false to hide
11832      */
11833     setVisible: function(visible){
11834         if(visible) {
11835             this.show();
11836         }else{
11837             this.hide();
11838         }
11839     },
11840     
11841     /**
11842      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11843      * @param {Boolean} state (optional) Force a particular state
11844      */
11845     toggle : function(state){
11846         state = state === undefined ? !this.pressed : state;
11847         if(state != this.pressed){
11848             if(state){
11849                 this.el.addClass("x-btn-pressed");
11850                 this.pressed = true;
11851                 this.fireEvent("toggle", this, true);
11852             }else{
11853                 this.el.removeClass("x-btn-pressed");
11854                 this.pressed = false;
11855                 this.fireEvent("toggle", this, false);
11856             }
11857             if(this.toggleHandler){
11858                 this.toggleHandler.call(this.scope || this, this, state);
11859             }
11860         }
11861     },
11862     
11863     /**
11864      * Focus the button
11865      */
11866     focus : function(){
11867         this.el.child('button:first').focus();
11868     },
11869     
11870     /**
11871      * Disable this button
11872      */
11873     disable : function(){
11874         if(this.el){
11875             this.el.addClass("x-btn-disabled");
11876         }
11877         this.disabled = true;
11878     },
11879     
11880     /**
11881      * Enable this button
11882      */
11883     enable : function(){
11884         if(this.el){
11885             this.el.removeClass("x-btn-disabled");
11886         }
11887         this.disabled = false;
11888     },
11889
11890     /**
11891      * Convenience function for boolean enable/disable
11892      * @param {Boolean} enabled True to enable, false to disable
11893      */
11894     setDisabled : function(v){
11895         this[v !== true ? "enable" : "disable"]();
11896     },
11897
11898     // private
11899     onClick : function(e){
11900         if(e){
11901             e.preventDefault();
11902         }
11903         if(e.button != 0){
11904             return;
11905         }
11906         if(!this.disabled){
11907             if(this.enableToggle){
11908                 this.toggle();
11909             }
11910             if(this.menu && !this.menu.isVisible()){
11911                 this.menu.show(this.el, this.menuAlign);
11912             }
11913             this.fireEvent("click", this, e);
11914             if(this.handler){
11915                 this.el.removeClass("x-btn-over");
11916                 this.handler.call(this.scope || this, this, e);
11917             }
11918         }
11919     },
11920     // private
11921     onMouseOver : function(e){
11922         if(!this.disabled){
11923             this.el.addClass("x-btn-over");
11924             this.fireEvent('mouseover', this, e);
11925         }
11926     },
11927     // private
11928     onMouseOut : function(e){
11929         if(!e.within(this.el,  true)){
11930             this.el.removeClass("x-btn-over");
11931             this.fireEvent('mouseout', this, e);
11932         }
11933     },
11934     // private
11935     onFocus : function(e){
11936         if(!this.disabled){
11937             this.el.addClass("x-btn-focus");
11938         }
11939     },
11940     // private
11941     onBlur : function(e){
11942         this.el.removeClass("x-btn-focus");
11943     },
11944     // private
11945     onMouseDown : function(e){
11946         if(!this.disabled && e.button == 0){
11947             this.el.addClass("x-btn-click");
11948             Roo.get(document).on('mouseup', this.onMouseUp, this);
11949         }
11950     },
11951     // private
11952     onMouseUp : function(e){
11953         if(e.button == 0){
11954             this.el.removeClass("x-btn-click");
11955             Roo.get(document).un('mouseup', this.onMouseUp, this);
11956         }
11957     },
11958     // private
11959     onMenuShow : function(e){
11960         this.el.addClass("x-btn-menu-active");
11961     },
11962     // private
11963     onMenuHide : function(e){
11964         this.el.removeClass("x-btn-menu-active");
11965     }   
11966 });
11967
11968 // Private utility class used by Button
11969 Roo.ButtonToggleMgr = function(){
11970    var groups = {};
11971    
11972    function toggleGroup(btn, state){
11973        if(state){
11974            var g = groups[btn.toggleGroup];
11975            for(var i = 0, l = g.length; i < l; i++){
11976                if(g[i] != btn){
11977                    g[i].toggle(false);
11978                }
11979            }
11980        }
11981    }
11982    
11983    return {
11984        register : function(btn){
11985            if(!btn.toggleGroup){
11986                return;
11987            }
11988            var g = groups[btn.toggleGroup];
11989            if(!g){
11990                g = groups[btn.toggleGroup] = [];
11991            }
11992            g.push(btn);
11993            btn.on("toggle", toggleGroup);
11994        },
11995        
11996        unregister : function(btn){
11997            if(!btn.toggleGroup){
11998                return;
11999            }
12000            var g = groups[btn.toggleGroup];
12001            if(g){
12002                g.remove(btn);
12003                btn.un("toggle", toggleGroup);
12004            }
12005        }
12006    };
12007 }();/*
12008  * Based on:
12009  * Ext JS Library 1.1.1
12010  * Copyright(c) 2006-2007, Ext JS, LLC.
12011  *
12012  * Originally Released Under LGPL - original licence link has changed is not relivant.
12013  *
12014  * Fork - LGPL
12015  * <script type="text/javascript">
12016  */
12017  
12018 /**
12019  * @class Roo.SplitButton
12020  * @extends Roo.Button
12021  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12022  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12023  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12024  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12025  * @cfg {String} arrowTooltip The title attribute of the arrow
12026  * @constructor
12027  * Create a new menu button
12028  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12029  * @param {Object} config The config object
12030  */
12031 Roo.SplitButton = function(renderTo, config){
12032     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12033     /**
12034      * @event arrowclick
12035      * Fires when this button's arrow is clicked
12036      * @param {SplitButton} this
12037      * @param {EventObject} e The click event
12038      */
12039     this.addEvents({"arrowclick":true});
12040 };
12041
12042 Roo.extend(Roo.SplitButton, Roo.Button, {
12043     render : function(renderTo){
12044         // this is one sweet looking template!
12045         var tpl = new Roo.Template(
12046             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12047             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12048             '<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>',
12049             "</tbody></table></td><td>",
12050             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12051             '<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>',
12052             "</tbody></table></td></tr></table>"
12053         );
12054         var btn = tpl.append(renderTo, [this.text, this.type], true);
12055         var btnEl = btn.child("button");
12056         if(this.cls){
12057             btn.addClass(this.cls);
12058         }
12059         if(this.icon){
12060             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12061         }
12062         if(this.iconCls){
12063             btnEl.addClass(this.iconCls);
12064             if(!this.cls){
12065                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12066             }
12067         }
12068         this.el = btn;
12069         if(this.handleMouseEvents){
12070             btn.on("mouseover", this.onMouseOver, this);
12071             btn.on("mouseout", this.onMouseOut, this);
12072             btn.on("mousedown", this.onMouseDown, this);
12073             btn.on("mouseup", this.onMouseUp, this);
12074         }
12075         btn.on(this.clickEvent, this.onClick, this);
12076         if(this.tooltip){
12077             if(typeof this.tooltip == 'object'){
12078                 Roo.QuickTips.tips(Roo.apply({
12079                       target: btnEl.id
12080                 }, this.tooltip));
12081             } else {
12082                 btnEl.dom[this.tooltipType] = this.tooltip;
12083             }
12084         }
12085         if(this.arrowTooltip){
12086             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12087         }
12088         if(this.hidden){
12089             this.hide();
12090         }
12091         if(this.disabled){
12092             this.disable();
12093         }
12094         if(this.pressed){
12095             this.el.addClass("x-btn-pressed");
12096         }
12097         if(Roo.isIE && !Roo.isIE7){
12098             this.autoWidth.defer(1, this);
12099         }else{
12100             this.autoWidth();
12101         }
12102         if(this.menu){
12103             this.menu.on("show", this.onMenuShow, this);
12104             this.menu.on("hide", this.onMenuHide, this);
12105         }
12106         this.fireEvent('render', this);
12107     },
12108
12109     // private
12110     autoWidth : function(){
12111         if(this.el){
12112             var tbl = this.el.child("table:first");
12113             var tbl2 = this.el.child("table:last");
12114             this.el.setWidth("auto");
12115             tbl.setWidth("auto");
12116             if(Roo.isIE7 && Roo.isStrict){
12117                 var ib = this.el.child('button:first');
12118                 if(ib && ib.getWidth() > 20){
12119                     ib.clip();
12120                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12121                 }
12122             }
12123             if(this.minWidth){
12124                 if(this.hidden){
12125                     this.el.beginMeasure();
12126                 }
12127                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12128                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12129                 }
12130                 if(this.hidden){
12131                     this.el.endMeasure();
12132                 }
12133             }
12134             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12135         } 
12136     },
12137     /**
12138      * Sets this button's click handler
12139      * @param {Function} handler The function to call when the button is clicked
12140      * @param {Object} scope (optional) Scope for the function passed above
12141      */
12142     setHandler : function(handler, scope){
12143         this.handler = handler;
12144         this.scope = scope;  
12145     },
12146     
12147     /**
12148      * Sets this button's arrow click handler
12149      * @param {Function} handler The function to call when the arrow is clicked
12150      * @param {Object} scope (optional) Scope for the function passed above
12151      */
12152     setArrowHandler : function(handler, scope){
12153         this.arrowHandler = handler;
12154         this.scope = scope;  
12155     },
12156     
12157     /**
12158      * Focus the button
12159      */
12160     focus : function(){
12161         if(this.el){
12162             this.el.child("button:first").focus();
12163         }
12164     },
12165
12166     // private
12167     onClick : function(e){
12168         e.preventDefault();
12169         if(!this.disabled){
12170             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12171                 if(this.menu && !this.menu.isVisible()){
12172                     this.menu.show(this.el, this.menuAlign);
12173                 }
12174                 this.fireEvent("arrowclick", this, e);
12175                 if(this.arrowHandler){
12176                     this.arrowHandler.call(this.scope || this, this, e);
12177                 }
12178             }else{
12179                 this.fireEvent("click", this, e);
12180                 if(this.handler){
12181                     this.handler.call(this.scope || this, this, e);
12182                 }
12183             }
12184         }
12185     },
12186     // private
12187     onMouseDown : function(e){
12188         if(!this.disabled){
12189             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12190         }
12191     },
12192     // private
12193     onMouseUp : function(e){
12194         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12195     }   
12196 });
12197
12198
12199 // backwards compat
12200 Roo.MenuButton = Roo.SplitButton;/*
12201  * Based on:
12202  * Ext JS Library 1.1.1
12203  * Copyright(c) 2006-2007, Ext JS, LLC.
12204  *
12205  * Originally Released Under LGPL - original licence link has changed is not relivant.
12206  *
12207  * Fork - LGPL
12208  * <script type="text/javascript">
12209  */
12210
12211 /**
12212  * @class Roo.Toolbar
12213  * Basic Toolbar class.
12214  * @constructor
12215  * Creates a new Toolbar
12216  * @param {Object} config The config object
12217  */ 
12218 Roo.Toolbar = function(container, buttons, config)
12219 {
12220     /// old consturctor format still supported..
12221     if(container instanceof Array){ // omit the container for later rendering
12222         buttons = container;
12223         config = buttons;
12224         container = null;
12225     }
12226     if (typeof(container) == 'object' && container.xtype) {
12227         config = container;
12228         container = config.container;
12229         buttons = config.buttons; // not really - use items!!
12230     }
12231     var xitems = [];
12232     if (config && config.items) {
12233         xitems = config.items;
12234         delete config.items;
12235     }
12236     Roo.apply(this, config);
12237     this.buttons = buttons;
12238     
12239     if(container){
12240         this.render(container);
12241     }
12242     Roo.each(xitems, function(b) {
12243         this.add(b);
12244     }, this);
12245     
12246 };
12247
12248 Roo.Toolbar.prototype = {
12249     /**
12250      * @cfg {Roo.data.Store} items
12251      * array of button configs or elements to add
12252      */
12253     
12254     /**
12255      * @cfg {String/HTMLElement/Element} container
12256      * The id or element that will contain the toolbar
12257      */
12258     // private
12259     render : function(ct){
12260         this.el = Roo.get(ct);
12261         if(this.cls){
12262             this.el.addClass(this.cls);
12263         }
12264         // using a table allows for vertical alignment
12265         // 100% width is needed by Safari...
12266         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12267         this.tr = this.el.child("tr", true);
12268         var autoId = 0;
12269         this.items = new Roo.util.MixedCollection(false, function(o){
12270             return o.id || ("item" + (++autoId));
12271         });
12272         if(this.buttons){
12273             this.add.apply(this, this.buttons);
12274             delete this.buttons;
12275         }
12276     },
12277
12278     /**
12279      * Adds element(s) to the toolbar -- this function takes a variable number of 
12280      * arguments of mixed type and adds them to the toolbar.
12281      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12282      * <ul>
12283      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12284      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12285      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12286      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12287      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12288      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12289      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12290      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12291      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12292      * </ul>
12293      * @param {Mixed} arg2
12294      * @param {Mixed} etc.
12295      */
12296     add : function(){
12297         var a = arguments, l = a.length;
12298         for(var i = 0; i < l; i++){
12299             this._add(a[i]);
12300         }
12301     },
12302     // private..
12303     _add : function(el) {
12304         
12305         if (el.xtype) {
12306             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12307         }
12308         
12309         if (el.applyTo){ // some kind of form field
12310             return this.addField(el);
12311         } 
12312         if (el.render){ // some kind of Toolbar.Item
12313             return this.addItem(el);
12314         }
12315         if (typeof el == "string"){ // string
12316             if(el == "separator" || el == "-"){
12317                 return this.addSeparator();
12318             }
12319             if (el == " "){
12320                 return this.addSpacer();
12321             }
12322             if(el == "->"){
12323                 return this.addFill();
12324             }
12325             return this.addText(el);
12326             
12327         }
12328         if(el.tagName){ // element
12329             return this.addElement(el);
12330         }
12331         if(typeof el == "object"){ // must be button config?
12332             return this.addButton(el);
12333         }
12334         // and now what?!?!
12335         return false;
12336         
12337     },
12338     
12339     /**
12340      * Add an Xtype element
12341      * @param {Object} xtype Xtype Object
12342      * @return {Object} created Object
12343      */
12344     addxtype : function(e){
12345         return this.add(e);  
12346     },
12347     
12348     /**
12349      * Returns the Element for this toolbar.
12350      * @return {Roo.Element}
12351      */
12352     getEl : function(){
12353         return this.el;  
12354     },
12355     
12356     /**
12357      * Adds a separator
12358      * @return {Roo.Toolbar.Item} The separator item
12359      */
12360     addSeparator : function(){
12361         return this.addItem(new Roo.Toolbar.Separator());
12362     },
12363
12364     /**
12365      * Adds a spacer element
12366      * @return {Roo.Toolbar.Spacer} The spacer item
12367      */
12368     addSpacer : function(){
12369         return this.addItem(new Roo.Toolbar.Spacer());
12370     },
12371
12372     /**
12373      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12374      * @return {Roo.Toolbar.Fill} The fill item
12375      */
12376     addFill : function(){
12377         return this.addItem(new Roo.Toolbar.Fill());
12378     },
12379
12380     /**
12381      * Adds any standard HTML element to the toolbar
12382      * @param {String/HTMLElement/Element} el The element or id of the element to add
12383      * @return {Roo.Toolbar.Item} The element's item
12384      */
12385     addElement : function(el){
12386         return this.addItem(new Roo.Toolbar.Item(el));
12387     },
12388     /**
12389      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12390      * @type Roo.util.MixedCollection  
12391      */
12392     items : false,
12393      
12394     /**
12395      * Adds any Toolbar.Item or subclass
12396      * @param {Roo.Toolbar.Item} item
12397      * @return {Roo.Toolbar.Item} The item
12398      */
12399     addItem : function(item){
12400         var td = this.nextBlock();
12401         item.render(td);
12402         this.items.add(item);
12403         return item;
12404     },
12405     
12406     /**
12407      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12408      * @param {Object/Array} config A button config or array of configs
12409      * @return {Roo.Toolbar.Button/Array}
12410      */
12411     addButton : function(config){
12412         if(config instanceof Array){
12413             var buttons = [];
12414             for(var i = 0, len = config.length; i < len; i++) {
12415                 buttons.push(this.addButton(config[i]));
12416             }
12417             return buttons;
12418         }
12419         var b = config;
12420         if(!(config instanceof Roo.Toolbar.Button)){
12421             b = config.split ?
12422                 new Roo.Toolbar.SplitButton(config) :
12423                 new Roo.Toolbar.Button(config);
12424         }
12425         var td = this.nextBlock();
12426         b.render(td);
12427         this.items.add(b);
12428         return b;
12429     },
12430     
12431     /**
12432      * Adds text to the toolbar
12433      * @param {String} text The text to add
12434      * @return {Roo.Toolbar.Item} The element's item
12435      */
12436     addText : function(text){
12437         return this.addItem(new Roo.Toolbar.TextItem(text));
12438     },
12439     
12440     /**
12441      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12442      * @param {Number} index The index where the item is to be inserted
12443      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12444      * @return {Roo.Toolbar.Button/Item}
12445      */
12446     insertButton : function(index, item){
12447         if(item instanceof Array){
12448             var buttons = [];
12449             for(var i = 0, len = item.length; i < len; i++) {
12450                buttons.push(this.insertButton(index + i, item[i]));
12451             }
12452             return buttons;
12453         }
12454         if (!(item instanceof Roo.Toolbar.Button)){
12455            item = new Roo.Toolbar.Button(item);
12456         }
12457         var td = document.createElement("td");
12458         this.tr.insertBefore(td, this.tr.childNodes[index]);
12459         item.render(td);
12460         this.items.insert(index, item);
12461         return item;
12462     },
12463     
12464     /**
12465      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12466      * @param {Object} config
12467      * @return {Roo.Toolbar.Item} The element's item
12468      */
12469     addDom : function(config, returnEl){
12470         var td = this.nextBlock();
12471         Roo.DomHelper.overwrite(td, config);
12472         var ti = new Roo.Toolbar.Item(td.firstChild);
12473         ti.render(td);
12474         this.items.add(ti);
12475         return ti;
12476     },
12477
12478     /**
12479      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12480      * @type Roo.util.MixedCollection  
12481      */
12482     fields : false,
12483     
12484     /**
12485      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12486      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12487      * @param {Roo.form.Field} field
12488      * @return {Roo.ToolbarItem}
12489      */
12490      
12491       
12492     addField : function(field) {
12493         if (!this.fields) {
12494             var autoId = 0;
12495             this.fields = new Roo.util.MixedCollection(false, function(o){
12496                 return o.id || ("item" + (++autoId));
12497             });
12498
12499         }
12500         
12501         var td = this.nextBlock();
12502         field.render(td);
12503         var ti = new Roo.Toolbar.Item(td.firstChild);
12504         ti.render(td);
12505         this.items.add(ti);
12506         this.fields.add(field);
12507         return ti;
12508     },
12509     /**
12510      * Hide the toolbar
12511      * @method hide
12512      */
12513      
12514       
12515     hide : function()
12516     {
12517         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12518         this.el.child('div').hide();
12519     },
12520     /**
12521      * Show the toolbar
12522      * @method show
12523      */
12524     show : function()
12525     {
12526         this.el.child('div').show();
12527     },
12528       
12529     // private
12530     nextBlock : function(){
12531         var td = document.createElement("td");
12532         this.tr.appendChild(td);
12533         return td;
12534     },
12535
12536     // private
12537     destroy : function(){
12538         if(this.items){ // rendered?
12539             Roo.destroy.apply(Roo, this.items.items);
12540         }
12541         if(this.fields){ // rendered?
12542             Roo.destroy.apply(Roo, this.fields.items);
12543         }
12544         Roo.Element.uncache(this.el, this.tr);
12545     }
12546 };
12547
12548 /**
12549  * @class Roo.Toolbar.Item
12550  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12551  * @constructor
12552  * Creates a new Item
12553  * @param {HTMLElement} el 
12554  */
12555 Roo.Toolbar.Item = function(el){
12556     this.el = Roo.getDom(el);
12557     this.id = Roo.id(this.el);
12558     this.hidden = false;
12559 };
12560
12561 Roo.Toolbar.Item.prototype = {
12562     
12563     /**
12564      * Get this item's HTML Element
12565      * @return {HTMLElement}
12566      */
12567     getEl : function(){
12568        return this.el;  
12569     },
12570
12571     // private
12572     render : function(td){
12573         this.td = td;
12574         td.appendChild(this.el);
12575     },
12576     
12577     /**
12578      * Removes and destroys this item.
12579      */
12580     destroy : function(){
12581         this.td.parentNode.removeChild(this.td);
12582     },
12583     
12584     /**
12585      * Shows this item.
12586      */
12587     show: function(){
12588         this.hidden = false;
12589         this.td.style.display = "";
12590     },
12591     
12592     /**
12593      * Hides this item.
12594      */
12595     hide: function(){
12596         this.hidden = true;
12597         this.td.style.display = "none";
12598     },
12599     
12600     /**
12601      * Convenience function for boolean show/hide.
12602      * @param {Boolean} visible true to show/false to hide
12603      */
12604     setVisible: function(visible){
12605         if(visible) {
12606             this.show();
12607         }else{
12608             this.hide();
12609         }
12610     },
12611     
12612     /**
12613      * Try to focus this item.
12614      */
12615     focus : function(){
12616         Roo.fly(this.el).focus();
12617     },
12618     
12619     /**
12620      * Disables this item.
12621      */
12622     disable : function(){
12623         Roo.fly(this.td).addClass("x-item-disabled");
12624         this.disabled = true;
12625         this.el.disabled = true;
12626     },
12627     
12628     /**
12629      * Enables this item.
12630      */
12631     enable : function(){
12632         Roo.fly(this.td).removeClass("x-item-disabled");
12633         this.disabled = false;
12634         this.el.disabled = false;
12635     }
12636 };
12637
12638
12639 /**
12640  * @class Roo.Toolbar.Separator
12641  * @extends Roo.Toolbar.Item
12642  * A simple toolbar separator class
12643  * @constructor
12644  * Creates a new Separator
12645  */
12646 Roo.Toolbar.Separator = function(){
12647     var s = document.createElement("span");
12648     s.className = "ytb-sep";
12649     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12650 };
12651 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12652     enable:Roo.emptyFn,
12653     disable:Roo.emptyFn,
12654     focus:Roo.emptyFn
12655 });
12656
12657 /**
12658  * @class Roo.Toolbar.Spacer
12659  * @extends Roo.Toolbar.Item
12660  * A simple element that adds extra horizontal space to a toolbar.
12661  * @constructor
12662  * Creates a new Spacer
12663  */
12664 Roo.Toolbar.Spacer = function(){
12665     var s = document.createElement("div");
12666     s.className = "ytb-spacer";
12667     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12668 };
12669 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12670     enable:Roo.emptyFn,
12671     disable:Roo.emptyFn,
12672     focus:Roo.emptyFn
12673 });
12674
12675 /**
12676  * @class Roo.Toolbar.Fill
12677  * @extends Roo.Toolbar.Spacer
12678  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12679  * @constructor
12680  * Creates a new Spacer
12681  */
12682 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12683     // private
12684     render : function(td){
12685         td.style.width = '100%';
12686         Roo.Toolbar.Fill.superclass.render.call(this, td);
12687     }
12688 });
12689
12690 /**
12691  * @class Roo.Toolbar.TextItem
12692  * @extends Roo.Toolbar.Item
12693  * A simple class that renders text directly into a toolbar.
12694  * @constructor
12695  * Creates a new TextItem
12696  * @param {String} text
12697  */
12698 Roo.Toolbar.TextItem = function(text){
12699     if (typeof(text) == 'object') {
12700         text = text.text;
12701     }
12702     var s = document.createElement("span");
12703     s.className = "ytb-text";
12704     s.innerHTML = text;
12705     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12706 };
12707 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12708     enable:Roo.emptyFn,
12709     disable:Roo.emptyFn,
12710     focus:Roo.emptyFn
12711 });
12712
12713 /**
12714  * @class Roo.Toolbar.Button
12715  * @extends Roo.Button
12716  * A button that renders into a toolbar.
12717  * @constructor
12718  * Creates a new Button
12719  * @param {Object} config A standard {@link Roo.Button} config object
12720  */
12721 Roo.Toolbar.Button = function(config){
12722     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12723 };
12724 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12725     render : function(td){
12726         this.td = td;
12727         Roo.Toolbar.Button.superclass.render.call(this, td);
12728     },
12729     
12730     /**
12731      * Removes and destroys this button
12732      */
12733     destroy : function(){
12734         Roo.Toolbar.Button.superclass.destroy.call(this);
12735         this.td.parentNode.removeChild(this.td);
12736     },
12737     
12738     /**
12739      * Shows this button
12740      */
12741     show: function(){
12742         this.hidden = false;
12743         this.td.style.display = "";
12744     },
12745     
12746     /**
12747      * Hides this button
12748      */
12749     hide: function(){
12750         this.hidden = true;
12751         this.td.style.display = "none";
12752     },
12753
12754     /**
12755      * Disables this item
12756      */
12757     disable : function(){
12758         Roo.fly(this.td).addClass("x-item-disabled");
12759         this.disabled = true;
12760     },
12761
12762     /**
12763      * Enables this item
12764      */
12765     enable : function(){
12766         Roo.fly(this.td).removeClass("x-item-disabled");
12767         this.disabled = false;
12768     }
12769 });
12770 // backwards compat
12771 Roo.ToolbarButton = Roo.Toolbar.Button;
12772
12773 /**
12774  * @class Roo.Toolbar.SplitButton
12775  * @extends Roo.SplitButton
12776  * A menu button that renders into a toolbar.
12777  * @constructor
12778  * Creates a new SplitButton
12779  * @param {Object} config A standard {@link Roo.SplitButton} config object
12780  */
12781 Roo.Toolbar.SplitButton = function(config){
12782     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12783 };
12784 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12785     render : function(td){
12786         this.td = td;
12787         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12788     },
12789     
12790     /**
12791      * Removes and destroys this button
12792      */
12793     destroy : function(){
12794         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12795         this.td.parentNode.removeChild(this.td);
12796     },
12797     
12798     /**
12799      * Shows this button
12800      */
12801     show: function(){
12802         this.hidden = false;
12803         this.td.style.display = "";
12804     },
12805     
12806     /**
12807      * Hides this button
12808      */
12809     hide: function(){
12810         this.hidden = true;
12811         this.td.style.display = "none";
12812     }
12813 });
12814
12815 // backwards compat
12816 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12817  * Based on:
12818  * Ext JS Library 1.1.1
12819  * Copyright(c) 2006-2007, Ext JS, LLC.
12820  *
12821  * Originally Released Under LGPL - original licence link has changed is not relivant.
12822  *
12823  * Fork - LGPL
12824  * <script type="text/javascript">
12825  */
12826  
12827 /**
12828  * @class Roo.PagingToolbar
12829  * @extends Roo.Toolbar
12830  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12831  * @constructor
12832  * Create a new PagingToolbar
12833  * @param {Object} config The config object
12834  */
12835 Roo.PagingToolbar = function(el, ds, config)
12836 {
12837     // old args format still supported... - xtype is prefered..
12838     if (typeof(el) == 'object' && el.xtype) {
12839         // created from xtype...
12840         config = el;
12841         ds = el.dataSource;
12842         el = config.container;
12843     }
12844     var items = [];
12845     if (config.items) {
12846         items = config.items;
12847         config.items = [];
12848     }
12849     
12850     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12851     this.ds = ds;
12852     this.cursor = 0;
12853     this.renderButtons(this.el);
12854     this.bind(ds);
12855     
12856     // supprot items array.
12857    
12858     Roo.each(items, function(e) {
12859         this.add(Roo.factory(e));
12860     },this);
12861     
12862 };
12863
12864 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12865     /**
12866      * @cfg {Roo.data.Store} dataSource
12867      * The underlying data store providing the paged data
12868      */
12869     /**
12870      * @cfg {String/HTMLElement/Element} container
12871      * container The id or element that will contain the toolbar
12872      */
12873     /**
12874      * @cfg {Boolean} displayInfo
12875      * True to display the displayMsg (defaults to false)
12876      */
12877     /**
12878      * @cfg {Number} pageSize
12879      * The number of records to display per page (defaults to 20)
12880      */
12881     pageSize: 20,
12882     /**
12883      * @cfg {String} displayMsg
12884      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12885      */
12886     displayMsg : 'Displaying {0} - {1} of {2}',
12887     /**
12888      * @cfg {String} emptyMsg
12889      * The message to display when no records are found (defaults to "No data to display")
12890      */
12891     emptyMsg : 'No data to display',
12892     /**
12893      * Customizable piece of the default paging text (defaults to "Page")
12894      * @type String
12895      */
12896     beforePageText : "Page",
12897     /**
12898      * Customizable piece of the default paging text (defaults to "of %0")
12899      * @type String
12900      */
12901     afterPageText : "of {0}",
12902     /**
12903      * Customizable piece of the default paging text (defaults to "First Page")
12904      * @type String
12905      */
12906     firstText : "First Page",
12907     /**
12908      * Customizable piece of the default paging text (defaults to "Previous Page")
12909      * @type String
12910      */
12911     prevText : "Previous Page",
12912     /**
12913      * Customizable piece of the default paging text (defaults to "Next Page")
12914      * @type String
12915      */
12916     nextText : "Next Page",
12917     /**
12918      * Customizable piece of the default paging text (defaults to "Last Page")
12919      * @type String
12920      */
12921     lastText : "Last Page",
12922     /**
12923      * Customizable piece of the default paging text (defaults to "Refresh")
12924      * @type String
12925      */
12926     refreshText : "Refresh",
12927
12928     // private
12929     renderButtons : function(el){
12930         Roo.PagingToolbar.superclass.render.call(this, el);
12931         this.first = this.addButton({
12932             tooltip: this.firstText,
12933             cls: "x-btn-icon x-grid-page-first",
12934             disabled: true,
12935             handler: this.onClick.createDelegate(this, ["first"])
12936         });
12937         this.prev = this.addButton({
12938             tooltip: this.prevText,
12939             cls: "x-btn-icon x-grid-page-prev",
12940             disabled: true,
12941             handler: this.onClick.createDelegate(this, ["prev"])
12942         });
12943         //this.addSeparator();
12944         this.add(this.beforePageText);
12945         this.field = Roo.get(this.addDom({
12946            tag: "input",
12947            type: "text",
12948            size: "3",
12949            value: "1",
12950            cls: "x-grid-page-number"
12951         }).el);
12952         this.field.on("keydown", this.onPagingKeydown, this);
12953         this.field.on("focus", function(){this.dom.select();});
12954         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12955         this.field.setHeight(18);
12956         //this.addSeparator();
12957         this.next = this.addButton({
12958             tooltip: this.nextText,
12959             cls: "x-btn-icon x-grid-page-next",
12960             disabled: true,
12961             handler: this.onClick.createDelegate(this, ["next"])
12962         });
12963         this.last = this.addButton({
12964             tooltip: this.lastText,
12965             cls: "x-btn-icon x-grid-page-last",
12966             disabled: true,
12967             handler: this.onClick.createDelegate(this, ["last"])
12968         });
12969         //this.addSeparator();
12970         this.loading = this.addButton({
12971             tooltip: this.refreshText,
12972             cls: "x-btn-icon x-grid-loading",
12973             handler: this.onClick.createDelegate(this, ["refresh"])
12974         });
12975
12976         if(this.displayInfo){
12977             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12978         }
12979     },
12980
12981     // private
12982     updateInfo : function(){
12983         if(this.displayEl){
12984             var count = this.ds.getCount();
12985             var msg = count == 0 ?
12986                 this.emptyMsg :
12987                 String.format(
12988                     this.displayMsg,
12989                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12990                 );
12991             this.displayEl.update(msg);
12992         }
12993     },
12994
12995     // private
12996     onLoad : function(ds, r, o){
12997        this.cursor = o.params ? o.params.start : 0;
12998        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12999
13000        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13001        this.field.dom.value = ap;
13002        this.first.setDisabled(ap == 1);
13003        this.prev.setDisabled(ap == 1);
13004        this.next.setDisabled(ap == ps);
13005        this.last.setDisabled(ap == ps);
13006        this.loading.enable();
13007        this.updateInfo();
13008     },
13009
13010     // private
13011     getPageData : function(){
13012         var total = this.ds.getTotalCount();
13013         return {
13014             total : total,
13015             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13016             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13017         };
13018     },
13019
13020     // private
13021     onLoadError : function(){
13022         this.loading.enable();
13023     },
13024
13025     // private
13026     onPagingKeydown : function(e){
13027         var k = e.getKey();
13028         var d = this.getPageData();
13029         if(k == e.RETURN){
13030             var v = this.field.dom.value, pageNum;
13031             if(!v || isNaN(pageNum = parseInt(v, 10))){
13032                 this.field.dom.value = d.activePage;
13033                 return;
13034             }
13035             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13036             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13037             e.stopEvent();
13038         }
13039         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))
13040         {
13041           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13042           this.field.dom.value = pageNum;
13043           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13044           e.stopEvent();
13045         }
13046         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13047         {
13048           var v = this.field.dom.value, pageNum; 
13049           var increment = (e.shiftKey) ? 10 : 1;
13050           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13051             increment *= -1;
13052           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13053             this.field.dom.value = d.activePage;
13054             return;
13055           }
13056           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13057           {
13058             this.field.dom.value = parseInt(v, 10) + increment;
13059             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13060             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13061           }
13062           e.stopEvent();
13063         }
13064     },
13065
13066     // private
13067     beforeLoad : function(){
13068         if(this.loading){
13069             this.loading.disable();
13070         }
13071     },
13072
13073     // private
13074     onClick : function(which){
13075         var ds = this.ds;
13076         switch(which){
13077             case "first":
13078                 ds.load({params:{start: 0, limit: this.pageSize}});
13079             break;
13080             case "prev":
13081                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13082             break;
13083             case "next":
13084                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13085             break;
13086             case "last":
13087                 var total = ds.getTotalCount();
13088                 var extra = total % this.pageSize;
13089                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13090                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13091             break;
13092             case "refresh":
13093                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13094             break;
13095         }
13096     },
13097
13098     /**
13099      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13100      * @param {Roo.data.Store} store The data store to unbind
13101      */
13102     unbind : function(ds){
13103         ds.un("beforeload", this.beforeLoad, this);
13104         ds.un("load", this.onLoad, this);
13105         ds.un("loadexception", this.onLoadError, this);
13106         ds.un("remove", this.updateInfo, this);
13107         ds.un("add", this.updateInfo, this);
13108         this.ds = undefined;
13109     },
13110
13111     /**
13112      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13113      * @param {Roo.data.Store} store The data store to bind
13114      */
13115     bind : function(ds){
13116         ds.on("beforeload", this.beforeLoad, this);
13117         ds.on("load", this.onLoad, this);
13118         ds.on("loadexception", this.onLoadError, this);
13119         ds.on("remove", this.updateInfo, this);
13120         ds.on("add", this.updateInfo, this);
13121         this.ds = ds;
13122     }
13123 });/*
13124  * Based on:
13125  * Ext JS Library 1.1.1
13126  * Copyright(c) 2006-2007, Ext JS, LLC.
13127  *
13128  * Originally Released Under LGPL - original licence link has changed is not relivant.
13129  *
13130  * Fork - LGPL
13131  * <script type="text/javascript">
13132  */
13133
13134 /**
13135  * @class Roo.Resizable
13136  * @extends Roo.util.Observable
13137  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13138  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13139  * 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
13140  * the element will be wrapped for you automatically.</p>
13141  * <p>Here is the list of valid resize handles:</p>
13142  * <pre>
13143 Value   Description
13144 ------  -------------------
13145  'n'     north
13146  's'     south
13147  'e'     east
13148  'w'     west
13149  'nw'    northwest
13150  'sw'    southwest
13151  'se'    southeast
13152  'ne'    northeast
13153  'hd'    horizontal drag
13154  'all'   all
13155 </pre>
13156  * <p>Here's an example showing the creation of a typical Resizable:</p>
13157  * <pre><code>
13158 var resizer = new Roo.Resizable("element-id", {
13159     handles: 'all',
13160     minWidth: 200,
13161     minHeight: 100,
13162     maxWidth: 500,
13163     maxHeight: 400,
13164     pinned: true
13165 });
13166 resizer.on("resize", myHandler);
13167 </code></pre>
13168  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13169  * resizer.east.setDisplayed(false);</p>
13170  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13171  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13172  * resize operation's new size (defaults to [0, 0])
13173  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13174  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13175  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13176  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13177  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13178  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13179  * @cfg {Number} width The width of the element in pixels (defaults to null)
13180  * @cfg {Number} height The height of the element in pixels (defaults to null)
13181  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13182  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13183  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13184  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13185  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13186  * in favor of the handles config option (defaults to false)
13187  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13188  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13189  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13190  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13191  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13192  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13193  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13194  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13195  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13196  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13197  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13198  * @constructor
13199  * Create a new resizable component
13200  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13201  * @param {Object} config configuration options
13202   */
13203 Roo.Resizable = function(el, config)
13204 {
13205     this.el = Roo.get(el);
13206
13207     if(config && config.wrap){
13208         config.resizeChild = this.el;
13209         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13210         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13211         this.el.setStyle("overflow", "hidden");
13212         this.el.setPositioning(config.resizeChild.getPositioning());
13213         config.resizeChild.clearPositioning();
13214         if(!config.width || !config.height){
13215             var csize = config.resizeChild.getSize();
13216             this.el.setSize(csize.width, csize.height);
13217         }
13218         if(config.pinned && !config.adjustments){
13219             config.adjustments = "auto";
13220         }
13221     }
13222
13223     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13224     this.proxy.unselectable();
13225     this.proxy.enableDisplayMode('block');
13226
13227     Roo.apply(this, config);
13228
13229     if(this.pinned){
13230         this.disableTrackOver = true;
13231         this.el.addClass("x-resizable-pinned");
13232     }
13233     // if the element isn't positioned, make it relative
13234     var position = this.el.getStyle("position");
13235     if(position != "absolute" && position != "fixed"){
13236         this.el.setStyle("position", "relative");
13237     }
13238     if(!this.handles){ // no handles passed, must be legacy style
13239         this.handles = 's,e,se';
13240         if(this.multiDirectional){
13241             this.handles += ',n,w';
13242         }
13243     }
13244     if(this.handles == "all"){
13245         this.handles = "n s e w ne nw se sw";
13246     }
13247     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13248     var ps = Roo.Resizable.positions;
13249     for(var i = 0, len = hs.length; i < len; i++){
13250         if(hs[i] && ps[hs[i]]){
13251             var pos = ps[hs[i]];
13252             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13253         }
13254     }
13255     // legacy
13256     this.corner = this.southeast;
13257     
13258     // updateBox = the box can move..
13259     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13260         this.updateBox = true;
13261     }
13262
13263     this.activeHandle = null;
13264
13265     if(this.resizeChild){
13266         if(typeof this.resizeChild == "boolean"){
13267             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13268         }else{
13269             this.resizeChild = Roo.get(this.resizeChild, true);
13270         }
13271     }
13272     
13273     if(this.adjustments == "auto"){
13274         var rc = this.resizeChild;
13275         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13276         if(rc && (hw || hn)){
13277             rc.position("relative");
13278             rc.setLeft(hw ? hw.el.getWidth() : 0);
13279             rc.setTop(hn ? hn.el.getHeight() : 0);
13280         }
13281         this.adjustments = [
13282             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13283             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13284         ];
13285     }
13286
13287     if(this.draggable){
13288         this.dd = this.dynamic ?
13289             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13290         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13291     }
13292
13293     // public events
13294     this.addEvents({
13295         /**
13296          * @event beforeresize
13297          * Fired before resize is allowed. Set enabled to false to cancel resize.
13298          * @param {Roo.Resizable} this
13299          * @param {Roo.EventObject} e The mousedown event
13300          */
13301         "beforeresize" : true,
13302         /**
13303          * @event resize
13304          * Fired after a resize.
13305          * @param {Roo.Resizable} this
13306          * @param {Number} width The new width
13307          * @param {Number} height The new height
13308          * @param {Roo.EventObject} e The mouseup event
13309          */
13310         "resize" : true
13311     });
13312
13313     if(this.width !== null && this.height !== null){
13314         this.resizeTo(this.width, this.height);
13315     }else{
13316         this.updateChildSize();
13317     }
13318     if(Roo.isIE){
13319         this.el.dom.style.zoom = 1;
13320     }
13321     Roo.Resizable.superclass.constructor.call(this);
13322 };
13323
13324 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13325         resizeChild : false,
13326         adjustments : [0, 0],
13327         minWidth : 5,
13328         minHeight : 5,
13329         maxWidth : 10000,
13330         maxHeight : 10000,
13331         enabled : true,
13332         animate : false,
13333         duration : .35,
13334         dynamic : false,
13335         handles : false,
13336         multiDirectional : false,
13337         disableTrackOver : false,
13338         easing : 'easeOutStrong',
13339         widthIncrement : 0,
13340         heightIncrement : 0,
13341         pinned : false,
13342         width : null,
13343         height : null,
13344         preserveRatio : false,
13345         transparent: false,
13346         minX: 0,
13347         minY: 0,
13348         draggable: false,
13349
13350         /**
13351          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13352          */
13353         constrainTo: undefined,
13354         /**
13355          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13356          */
13357         resizeRegion: undefined,
13358
13359
13360     /**
13361      * Perform a manual resize
13362      * @param {Number} width
13363      * @param {Number} height
13364      */
13365     resizeTo : function(width, height){
13366         this.el.setSize(width, height);
13367         this.updateChildSize();
13368         this.fireEvent("resize", this, width, height, null);
13369     },
13370
13371     // private
13372     startSizing : function(e, handle){
13373         this.fireEvent("beforeresize", this, e);
13374         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13375
13376             if(!this.overlay){
13377                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13378                 this.overlay.unselectable();
13379                 this.overlay.enableDisplayMode("block");
13380                 this.overlay.on("mousemove", this.onMouseMove, this);
13381                 this.overlay.on("mouseup", this.onMouseUp, this);
13382             }
13383             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13384
13385             this.resizing = true;
13386             this.startBox = this.el.getBox();
13387             this.startPoint = e.getXY();
13388             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13389                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13390
13391             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13392             this.overlay.show();
13393
13394             if(this.constrainTo) {
13395                 var ct = Roo.get(this.constrainTo);
13396                 this.resizeRegion = ct.getRegion().adjust(
13397                     ct.getFrameWidth('t'),
13398                     ct.getFrameWidth('l'),
13399                     -ct.getFrameWidth('b'),
13400                     -ct.getFrameWidth('r')
13401                 );
13402             }
13403
13404             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13405             this.proxy.show();
13406             this.proxy.setBox(this.startBox);
13407             if(!this.dynamic){
13408                 this.proxy.setStyle('visibility', 'visible');
13409             }
13410         }
13411     },
13412
13413     // private
13414     onMouseDown : function(handle, e){
13415         if(this.enabled){
13416             e.stopEvent();
13417             this.activeHandle = handle;
13418             this.startSizing(e, handle);
13419         }
13420     },
13421
13422     // private
13423     onMouseUp : function(e){
13424         var size = this.resizeElement();
13425         this.resizing = false;
13426         this.handleOut();
13427         this.overlay.hide();
13428         this.proxy.hide();
13429         this.fireEvent("resize", this, size.width, size.height, e);
13430     },
13431
13432     // private
13433     updateChildSize : function(){
13434         if(this.resizeChild){
13435             var el = this.el;
13436             var child = this.resizeChild;
13437             var adj = this.adjustments;
13438             if(el.dom.offsetWidth){
13439                 var b = el.getSize(true);
13440                 child.setSize(b.width+adj[0], b.height+adj[1]);
13441             }
13442             // Second call here for IE
13443             // The first call enables instant resizing and
13444             // the second call corrects scroll bars if they
13445             // exist
13446             if(Roo.isIE){
13447                 setTimeout(function(){
13448                     if(el.dom.offsetWidth){
13449                         var b = el.getSize(true);
13450                         child.setSize(b.width+adj[0], b.height+adj[1]);
13451                     }
13452                 }, 10);
13453             }
13454         }
13455     },
13456
13457     // private
13458     snap : function(value, inc, min){
13459         if(!inc || !value) return value;
13460         var newValue = value;
13461         var m = value % inc;
13462         if(m > 0){
13463             if(m > (inc/2)){
13464                 newValue = value + (inc-m);
13465             }else{
13466                 newValue = value - m;
13467             }
13468         }
13469         return Math.max(min, newValue);
13470     },
13471
13472     // private
13473     resizeElement : function(){
13474         var box = this.proxy.getBox();
13475         if(this.updateBox){
13476             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13477         }else{
13478             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13479         }
13480         this.updateChildSize();
13481         if(!this.dynamic){
13482             this.proxy.hide();
13483         }
13484         return box;
13485     },
13486
13487     // private
13488     constrain : function(v, diff, m, mx){
13489         if(v - diff < m){
13490             diff = v - m;
13491         }else if(v - diff > mx){
13492             diff = mx - v;
13493         }
13494         return diff;
13495     },
13496
13497     // private
13498     onMouseMove : function(e){
13499         if(this.enabled){
13500             try{// try catch so if something goes wrong the user doesn't get hung
13501
13502             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13503                 return;
13504             }
13505
13506             //var curXY = this.startPoint;
13507             var curSize = this.curSize || this.startBox;
13508             var x = this.startBox.x, y = this.startBox.y;
13509             var ox = x, oy = y;
13510             var w = curSize.width, h = curSize.height;
13511             var ow = w, oh = h;
13512             var mw = this.minWidth, mh = this.minHeight;
13513             var mxw = this.maxWidth, mxh = this.maxHeight;
13514             var wi = this.widthIncrement;
13515             var hi = this.heightIncrement;
13516
13517             var eventXY = e.getXY();
13518             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13519             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13520
13521             var pos = this.activeHandle.position;
13522
13523             switch(pos){
13524                 case "east":
13525                     w += diffX;
13526                     w = Math.min(Math.max(mw, w), mxw);
13527                     break;
13528              
13529                 case "south":
13530                     h += diffY;
13531                     h = Math.min(Math.max(mh, h), mxh);
13532                     break;
13533                 case "southeast":
13534                     w += diffX;
13535                     h += diffY;
13536                     w = Math.min(Math.max(mw, w), mxw);
13537                     h = Math.min(Math.max(mh, h), mxh);
13538                     break;
13539                 case "north":
13540                     diffY = this.constrain(h, diffY, mh, mxh);
13541                     y += diffY;
13542                     h -= diffY;
13543                     break;
13544                 case "hdrag":
13545                     
13546                     if (wi) {
13547                         var adiffX = Math.abs(diffX);
13548                         var sub = (adiffX % wi); // how much 
13549                         if (sub > (wi/2)) { // far enough to snap
13550                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13551                         } else {
13552                             // remove difference.. 
13553                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13554                         }
13555                     }
13556                     x += diffX;
13557                     x = Math.max(this.minX, x);
13558                     break;
13559                 case "west":
13560                     diffX = this.constrain(w, diffX, mw, mxw);
13561                     x += diffX;
13562                     w -= diffX;
13563                     break;
13564                 case "northeast":
13565                     w += diffX;
13566                     w = Math.min(Math.max(mw, w), mxw);
13567                     diffY = this.constrain(h, diffY, mh, mxh);
13568                     y += diffY;
13569                     h -= diffY;
13570                     break;
13571                 case "northwest":
13572                     diffX = this.constrain(w, diffX, mw, mxw);
13573                     diffY = this.constrain(h, diffY, mh, mxh);
13574                     y += diffY;
13575                     h -= diffY;
13576                     x += diffX;
13577                     w -= diffX;
13578                     break;
13579                case "southwest":
13580                     diffX = this.constrain(w, diffX, mw, mxw);
13581                     h += diffY;
13582                     h = Math.min(Math.max(mh, h), mxh);
13583                     x += diffX;
13584                     w -= diffX;
13585                     break;
13586             }
13587
13588             var sw = this.snap(w, wi, mw);
13589             var sh = this.snap(h, hi, mh);
13590             if(sw != w || sh != h){
13591                 switch(pos){
13592                     case "northeast":
13593                         y -= sh - h;
13594                     break;
13595                     case "north":
13596                         y -= sh - h;
13597                         break;
13598                     case "southwest":
13599                         x -= sw - w;
13600                     break;
13601                     case "west":
13602                         x -= sw - w;
13603                         break;
13604                     case "northwest":
13605                         x -= sw - w;
13606                         y -= sh - h;
13607                     break;
13608                 }
13609                 w = sw;
13610                 h = sh;
13611             }
13612
13613             if(this.preserveRatio){
13614                 switch(pos){
13615                     case "southeast":
13616                     case "east":
13617                         h = oh * (w/ow);
13618                         h = Math.min(Math.max(mh, h), mxh);
13619                         w = ow * (h/oh);
13620                        break;
13621                     case "south":
13622                         w = ow * (h/oh);
13623                         w = Math.min(Math.max(mw, w), mxw);
13624                         h = oh * (w/ow);
13625                         break;
13626                     case "northeast":
13627                         w = ow * (h/oh);
13628                         w = Math.min(Math.max(mw, w), mxw);
13629                         h = oh * (w/ow);
13630                     break;
13631                     case "north":
13632                         var tw = w;
13633                         w = ow * (h/oh);
13634                         w = Math.min(Math.max(mw, w), mxw);
13635                         h = oh * (w/ow);
13636                         x += (tw - w) / 2;
13637                         break;
13638                     case "southwest":
13639                         h = oh * (w/ow);
13640                         h = Math.min(Math.max(mh, h), mxh);
13641                         var tw = w;
13642                         w = ow * (h/oh);
13643                         x += tw - w;
13644                         break;
13645                     case "west":
13646                         var th = h;
13647                         h = oh * (w/ow);
13648                         h = Math.min(Math.max(mh, h), mxh);
13649                         y += (th - h) / 2;
13650                         var tw = w;
13651                         w = ow * (h/oh);
13652                         x += tw - w;
13653                        break;
13654                     case "northwest":
13655                         var tw = w;
13656                         var th = h;
13657                         h = oh * (w/ow);
13658                         h = Math.min(Math.max(mh, h), mxh);
13659                         w = ow * (h/oh);
13660                         y += th - h;
13661                         x += tw - w;
13662                        break;
13663
13664                 }
13665             }
13666             if (pos == 'hdrag') {
13667                 w = ow;
13668             }
13669             this.proxy.setBounds(x, y, w, h);
13670             if(this.dynamic){
13671                 this.resizeElement();
13672             }
13673             }catch(e){}
13674         }
13675     },
13676
13677     // private
13678     handleOver : function(){
13679         if(this.enabled){
13680             this.el.addClass("x-resizable-over");
13681         }
13682     },
13683
13684     // private
13685     handleOut : function(){
13686         if(!this.resizing){
13687             this.el.removeClass("x-resizable-over");
13688         }
13689     },
13690
13691     /**
13692      * Returns the element this component is bound to.
13693      * @return {Roo.Element}
13694      */
13695     getEl : function(){
13696         return this.el;
13697     },
13698
13699     /**
13700      * Returns the resizeChild element (or null).
13701      * @return {Roo.Element}
13702      */
13703     getResizeChild : function(){
13704         return this.resizeChild;
13705     },
13706
13707     /**
13708      * Destroys this resizable. If the element was wrapped and
13709      * removeEl is not true then the element remains.
13710      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13711      */
13712     destroy : function(removeEl){
13713         this.proxy.remove();
13714         if(this.overlay){
13715             this.overlay.removeAllListeners();
13716             this.overlay.remove();
13717         }
13718         var ps = Roo.Resizable.positions;
13719         for(var k in ps){
13720             if(typeof ps[k] != "function" && this[ps[k]]){
13721                 var h = this[ps[k]];
13722                 h.el.removeAllListeners();
13723                 h.el.remove();
13724             }
13725         }
13726         if(removeEl){
13727             this.el.update("");
13728             this.el.remove();
13729         }
13730     }
13731 });
13732
13733 // private
13734 // hash to map config positions to true positions
13735 Roo.Resizable.positions = {
13736     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13737     hd: "hdrag"
13738 };
13739
13740 // private
13741 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13742     if(!this.tpl){
13743         // only initialize the template if resizable is used
13744         var tpl = Roo.DomHelper.createTemplate(
13745             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13746         );
13747         tpl.compile();
13748         Roo.Resizable.Handle.prototype.tpl = tpl;
13749     }
13750     this.position = pos;
13751     this.rz = rz;
13752     // show north drag fro topdra
13753     var handlepos = pos == 'hdrag' ? 'north' : pos;
13754     
13755     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13756     if (pos == 'hdrag') {
13757         this.el.setStyle('cursor', 'pointer');
13758     }
13759     this.el.unselectable();
13760     if(transparent){
13761         this.el.setOpacity(0);
13762     }
13763     this.el.on("mousedown", this.onMouseDown, this);
13764     if(!disableTrackOver){
13765         this.el.on("mouseover", this.onMouseOver, this);
13766         this.el.on("mouseout", this.onMouseOut, this);
13767     }
13768 };
13769
13770 // private
13771 Roo.Resizable.Handle.prototype = {
13772     afterResize : function(rz){
13773         // do nothing
13774     },
13775     // private
13776     onMouseDown : function(e){
13777         this.rz.onMouseDown(this, e);
13778     },
13779     // private
13780     onMouseOver : function(e){
13781         this.rz.handleOver(this, e);
13782     },
13783     // private
13784     onMouseOut : function(e){
13785         this.rz.handleOut(this, e);
13786     }
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798 /**
13799  * @class Roo.Editor
13800  * @extends Roo.Component
13801  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13802  * @constructor
13803  * Create a new Editor
13804  * @param {Roo.form.Field} field The Field object (or descendant)
13805  * @param {Object} config The config object
13806  */
13807 Roo.Editor = function(field, config){
13808     Roo.Editor.superclass.constructor.call(this, config);
13809     this.field = field;
13810     this.addEvents({
13811         /**
13812              * @event beforestartedit
13813              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13814              * false from the handler of this event.
13815              * @param {Editor} this
13816              * @param {Roo.Element} boundEl The underlying element bound to this editor
13817              * @param {Mixed} value The field value being set
13818              */
13819         "beforestartedit" : true,
13820         /**
13821              * @event startedit
13822              * Fires when this editor is displayed
13823              * @param {Roo.Element} boundEl The underlying element bound to this editor
13824              * @param {Mixed} value The starting field value
13825              */
13826         "startedit" : true,
13827         /**
13828              * @event beforecomplete
13829              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13830              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13831              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13832              * event will not fire since no edit actually occurred.
13833              * @param {Editor} this
13834              * @param {Mixed} value The current field value
13835              * @param {Mixed} startValue The original field value
13836              */
13837         "beforecomplete" : true,
13838         /**
13839              * @event complete
13840              * Fires after editing is complete and any changed value has been written to the underlying field.
13841              * @param {Editor} this
13842              * @param {Mixed} value The current field value
13843              * @param {Mixed} startValue The original field value
13844              */
13845         "complete" : true,
13846         /**
13847          * @event specialkey
13848          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13849          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13850          * @param {Roo.form.Field} this
13851          * @param {Roo.EventObject} e The event object
13852          */
13853         "specialkey" : true
13854     });
13855 };
13856
13857 Roo.extend(Roo.Editor, Roo.Component, {
13858     /**
13859      * @cfg {Boolean/String} autosize
13860      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13861      * or "height" to adopt the height only (defaults to false)
13862      */
13863     /**
13864      * @cfg {Boolean} revertInvalid
13865      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13866      * validation fails (defaults to true)
13867      */
13868     /**
13869      * @cfg {Boolean} ignoreNoChange
13870      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13871      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13872      * will never be ignored.
13873      */
13874     /**
13875      * @cfg {Boolean} hideEl
13876      * False to keep the bound element visible while the editor is displayed (defaults to true)
13877      */
13878     /**
13879      * @cfg {Mixed} value
13880      * The data value of the underlying field (defaults to "")
13881      */
13882     value : "",
13883     /**
13884      * @cfg {String} alignment
13885      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13886      */
13887     alignment: "c-c?",
13888     /**
13889      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13890      * for bottom-right shadow (defaults to "frame")
13891      */
13892     shadow : "frame",
13893     /**
13894      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13895      */
13896     constrain : false,
13897     /**
13898      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13899      */
13900     completeOnEnter : false,
13901     /**
13902      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13903      */
13904     cancelOnEsc : false,
13905     /**
13906      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13907      */
13908     updateEl : false,
13909
13910     // private
13911     onRender : function(ct, position){
13912         this.el = new Roo.Layer({
13913             shadow: this.shadow,
13914             cls: "x-editor",
13915             parentEl : ct,
13916             shim : this.shim,
13917             shadowOffset:4,
13918             id: this.id,
13919             constrain: this.constrain
13920         });
13921         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13922         if(this.field.msgTarget != 'title'){
13923             this.field.msgTarget = 'qtip';
13924         }
13925         this.field.render(this.el);
13926         if(Roo.isGecko){
13927             this.field.el.dom.setAttribute('autocomplete', 'off');
13928         }
13929         this.field.on("specialkey", this.onSpecialKey, this);
13930         if(this.swallowKeys){
13931             this.field.el.swallowEvent(['keydown','keypress']);
13932         }
13933         this.field.show();
13934         this.field.on("blur", this.onBlur, this);
13935         if(this.field.grow){
13936             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13937         }
13938     },
13939
13940     onSpecialKey : function(field, e)
13941     {
13942         //Roo.log('editor onSpecialKey');
13943         if(this.completeOnEnter && e.getKey() == e.ENTER){
13944             e.stopEvent();
13945             this.completeEdit();
13946             return;
13947         }
13948         // do not fire special key otherwise it might hide close the editor...
13949         if(e.getKey() == e.ENTER){    
13950             return;
13951         }
13952         if(this.cancelOnEsc && e.getKey() == e.ESC){
13953             this.cancelEdit();
13954             return;
13955         } 
13956         this.fireEvent('specialkey', field, e);
13957     
13958     },
13959
13960     /**
13961      * Starts the editing process and shows the editor.
13962      * @param {String/HTMLElement/Element} el The element to edit
13963      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13964       * to the innerHTML of el.
13965      */
13966     startEdit : function(el, value){
13967         if(this.editing){
13968             this.completeEdit();
13969         }
13970         this.boundEl = Roo.get(el);
13971         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13972         if(!this.rendered){
13973             this.render(this.parentEl || document.body);
13974         }
13975         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13976             return;
13977         }
13978         this.startValue = v;
13979         this.field.setValue(v);
13980         if(this.autoSize){
13981             var sz = this.boundEl.getSize();
13982             switch(this.autoSize){
13983                 case "width":
13984                 this.setSize(sz.width,  "");
13985                 break;
13986                 case "height":
13987                 this.setSize("",  sz.height);
13988                 break;
13989                 default:
13990                 this.setSize(sz.width,  sz.height);
13991             }
13992         }
13993         this.el.alignTo(this.boundEl, this.alignment);
13994         this.editing = true;
13995         if(Roo.QuickTips){
13996             Roo.QuickTips.disable();
13997         }
13998         this.show();
13999     },
14000
14001     /**
14002      * Sets the height and width of this editor.
14003      * @param {Number} width The new width
14004      * @param {Number} height The new height
14005      */
14006     setSize : function(w, h){
14007         this.field.setSize(w, h);
14008         if(this.el){
14009             this.el.sync();
14010         }
14011     },
14012
14013     /**
14014      * Realigns the editor to the bound field based on the current alignment config value.
14015      */
14016     realign : function(){
14017         this.el.alignTo(this.boundEl, this.alignment);
14018     },
14019
14020     /**
14021      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14022      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14023      */
14024     completeEdit : function(remainVisible){
14025         if(!this.editing){
14026             return;
14027         }
14028         var v = this.getValue();
14029         if(this.revertInvalid !== false && !this.field.isValid()){
14030             v = this.startValue;
14031             this.cancelEdit(true);
14032         }
14033         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14034             this.editing = false;
14035             this.hide();
14036             return;
14037         }
14038         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14039             this.editing = false;
14040             if(this.updateEl && this.boundEl){
14041                 this.boundEl.update(v);
14042             }
14043             if(remainVisible !== true){
14044                 this.hide();
14045             }
14046             this.fireEvent("complete", this, v, this.startValue);
14047         }
14048     },
14049
14050     // private
14051     onShow : function(){
14052         this.el.show();
14053         if(this.hideEl !== false){
14054             this.boundEl.hide();
14055         }
14056         this.field.show();
14057         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14058             this.fixIEFocus = true;
14059             this.deferredFocus.defer(50, this);
14060         }else{
14061             this.field.focus();
14062         }
14063         this.fireEvent("startedit", this.boundEl, this.startValue);
14064     },
14065
14066     deferredFocus : function(){
14067         if(this.editing){
14068             this.field.focus();
14069         }
14070     },
14071
14072     /**
14073      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14074      * reverted to the original starting value.
14075      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14076      * cancel (defaults to false)
14077      */
14078     cancelEdit : function(remainVisible){
14079         if(this.editing){
14080             this.setValue(this.startValue);
14081             if(remainVisible !== true){
14082                 this.hide();
14083             }
14084         }
14085     },
14086
14087     // private
14088     onBlur : function(){
14089         if(this.allowBlur !== true && this.editing){
14090             this.completeEdit();
14091         }
14092     },
14093
14094     // private
14095     onHide : function(){
14096         if(this.editing){
14097             this.completeEdit();
14098             return;
14099         }
14100         this.field.blur();
14101         if(this.field.collapse){
14102             this.field.collapse();
14103         }
14104         this.el.hide();
14105         if(this.hideEl !== false){
14106             this.boundEl.show();
14107         }
14108         if(Roo.QuickTips){
14109             Roo.QuickTips.enable();
14110         }
14111     },
14112
14113     /**
14114      * Sets the data value of the editor
14115      * @param {Mixed} value Any valid value supported by the underlying field
14116      */
14117     setValue : function(v){
14118         this.field.setValue(v);
14119     },
14120
14121     /**
14122      * Gets the data value of the editor
14123      * @return {Mixed} The data value
14124      */
14125     getValue : function(){
14126         return this.field.getValue();
14127     }
14128 });/*
14129  * Based on:
14130  * Ext JS Library 1.1.1
14131  * Copyright(c) 2006-2007, Ext JS, LLC.
14132  *
14133  * Originally Released Under LGPL - original licence link has changed is not relivant.
14134  *
14135  * Fork - LGPL
14136  * <script type="text/javascript">
14137  */
14138  
14139 /**
14140  * @class Roo.BasicDialog
14141  * @extends Roo.util.Observable
14142  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14143  * <pre><code>
14144 var dlg = new Roo.BasicDialog("my-dlg", {
14145     height: 200,
14146     width: 300,
14147     minHeight: 100,
14148     minWidth: 150,
14149     modal: true,
14150     proxyDrag: true,
14151     shadow: true
14152 });
14153 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14154 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14155 dlg.addButton('Cancel', dlg.hide, dlg);
14156 dlg.show();
14157 </code></pre>
14158   <b>A Dialog should always be a direct child of the body element.</b>
14159  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14160  * @cfg {String} title Default text to display in the title bar (defaults to null)
14161  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14162  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14163  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14164  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14165  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14166  * (defaults to null with no animation)
14167  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14168  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14169  * property for valid values (defaults to 'all')
14170  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14171  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14172  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14173  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14174  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14175  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14176  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14177  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14178  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14179  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14180  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14181  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14182  * draggable = true (defaults to false)
14183  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14184  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14185  * shadow (defaults to false)
14186  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14187  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14188  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14189  * @cfg {Array} buttons Array of buttons
14190  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14191  * @constructor
14192  * Create a new BasicDialog.
14193  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14194  * @param {Object} config Configuration options
14195  */
14196 Roo.BasicDialog = function(el, config){
14197     this.el = Roo.get(el);
14198     var dh = Roo.DomHelper;
14199     if(!this.el && config && config.autoCreate){
14200         if(typeof config.autoCreate == "object"){
14201             if(!config.autoCreate.id){
14202                 config.autoCreate.id = el;
14203             }
14204             this.el = dh.append(document.body,
14205                         config.autoCreate, true);
14206         }else{
14207             this.el = dh.append(document.body,
14208                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14209         }
14210     }
14211     el = this.el;
14212     el.setDisplayed(true);
14213     el.hide = this.hideAction;
14214     this.id = el.id;
14215     el.addClass("x-dlg");
14216
14217     Roo.apply(this, config);
14218
14219     this.proxy = el.createProxy("x-dlg-proxy");
14220     this.proxy.hide = this.hideAction;
14221     this.proxy.setOpacity(.5);
14222     this.proxy.hide();
14223
14224     if(config.width){
14225         el.setWidth(config.width);
14226     }
14227     if(config.height){
14228         el.setHeight(config.height);
14229     }
14230     this.size = el.getSize();
14231     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14232         this.xy = [config.x,config.y];
14233     }else{
14234         this.xy = el.getCenterXY(true);
14235     }
14236     /** The header element @type Roo.Element */
14237     this.header = el.child("> .x-dlg-hd");
14238     /** The body element @type Roo.Element */
14239     this.body = el.child("> .x-dlg-bd");
14240     /** The footer element @type Roo.Element */
14241     this.footer = el.child("> .x-dlg-ft");
14242
14243     if(!this.header){
14244         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14245     }
14246     if(!this.body){
14247         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14248     }
14249
14250     this.header.unselectable();
14251     if(this.title){
14252         this.header.update(this.title);
14253     }
14254     // this element allows the dialog to be focused for keyboard event
14255     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14256     this.focusEl.swallowEvent("click", true);
14257
14258     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14259
14260     // wrap the body and footer for special rendering
14261     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14262     if(this.footer){
14263         this.bwrap.dom.appendChild(this.footer.dom);
14264     }
14265
14266     this.bg = this.el.createChild({
14267         tag: "div", cls:"x-dlg-bg",
14268         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14269     });
14270     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14271
14272
14273     if(this.autoScroll !== false && !this.autoTabs){
14274         this.body.setStyle("overflow", "auto");
14275     }
14276
14277     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14278
14279     if(this.closable !== false){
14280         this.el.addClass("x-dlg-closable");
14281         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14282         this.close.on("click", this.closeClick, this);
14283         this.close.addClassOnOver("x-dlg-close-over");
14284     }
14285     if(this.collapsible !== false){
14286         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14287         this.collapseBtn.on("click", this.collapseClick, this);
14288         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14289         this.header.on("dblclick", this.collapseClick, this);
14290     }
14291     if(this.resizable !== false){
14292         this.el.addClass("x-dlg-resizable");
14293         this.resizer = new Roo.Resizable(el, {
14294             minWidth: this.minWidth || 80,
14295             minHeight:this.minHeight || 80,
14296             handles: this.resizeHandles || "all",
14297             pinned: true
14298         });
14299         this.resizer.on("beforeresize", this.beforeResize, this);
14300         this.resizer.on("resize", this.onResize, this);
14301     }
14302     if(this.draggable !== false){
14303         el.addClass("x-dlg-draggable");
14304         if (!this.proxyDrag) {
14305             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14306         }
14307         else {
14308             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14309         }
14310         dd.setHandleElId(this.header.id);
14311         dd.endDrag = this.endMove.createDelegate(this);
14312         dd.startDrag = this.startMove.createDelegate(this);
14313         dd.onDrag = this.onDrag.createDelegate(this);
14314         dd.scroll = false;
14315         this.dd = dd;
14316     }
14317     if(this.modal){
14318         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14319         this.mask.enableDisplayMode("block");
14320         this.mask.hide();
14321         this.el.addClass("x-dlg-modal");
14322     }
14323     if(this.shadow){
14324         this.shadow = new Roo.Shadow({
14325             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14326             offset : this.shadowOffset
14327         });
14328     }else{
14329         this.shadowOffset = 0;
14330     }
14331     if(Roo.useShims && this.shim !== false){
14332         this.shim = this.el.createShim();
14333         this.shim.hide = this.hideAction;
14334         this.shim.hide();
14335     }else{
14336         this.shim = false;
14337     }
14338     if(this.autoTabs){
14339         this.initTabs();
14340     }
14341     if (this.buttons) { 
14342         var bts= this.buttons;
14343         this.buttons = [];
14344         Roo.each(bts, function(b) {
14345             this.addButton(b);
14346         }, this);
14347     }
14348     
14349     
14350     this.addEvents({
14351         /**
14352          * @event keydown
14353          * Fires when a key is pressed
14354          * @param {Roo.BasicDialog} this
14355          * @param {Roo.EventObject} e
14356          */
14357         "keydown" : true,
14358         /**
14359          * @event move
14360          * Fires when this dialog is moved by the user.
14361          * @param {Roo.BasicDialog} this
14362          * @param {Number} x The new page X
14363          * @param {Number} y The new page Y
14364          */
14365         "move" : true,
14366         /**
14367          * @event resize
14368          * Fires when this dialog is resized by the user.
14369          * @param {Roo.BasicDialog} this
14370          * @param {Number} width The new width
14371          * @param {Number} height The new height
14372          */
14373         "resize" : true,
14374         /**
14375          * @event beforehide
14376          * Fires before this dialog is hidden.
14377          * @param {Roo.BasicDialog} this
14378          */
14379         "beforehide" : true,
14380         /**
14381          * @event hide
14382          * Fires when this dialog is hidden.
14383          * @param {Roo.BasicDialog} this
14384          */
14385         "hide" : true,
14386         /**
14387          * @event beforeshow
14388          * Fires before this dialog is shown.
14389          * @param {Roo.BasicDialog} this
14390          */
14391         "beforeshow" : true,
14392         /**
14393          * @event show
14394          * Fires when this dialog is shown.
14395          * @param {Roo.BasicDialog} this
14396          */
14397         "show" : true
14398     });
14399     el.on("keydown", this.onKeyDown, this);
14400     el.on("mousedown", this.toFront, this);
14401     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14402     this.el.hide();
14403     Roo.DialogManager.register(this);
14404     Roo.BasicDialog.superclass.constructor.call(this);
14405 };
14406
14407 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14408     shadowOffset: Roo.isIE ? 6 : 5,
14409     minHeight: 80,
14410     minWidth: 200,
14411     minButtonWidth: 75,
14412     defaultButton: null,
14413     buttonAlign: "right",
14414     tabTag: 'div',
14415     firstShow: true,
14416
14417     /**
14418      * Sets the dialog title text
14419      * @param {String} text The title text to display
14420      * @return {Roo.BasicDialog} this
14421      */
14422     setTitle : function(text){
14423         this.header.update(text);
14424         return this;
14425     },
14426
14427     // private
14428     closeClick : function(){
14429         this.hide();
14430     },
14431
14432     // private
14433     collapseClick : function(){
14434         this[this.collapsed ? "expand" : "collapse"]();
14435     },
14436
14437     /**
14438      * Collapses the dialog to its minimized state (only the title bar is visible).
14439      * Equivalent to the user clicking the collapse dialog button.
14440      */
14441     collapse : function(){
14442         if(!this.collapsed){
14443             this.collapsed = true;
14444             this.el.addClass("x-dlg-collapsed");
14445             this.restoreHeight = this.el.getHeight();
14446             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14447         }
14448     },
14449
14450     /**
14451      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14452      * clicking the expand dialog button.
14453      */
14454     expand : function(){
14455         if(this.collapsed){
14456             this.collapsed = false;
14457             this.el.removeClass("x-dlg-collapsed");
14458             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14459         }
14460     },
14461
14462     /**
14463      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14464      * @return {Roo.TabPanel} The tabs component
14465      */
14466     initTabs : function(){
14467         var tabs = this.getTabs();
14468         while(tabs.getTab(0)){
14469             tabs.removeTab(0);
14470         }
14471         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14472             var dom = el.dom;
14473             tabs.addTab(Roo.id(dom), dom.title);
14474             dom.title = "";
14475         });
14476         tabs.activate(0);
14477         return tabs;
14478     },
14479
14480     // private
14481     beforeResize : function(){
14482         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14483     },
14484
14485     // private
14486     onResize : function(){
14487         this.refreshSize();
14488         this.syncBodyHeight();
14489         this.adjustAssets();
14490         this.focus();
14491         this.fireEvent("resize", this, this.size.width, this.size.height);
14492     },
14493
14494     // private
14495     onKeyDown : function(e){
14496         if(this.isVisible()){
14497             this.fireEvent("keydown", this, e);
14498         }
14499     },
14500
14501     /**
14502      * Resizes the dialog.
14503      * @param {Number} width
14504      * @param {Number} height
14505      * @return {Roo.BasicDialog} this
14506      */
14507     resizeTo : function(width, height){
14508         this.el.setSize(width, height);
14509         this.size = {width: width, height: height};
14510         this.syncBodyHeight();
14511         if(this.fixedcenter){
14512             this.center();
14513         }
14514         if(this.isVisible()){
14515             this.constrainXY();
14516             this.adjustAssets();
14517         }
14518         this.fireEvent("resize", this, width, height);
14519         return this;
14520     },
14521
14522
14523     /**
14524      * Resizes the dialog to fit the specified content size.
14525      * @param {Number} width
14526      * @param {Number} height
14527      * @return {Roo.BasicDialog} this
14528      */
14529     setContentSize : function(w, h){
14530         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14531         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14532         //if(!this.el.isBorderBox()){
14533             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14534             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14535         //}
14536         if(this.tabs){
14537             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14538             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14539         }
14540         this.resizeTo(w, h);
14541         return this;
14542     },
14543
14544     /**
14545      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14546      * executed in response to a particular key being pressed while the dialog is active.
14547      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14548      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14549      * @param {Function} fn The function to call
14550      * @param {Object} scope (optional) The scope of the function
14551      * @return {Roo.BasicDialog} this
14552      */
14553     addKeyListener : function(key, fn, scope){
14554         var keyCode, shift, ctrl, alt;
14555         if(typeof key == "object" && !(key instanceof Array)){
14556             keyCode = key["key"];
14557             shift = key["shift"];
14558             ctrl = key["ctrl"];
14559             alt = key["alt"];
14560         }else{
14561             keyCode = key;
14562         }
14563         var handler = function(dlg, e){
14564             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14565                 var k = e.getKey();
14566                 if(keyCode instanceof Array){
14567                     for(var i = 0, len = keyCode.length; i < len; i++){
14568                         if(keyCode[i] == k){
14569                           fn.call(scope || window, dlg, k, e);
14570                           return;
14571                         }
14572                     }
14573                 }else{
14574                     if(k == keyCode){
14575                         fn.call(scope || window, dlg, k, e);
14576                     }
14577                 }
14578             }
14579         };
14580         this.on("keydown", handler);
14581         return this;
14582     },
14583
14584     /**
14585      * Returns the TabPanel component (creates it if it doesn't exist).
14586      * Note: If you wish to simply check for the existence of tabs without creating them,
14587      * check for a null 'tabs' property.
14588      * @return {Roo.TabPanel} The tabs component
14589      */
14590     getTabs : function(){
14591         if(!this.tabs){
14592             this.el.addClass("x-dlg-auto-tabs");
14593             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14594             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14595         }
14596         return this.tabs;
14597     },
14598
14599     /**
14600      * Adds a button to the footer section of the dialog.
14601      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14602      * object or a valid Roo.DomHelper element config
14603      * @param {Function} handler The function called when the button is clicked
14604      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14605      * @return {Roo.Button} The new button
14606      */
14607     addButton : function(config, handler, scope){
14608         var dh = Roo.DomHelper;
14609         if(!this.footer){
14610             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14611         }
14612         if(!this.btnContainer){
14613             var tb = this.footer.createChild({
14614
14615                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14616                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14617             }, null, true);
14618             this.btnContainer = tb.firstChild.firstChild.firstChild;
14619         }
14620         var bconfig = {
14621             handler: handler,
14622             scope: scope,
14623             minWidth: this.minButtonWidth,
14624             hideParent:true
14625         };
14626         if(typeof config == "string"){
14627             bconfig.text = config;
14628         }else{
14629             if(config.tag){
14630                 bconfig.dhconfig = config;
14631             }else{
14632                 Roo.apply(bconfig, config);
14633             }
14634         }
14635         var fc = false;
14636         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14637             bconfig.position = Math.max(0, bconfig.position);
14638             fc = this.btnContainer.childNodes[bconfig.position];
14639         }
14640          
14641         var btn = new Roo.Button(
14642             fc ? 
14643                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14644                 : this.btnContainer.appendChild(document.createElement("td")),
14645             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14646             bconfig
14647         );
14648         this.syncBodyHeight();
14649         if(!this.buttons){
14650             /**
14651              * Array of all the buttons that have been added to this dialog via addButton
14652              * @type Array
14653              */
14654             this.buttons = [];
14655         }
14656         this.buttons.push(btn);
14657         return btn;
14658     },
14659
14660     /**
14661      * Sets the default button to be focused when the dialog is displayed.
14662      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14663      * @return {Roo.BasicDialog} this
14664      */
14665     setDefaultButton : function(btn){
14666         this.defaultButton = btn;
14667         return this;
14668     },
14669
14670     // private
14671     getHeaderFooterHeight : function(safe){
14672         var height = 0;
14673         if(this.header){
14674            height += this.header.getHeight();
14675         }
14676         if(this.footer){
14677            var fm = this.footer.getMargins();
14678             height += (this.footer.getHeight()+fm.top+fm.bottom);
14679         }
14680         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14681         height += this.centerBg.getPadding("tb");
14682         return height;
14683     },
14684
14685     // private
14686     syncBodyHeight : function(){
14687         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14688         var height = this.size.height - this.getHeaderFooterHeight(false);
14689         bd.setHeight(height-bd.getMargins("tb"));
14690         var hh = this.header.getHeight();
14691         var h = this.size.height-hh;
14692         cb.setHeight(h);
14693         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14694         bw.setHeight(h-cb.getPadding("tb"));
14695         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14696         bd.setWidth(bw.getWidth(true));
14697         if(this.tabs){
14698             this.tabs.syncHeight();
14699             if(Roo.isIE){
14700                 this.tabs.el.repaint();
14701             }
14702         }
14703     },
14704
14705     /**
14706      * Restores the previous state of the dialog if Roo.state is configured.
14707      * @return {Roo.BasicDialog} this
14708      */
14709     restoreState : function(){
14710         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14711         if(box && box.width){
14712             this.xy = [box.x, box.y];
14713             this.resizeTo(box.width, box.height);
14714         }
14715         return this;
14716     },
14717
14718     // private
14719     beforeShow : function(){
14720         this.expand();
14721         if(this.fixedcenter){
14722             this.xy = this.el.getCenterXY(true);
14723         }
14724         if(this.modal){
14725             Roo.get(document.body).addClass("x-body-masked");
14726             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14727             this.mask.show();
14728         }
14729         this.constrainXY();
14730     },
14731
14732     // private
14733     animShow : function(){
14734         var b = Roo.get(this.animateTarget).getBox();
14735         this.proxy.setSize(b.width, b.height);
14736         this.proxy.setLocation(b.x, b.y);
14737         this.proxy.show();
14738         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14739                     true, .35, this.showEl.createDelegate(this));
14740     },
14741
14742     /**
14743      * Shows the dialog.
14744      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14745      * @return {Roo.BasicDialog} this
14746      */
14747     show : function(animateTarget){
14748         if (this.fireEvent("beforeshow", this) === false){
14749             return;
14750         }
14751         if(this.syncHeightBeforeShow){
14752             this.syncBodyHeight();
14753         }else if(this.firstShow){
14754             this.firstShow = false;
14755             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14756         }
14757         this.animateTarget = animateTarget || this.animateTarget;
14758         if(!this.el.isVisible()){
14759             this.beforeShow();
14760             if(this.animateTarget && Roo.get(this.animateTarget)){
14761                 this.animShow();
14762             }else{
14763                 this.showEl();
14764             }
14765         }
14766         return this;
14767     },
14768
14769     // private
14770     showEl : function(){
14771         this.proxy.hide();
14772         this.el.setXY(this.xy);
14773         this.el.show();
14774         this.adjustAssets(true);
14775         this.toFront();
14776         this.focus();
14777         // IE peekaboo bug - fix found by Dave Fenwick
14778         if(Roo.isIE){
14779             this.el.repaint();
14780         }
14781         this.fireEvent("show", this);
14782     },
14783
14784     /**
14785      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14786      * dialog itself will receive focus.
14787      */
14788     focus : function(){
14789         if(this.defaultButton){
14790             this.defaultButton.focus();
14791         }else{
14792             this.focusEl.focus();
14793         }
14794     },
14795
14796     // private
14797     constrainXY : function(){
14798         if(this.constraintoviewport !== false){
14799             if(!this.viewSize){
14800                 if(this.container){
14801                     var s = this.container.getSize();
14802                     this.viewSize = [s.width, s.height];
14803                 }else{
14804                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14805                 }
14806             }
14807             var s = Roo.get(this.container||document).getScroll();
14808
14809             var x = this.xy[0], y = this.xy[1];
14810             var w = this.size.width, h = this.size.height;
14811             var vw = this.viewSize[0], vh = this.viewSize[1];
14812             // only move it if it needs it
14813             var moved = false;
14814             // first validate right/bottom
14815             if(x + w > vw+s.left){
14816                 x = vw - w;
14817                 moved = true;
14818             }
14819             if(y + h > vh+s.top){
14820                 y = vh - h;
14821                 moved = true;
14822             }
14823             // then make sure top/left isn't negative
14824             if(x < s.left){
14825                 x = s.left;
14826                 moved = true;
14827             }
14828             if(y < s.top){
14829                 y = s.top;
14830                 moved = true;
14831             }
14832             if(moved){
14833                 // cache xy
14834                 this.xy = [x, y];
14835                 if(this.isVisible()){
14836                     this.el.setLocation(x, y);
14837                     this.adjustAssets();
14838                 }
14839             }
14840         }
14841     },
14842
14843     // private
14844     onDrag : function(){
14845         if(!this.proxyDrag){
14846             this.xy = this.el.getXY();
14847             this.adjustAssets();
14848         }
14849     },
14850
14851     // private
14852     adjustAssets : function(doShow){
14853         var x = this.xy[0], y = this.xy[1];
14854         var w = this.size.width, h = this.size.height;
14855         if(doShow === true){
14856             if(this.shadow){
14857                 this.shadow.show(this.el);
14858             }
14859             if(this.shim){
14860                 this.shim.show();
14861             }
14862         }
14863         if(this.shadow && this.shadow.isVisible()){
14864             this.shadow.show(this.el);
14865         }
14866         if(this.shim && this.shim.isVisible()){
14867             this.shim.setBounds(x, y, w, h);
14868         }
14869     },
14870
14871     // private
14872     adjustViewport : function(w, h){
14873         if(!w || !h){
14874             w = Roo.lib.Dom.getViewWidth();
14875             h = Roo.lib.Dom.getViewHeight();
14876         }
14877         // cache the size
14878         this.viewSize = [w, h];
14879         if(this.modal && this.mask.isVisible()){
14880             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14881             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14882         }
14883         if(this.isVisible()){
14884             this.constrainXY();
14885         }
14886     },
14887
14888     /**
14889      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14890      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14891      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14892      */
14893     destroy : function(removeEl){
14894         if(this.isVisible()){
14895             this.animateTarget = null;
14896             this.hide();
14897         }
14898         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14899         if(this.tabs){
14900             this.tabs.destroy(removeEl);
14901         }
14902         Roo.destroy(
14903              this.shim,
14904              this.proxy,
14905              this.resizer,
14906              this.close,
14907              this.mask
14908         );
14909         if(this.dd){
14910             this.dd.unreg();
14911         }
14912         if(this.buttons){
14913            for(var i = 0, len = this.buttons.length; i < len; i++){
14914                this.buttons[i].destroy();
14915            }
14916         }
14917         this.el.removeAllListeners();
14918         if(removeEl === true){
14919             this.el.update("");
14920             this.el.remove();
14921         }
14922         Roo.DialogManager.unregister(this);
14923     },
14924
14925     // private
14926     startMove : function(){
14927         if(this.proxyDrag){
14928             this.proxy.show();
14929         }
14930         if(this.constraintoviewport !== false){
14931             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14932         }
14933     },
14934
14935     // private
14936     endMove : function(){
14937         if(!this.proxyDrag){
14938             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14939         }else{
14940             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14941             this.proxy.hide();
14942         }
14943         this.refreshSize();
14944         this.adjustAssets();
14945         this.focus();
14946         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14947     },
14948
14949     /**
14950      * Brings this dialog to the front of any other visible dialogs
14951      * @return {Roo.BasicDialog} this
14952      */
14953     toFront : function(){
14954         Roo.DialogManager.bringToFront(this);
14955         return this;
14956     },
14957
14958     /**
14959      * Sends this dialog to the back (under) of any other visible dialogs
14960      * @return {Roo.BasicDialog} this
14961      */
14962     toBack : function(){
14963         Roo.DialogManager.sendToBack(this);
14964         return this;
14965     },
14966
14967     /**
14968      * Centers this dialog in the viewport
14969      * @return {Roo.BasicDialog} this
14970      */
14971     center : function(){
14972         var xy = this.el.getCenterXY(true);
14973         this.moveTo(xy[0], xy[1]);
14974         return this;
14975     },
14976
14977     /**
14978      * Moves the dialog's top-left corner to the specified point
14979      * @param {Number} x
14980      * @param {Number} y
14981      * @return {Roo.BasicDialog} this
14982      */
14983     moveTo : function(x, y){
14984         this.xy = [x,y];
14985         if(this.isVisible()){
14986             this.el.setXY(this.xy);
14987             this.adjustAssets();
14988         }
14989         return this;
14990     },
14991
14992     /**
14993      * Aligns the dialog to the specified element
14994      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14995      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14996      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14997      * @return {Roo.BasicDialog} this
14998      */
14999     alignTo : function(element, position, offsets){
15000         this.xy = this.el.getAlignToXY(element, position, offsets);
15001         if(this.isVisible()){
15002             this.el.setXY(this.xy);
15003             this.adjustAssets();
15004         }
15005         return this;
15006     },
15007
15008     /**
15009      * Anchors an element to another element and realigns it when the window is resized.
15010      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15011      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15012      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15013      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15014      * is a number, it is used as the buffer delay (defaults to 50ms).
15015      * @return {Roo.BasicDialog} this
15016      */
15017     anchorTo : function(el, alignment, offsets, monitorScroll){
15018         var action = function(){
15019             this.alignTo(el, alignment, offsets);
15020         };
15021         Roo.EventManager.onWindowResize(action, this);
15022         var tm = typeof monitorScroll;
15023         if(tm != 'undefined'){
15024             Roo.EventManager.on(window, 'scroll', action, this,
15025                 {buffer: tm == 'number' ? monitorScroll : 50});
15026         }
15027         action.call(this);
15028         return this;
15029     },
15030
15031     /**
15032      * Returns true if the dialog is visible
15033      * @return {Boolean}
15034      */
15035     isVisible : function(){
15036         return this.el.isVisible();
15037     },
15038
15039     // private
15040     animHide : function(callback){
15041         var b = Roo.get(this.animateTarget).getBox();
15042         this.proxy.show();
15043         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15044         this.el.hide();
15045         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15046                     this.hideEl.createDelegate(this, [callback]));
15047     },
15048
15049     /**
15050      * Hides the dialog.
15051      * @param {Function} callback (optional) Function to call when the dialog is hidden
15052      * @return {Roo.BasicDialog} this
15053      */
15054     hide : function(callback){
15055         if (this.fireEvent("beforehide", this) === false){
15056             return;
15057         }
15058         if(this.shadow){
15059             this.shadow.hide();
15060         }
15061         if(this.shim) {
15062           this.shim.hide();
15063         }
15064         // sometimes animateTarget seems to get set.. causing problems...
15065         // this just double checks..
15066         if(this.animateTarget && Roo.get(this.animateTarget)) {
15067            this.animHide(callback);
15068         }else{
15069             this.el.hide();
15070             this.hideEl(callback);
15071         }
15072         return this;
15073     },
15074
15075     // private
15076     hideEl : function(callback){
15077         this.proxy.hide();
15078         if(this.modal){
15079             this.mask.hide();
15080             Roo.get(document.body).removeClass("x-body-masked");
15081         }
15082         this.fireEvent("hide", this);
15083         if(typeof callback == "function"){
15084             callback();
15085         }
15086     },
15087
15088     // private
15089     hideAction : function(){
15090         this.setLeft("-10000px");
15091         this.setTop("-10000px");
15092         this.setStyle("visibility", "hidden");
15093     },
15094
15095     // private
15096     refreshSize : function(){
15097         this.size = this.el.getSize();
15098         this.xy = this.el.getXY();
15099         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15100     },
15101
15102     // private
15103     // z-index is managed by the DialogManager and may be overwritten at any time
15104     setZIndex : function(index){
15105         if(this.modal){
15106             this.mask.setStyle("z-index", index);
15107         }
15108         if(this.shim){
15109             this.shim.setStyle("z-index", ++index);
15110         }
15111         if(this.shadow){
15112             this.shadow.setZIndex(++index);
15113         }
15114         this.el.setStyle("z-index", ++index);
15115         if(this.proxy){
15116             this.proxy.setStyle("z-index", ++index);
15117         }
15118         if(this.resizer){
15119             this.resizer.proxy.setStyle("z-index", ++index);
15120         }
15121
15122         this.lastZIndex = index;
15123     },
15124
15125     /**
15126      * Returns the element for this dialog
15127      * @return {Roo.Element} The underlying dialog Element
15128      */
15129     getEl : function(){
15130         return this.el;
15131     }
15132 });
15133
15134 /**
15135  * @class Roo.DialogManager
15136  * Provides global access to BasicDialogs that have been created and
15137  * support for z-indexing (layering) multiple open dialogs.
15138  */
15139 Roo.DialogManager = function(){
15140     var list = {};
15141     var accessList = [];
15142     var front = null;
15143
15144     // private
15145     var sortDialogs = function(d1, d2){
15146         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15147     };
15148
15149     // private
15150     var orderDialogs = function(){
15151         accessList.sort(sortDialogs);
15152         var seed = Roo.DialogManager.zseed;
15153         for(var i = 0, len = accessList.length; i < len; i++){
15154             var dlg = accessList[i];
15155             if(dlg){
15156                 dlg.setZIndex(seed + (i*10));
15157             }
15158         }
15159     };
15160
15161     return {
15162         /**
15163          * The starting z-index for BasicDialogs (defaults to 9000)
15164          * @type Number The z-index value
15165          */
15166         zseed : 9000,
15167
15168         // private
15169         register : function(dlg){
15170             list[dlg.id] = dlg;
15171             accessList.push(dlg);
15172         },
15173
15174         // private
15175         unregister : function(dlg){
15176             delete list[dlg.id];
15177             var i=0;
15178             var len=0;
15179             if(!accessList.indexOf){
15180                 for(  i = 0, len = accessList.length; i < len; i++){
15181                     if(accessList[i] == dlg){
15182                         accessList.splice(i, 1);
15183                         return;
15184                     }
15185                 }
15186             }else{
15187                  i = accessList.indexOf(dlg);
15188                 if(i != -1){
15189                     accessList.splice(i, 1);
15190                 }
15191             }
15192         },
15193
15194         /**
15195          * Gets a registered dialog by id
15196          * @param {String/Object} id The id of the dialog or a dialog
15197          * @return {Roo.BasicDialog} this
15198          */
15199         get : function(id){
15200             return typeof id == "object" ? id : list[id];
15201         },
15202
15203         /**
15204          * Brings the specified dialog to the front
15205          * @param {String/Object} dlg The id of the dialog or a dialog
15206          * @return {Roo.BasicDialog} this
15207          */
15208         bringToFront : function(dlg){
15209             dlg = this.get(dlg);
15210             if(dlg != front){
15211                 front = dlg;
15212                 dlg._lastAccess = new Date().getTime();
15213                 orderDialogs();
15214             }
15215             return dlg;
15216         },
15217
15218         /**
15219          * Sends the specified dialog to the back
15220          * @param {String/Object} dlg The id of the dialog or a dialog
15221          * @return {Roo.BasicDialog} this
15222          */
15223         sendToBack : function(dlg){
15224             dlg = this.get(dlg);
15225             dlg._lastAccess = -(new Date().getTime());
15226             orderDialogs();
15227             return dlg;
15228         },
15229
15230         /**
15231          * Hides all dialogs
15232          */
15233         hideAll : function(){
15234             for(var id in list){
15235                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15236                     list[id].hide();
15237                 }
15238             }
15239         }
15240     };
15241 }();
15242
15243 /**
15244  * @class Roo.LayoutDialog
15245  * @extends Roo.BasicDialog
15246  * Dialog which provides adjustments for working with a layout in a Dialog.
15247  * Add your necessary layout config options to the dialog's config.<br>
15248  * Example usage (including a nested layout):
15249  * <pre><code>
15250 if(!dialog){
15251     dialog = new Roo.LayoutDialog("download-dlg", {
15252         modal: true,
15253         width:600,
15254         height:450,
15255         shadow:true,
15256         minWidth:500,
15257         minHeight:350,
15258         autoTabs:true,
15259         proxyDrag:true,
15260         // layout config merges with the dialog config
15261         center:{
15262             tabPosition: "top",
15263             alwaysShowTabs: true
15264         }
15265     });
15266     dialog.addKeyListener(27, dialog.hide, dialog);
15267     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15268     dialog.addButton("Build It!", this.getDownload, this);
15269
15270     // we can even add nested layouts
15271     var innerLayout = new Roo.BorderLayout("dl-inner", {
15272         east: {
15273             initialSize: 200,
15274             autoScroll:true,
15275             split:true
15276         },
15277         center: {
15278             autoScroll:true
15279         }
15280     });
15281     innerLayout.beginUpdate();
15282     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15283     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15284     innerLayout.endUpdate(true);
15285
15286     var layout = dialog.getLayout();
15287     layout.beginUpdate();
15288     layout.add("center", new Roo.ContentPanel("standard-panel",
15289                         {title: "Download the Source", fitToFrame:true}));
15290     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15291                {title: "Build your own roo.js"}));
15292     layout.getRegion("center").showPanel(sp);
15293     layout.endUpdate();
15294 }
15295 </code></pre>
15296     * @constructor
15297     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15298     * @param {Object} config configuration options
15299   */
15300 Roo.LayoutDialog = function(el, cfg){
15301     
15302     var config=  cfg;
15303     if (typeof(cfg) == 'undefined') {
15304         config = Roo.apply({}, el);
15305         // not sure why we use documentElement here.. - it should always be body.
15306         // IE7 borks horribly if we use documentElement.
15307         // webkit also does not like documentElement - it creates a body element...
15308         el = Roo.get( document.body || document.documentElement ).createChild();
15309         //config.autoCreate = true;
15310     }
15311     
15312     
15313     config.autoTabs = false;
15314     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15315     this.body.setStyle({overflow:"hidden", position:"relative"});
15316     this.layout = new Roo.BorderLayout(this.body.dom, config);
15317     this.layout.monitorWindowResize = false;
15318     this.el.addClass("x-dlg-auto-layout");
15319     // fix case when center region overwrites center function
15320     this.center = Roo.BasicDialog.prototype.center;
15321     this.on("show", this.layout.layout, this.layout, true);
15322     if (config.items) {
15323         var xitems = config.items;
15324         delete config.items;
15325         Roo.each(xitems, this.addxtype, this);
15326     }
15327     
15328     
15329 };
15330 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15331     /**
15332      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15333      * @deprecated
15334      */
15335     endUpdate : function(){
15336         this.layout.endUpdate();
15337     },
15338
15339     /**
15340      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15341      *  @deprecated
15342      */
15343     beginUpdate : function(){
15344         this.layout.beginUpdate();
15345     },
15346
15347     /**
15348      * Get the BorderLayout for this dialog
15349      * @return {Roo.BorderLayout}
15350      */
15351     getLayout : function(){
15352         return this.layout;
15353     },
15354
15355     showEl : function(){
15356         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15357         if(Roo.isIE7){
15358             this.layout.layout();
15359         }
15360     },
15361
15362     // private
15363     // Use the syncHeightBeforeShow config option to control this automatically
15364     syncBodyHeight : function(){
15365         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15366         if(this.layout){this.layout.layout();}
15367     },
15368     
15369       /**
15370      * Add an xtype element (actually adds to the layout.)
15371      * @return {Object} xdata xtype object data.
15372      */
15373     
15374     addxtype : function(c) {
15375         return this.layout.addxtype(c);
15376     }
15377 });/*
15378  * Based on:
15379  * Ext JS Library 1.1.1
15380  * Copyright(c) 2006-2007, Ext JS, LLC.
15381  *
15382  * Originally Released Under LGPL - original licence link has changed is not relivant.
15383  *
15384  * Fork - LGPL
15385  * <script type="text/javascript">
15386  */
15387  
15388 /**
15389  * @class Roo.MessageBox
15390  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15391  * Example usage:
15392  *<pre><code>
15393 // Basic alert:
15394 Roo.Msg.alert('Status', 'Changes saved successfully.');
15395
15396 // Prompt for user data:
15397 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15398     if (btn == 'ok'){
15399         // process text value...
15400     }
15401 });
15402
15403 // Show a dialog using config options:
15404 Roo.Msg.show({
15405    title:'Save Changes?',
15406    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15407    buttons: Roo.Msg.YESNOCANCEL,
15408    fn: processResult,
15409    animEl: 'elId'
15410 });
15411 </code></pre>
15412  * @singleton
15413  */
15414 Roo.MessageBox = function(){
15415     var dlg, opt, mask, waitTimer;
15416     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15417     var buttons, activeTextEl, bwidth;
15418
15419     // private
15420     var handleButton = function(button){
15421         dlg.hide();
15422         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15423     };
15424
15425     // private
15426     var handleHide = function(){
15427         if(opt && opt.cls){
15428             dlg.el.removeClass(opt.cls);
15429         }
15430         if(waitTimer){
15431             Roo.TaskMgr.stop(waitTimer);
15432             waitTimer = null;
15433         }
15434     };
15435
15436     // private
15437     var updateButtons = function(b){
15438         var width = 0;
15439         if(!b){
15440             buttons["ok"].hide();
15441             buttons["cancel"].hide();
15442             buttons["yes"].hide();
15443             buttons["no"].hide();
15444             dlg.footer.dom.style.display = 'none';
15445             return width;
15446         }
15447         dlg.footer.dom.style.display = '';
15448         for(var k in buttons){
15449             if(typeof buttons[k] != "function"){
15450                 if(b[k]){
15451                     buttons[k].show();
15452                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15453                     width += buttons[k].el.getWidth()+15;
15454                 }else{
15455                     buttons[k].hide();
15456                 }
15457             }
15458         }
15459         return width;
15460     };
15461
15462     // private
15463     var handleEsc = function(d, k, e){
15464         if(opt && opt.closable !== false){
15465             dlg.hide();
15466         }
15467         if(e){
15468             e.stopEvent();
15469         }
15470     };
15471
15472     return {
15473         /**
15474          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15475          * @return {Roo.BasicDialog} The BasicDialog element
15476          */
15477         getDialog : function(){
15478            if(!dlg){
15479                 dlg = new Roo.BasicDialog("x-msg-box", {
15480                     autoCreate : true,
15481                     shadow: true,
15482                     draggable: true,
15483                     resizable:false,
15484                     constraintoviewport:false,
15485                     fixedcenter:true,
15486                     collapsible : false,
15487                     shim:true,
15488                     modal: true,
15489                     width:400, height:100,
15490                     buttonAlign:"center",
15491                     closeClick : function(){
15492                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15493                             handleButton("no");
15494                         }else{
15495                             handleButton("cancel");
15496                         }
15497                     }
15498                 });
15499                 dlg.on("hide", handleHide);
15500                 mask = dlg.mask;
15501                 dlg.addKeyListener(27, handleEsc);
15502                 buttons = {};
15503                 var bt = this.buttonText;
15504                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15505                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15506                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15507                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15508                 bodyEl = dlg.body.createChild({
15509
15510                     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>'
15511                 });
15512                 msgEl = bodyEl.dom.firstChild;
15513                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15514                 textboxEl.enableDisplayMode();
15515                 textboxEl.addKeyListener([10,13], function(){
15516                     if(dlg.isVisible() && opt && opt.buttons){
15517                         if(opt.buttons.ok){
15518                             handleButton("ok");
15519                         }else if(opt.buttons.yes){
15520                             handleButton("yes");
15521                         }
15522                     }
15523                 });
15524                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15525                 textareaEl.enableDisplayMode();
15526                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15527                 progressEl.enableDisplayMode();
15528                 var pf = progressEl.dom.firstChild;
15529                 if (pf) {
15530                     pp = Roo.get(pf.firstChild);
15531                     pp.setHeight(pf.offsetHeight);
15532                 }
15533                 
15534             }
15535             return dlg;
15536         },
15537
15538         /**
15539          * Updates the message box body text
15540          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15541          * the XHTML-compliant non-breaking space character '&amp;#160;')
15542          * @return {Roo.MessageBox} This message box
15543          */
15544         updateText : function(text){
15545             if(!dlg.isVisible() && !opt.width){
15546                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15547             }
15548             msgEl.innerHTML = text || '&#160;';
15549             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15550                         Math.max(opt.minWidth || this.minWidth, bwidth));
15551             if(opt.prompt){
15552                 activeTextEl.setWidth(w);
15553             }
15554             if(dlg.isVisible()){
15555                 dlg.fixedcenter = false;
15556             }
15557             dlg.setContentSize(w, bodyEl.getHeight());
15558             if(dlg.isVisible()){
15559                 dlg.fixedcenter = true;
15560             }
15561             return this;
15562         },
15563
15564         /**
15565          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15566          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15567          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15568          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15569          * @return {Roo.MessageBox} This message box
15570          */
15571         updateProgress : function(value, text){
15572             if(text){
15573                 this.updateText(text);
15574             }
15575             if (pp) { // weird bug on my firefox - for some reason this is not defined
15576                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15577             }
15578             return this;
15579         },        
15580
15581         /**
15582          * Returns true if the message box is currently displayed
15583          * @return {Boolean} True if the message box is visible, else false
15584          */
15585         isVisible : function(){
15586             return dlg && dlg.isVisible();  
15587         },
15588
15589         /**
15590          * Hides the message box if it is displayed
15591          */
15592         hide : function(){
15593             if(this.isVisible()){
15594                 dlg.hide();
15595             }  
15596         },
15597
15598         /**
15599          * Displays a new message box, or reinitializes an existing message box, based on the config options
15600          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15601          * The following config object properties are supported:
15602          * <pre>
15603 Property    Type             Description
15604 ----------  ---------------  ------------------------------------------------------------------------------------
15605 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15606                                    closes (defaults to undefined)
15607 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15608                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15609 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15610                                    progress and wait dialogs will ignore this property and always hide the
15611                                    close button as they can only be closed programmatically.
15612 cls               String           A custom CSS class to apply to the message box element
15613 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15614                                    displayed (defaults to 75)
15615 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15616                                    function will be btn (the name of the button that was clicked, if applicable,
15617                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15618                                    Progress and wait dialogs will ignore this option since they do not respond to
15619                                    user actions and can only be closed programmatically, so any required function
15620                                    should be called by the same code after it closes the dialog.
15621 icon              String           A CSS class that provides a background image to be used as an icon for
15622                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15623 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15624 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15625 modal             Boolean          False to allow user interaction with the page while the message box is
15626                                    displayed (defaults to true)
15627 msg               String           A string that will replace the existing message box body text (defaults
15628                                    to the XHTML-compliant non-breaking space character '&#160;')
15629 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15630 progress          Boolean          True to display a progress bar (defaults to false)
15631 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15632 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15633 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15634 title             String           The title text
15635 value             String           The string value to set into the active textbox element if displayed
15636 wait              Boolean          True to display a progress bar (defaults to false)
15637 width             Number           The width of the dialog in pixels
15638 </pre>
15639          *
15640          * Example usage:
15641          * <pre><code>
15642 Roo.Msg.show({
15643    title: 'Address',
15644    msg: 'Please enter your address:',
15645    width: 300,
15646    buttons: Roo.MessageBox.OKCANCEL,
15647    multiline: true,
15648    fn: saveAddress,
15649    animEl: 'addAddressBtn'
15650 });
15651 </code></pre>
15652          * @param {Object} config Configuration options
15653          * @return {Roo.MessageBox} This message box
15654          */
15655         show : function(options){
15656             if(this.isVisible()){
15657                 this.hide();
15658             }
15659             var d = this.getDialog();
15660             opt = options;
15661             d.setTitle(opt.title || "&#160;");
15662             d.close.setDisplayed(opt.closable !== false);
15663             activeTextEl = textboxEl;
15664             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15665             if(opt.prompt){
15666                 if(opt.multiline){
15667                     textboxEl.hide();
15668                     textareaEl.show();
15669                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15670                         opt.multiline : this.defaultTextHeight);
15671                     activeTextEl = textareaEl;
15672                 }else{
15673                     textboxEl.show();
15674                     textareaEl.hide();
15675                 }
15676             }else{
15677                 textboxEl.hide();
15678                 textareaEl.hide();
15679             }
15680             progressEl.setDisplayed(opt.progress === true);
15681             this.updateProgress(0);
15682             activeTextEl.dom.value = opt.value || "";
15683             if(opt.prompt){
15684                 dlg.setDefaultButton(activeTextEl);
15685             }else{
15686                 var bs = opt.buttons;
15687                 var db = null;
15688                 if(bs && bs.ok){
15689                     db = buttons["ok"];
15690                 }else if(bs && bs.yes){
15691                     db = buttons["yes"];
15692                 }
15693                 dlg.setDefaultButton(db);
15694             }
15695             bwidth = updateButtons(opt.buttons);
15696             this.updateText(opt.msg);
15697             if(opt.cls){
15698                 d.el.addClass(opt.cls);
15699             }
15700             d.proxyDrag = opt.proxyDrag === true;
15701             d.modal = opt.modal !== false;
15702             d.mask = opt.modal !== false ? mask : false;
15703             if(!d.isVisible()){
15704                 // force it to the end of the z-index stack so it gets a cursor in FF
15705                 document.body.appendChild(dlg.el.dom);
15706                 d.animateTarget = null;
15707                 d.show(options.animEl);
15708             }
15709             return this;
15710         },
15711
15712         /**
15713          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15714          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15715          * and closing the message box when the process is complete.
15716          * @param {String} title The title bar text
15717          * @param {String} msg The message box body text
15718          * @return {Roo.MessageBox} This message box
15719          */
15720         progress : function(title, msg){
15721             this.show({
15722                 title : title,
15723                 msg : msg,
15724                 buttons: false,
15725                 progress:true,
15726                 closable:false,
15727                 minWidth: this.minProgressWidth,
15728                 modal : true
15729             });
15730             return this;
15731         },
15732
15733         /**
15734          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15735          * If a callback function is passed it will be called after the user clicks the button, and the
15736          * id of the button that was clicked will be passed as the only parameter to the callback
15737          * (could also be the top-right close button).
15738          * @param {String} title The title bar text
15739          * @param {String} msg The message box body text
15740          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15741          * @param {Object} scope (optional) The scope of the callback function
15742          * @return {Roo.MessageBox} This message box
15743          */
15744         alert : function(title, msg, fn, scope){
15745             this.show({
15746                 title : title,
15747                 msg : msg,
15748                 buttons: this.OK,
15749                 fn: fn,
15750                 scope : scope,
15751                 modal : true
15752             });
15753             return this;
15754         },
15755
15756         /**
15757          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15758          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15759          * You are responsible for closing the message box when the process is complete.
15760          * @param {String} msg The message box body text
15761          * @param {String} title (optional) The title bar text
15762          * @return {Roo.MessageBox} This message box
15763          */
15764         wait : function(msg, title){
15765             this.show({
15766                 title : title,
15767                 msg : msg,
15768                 buttons: false,
15769                 closable:false,
15770                 progress:true,
15771                 modal:true,
15772                 width:300,
15773                 wait:true
15774             });
15775             waitTimer = Roo.TaskMgr.start({
15776                 run: function(i){
15777                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15778                 },
15779                 interval: 1000
15780             });
15781             return this;
15782         },
15783
15784         /**
15785          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15786          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15787          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15788          * @param {String} title The title bar text
15789          * @param {String} msg The message box body text
15790          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15791          * @param {Object} scope (optional) The scope of the callback function
15792          * @return {Roo.MessageBox} This message box
15793          */
15794         confirm : function(title, msg, fn, scope){
15795             this.show({
15796                 title : title,
15797                 msg : msg,
15798                 buttons: this.YESNO,
15799                 fn: fn,
15800                 scope : scope,
15801                 modal : true
15802             });
15803             return this;
15804         },
15805
15806         /**
15807          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15808          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15809          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15810          * (could also be the top-right close button) and the text that was entered will be passed as the two
15811          * parameters to the callback.
15812          * @param {String} title The title bar text
15813          * @param {String} msg The message box body text
15814          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15815          * @param {Object} scope (optional) The scope of the callback function
15816          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15817          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15818          * @return {Roo.MessageBox} This message box
15819          */
15820         prompt : function(title, msg, fn, scope, multiline){
15821             this.show({
15822                 title : title,
15823                 msg : msg,
15824                 buttons: this.OKCANCEL,
15825                 fn: fn,
15826                 minWidth:250,
15827                 scope : scope,
15828                 prompt:true,
15829                 multiline: multiline,
15830                 modal : true
15831             });
15832             return this;
15833         },
15834
15835         /**
15836          * Button config that displays a single OK button
15837          * @type Object
15838          */
15839         OK : {ok:true},
15840         /**
15841          * Button config that displays Yes and No buttons
15842          * @type Object
15843          */
15844         YESNO : {yes:true, no:true},
15845         /**
15846          * Button config that displays OK and Cancel buttons
15847          * @type Object
15848          */
15849         OKCANCEL : {ok:true, cancel:true},
15850         /**
15851          * Button config that displays Yes, No and Cancel buttons
15852          * @type Object
15853          */
15854         YESNOCANCEL : {yes:true, no:true, cancel:true},
15855
15856         /**
15857          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15858          * @type Number
15859          */
15860         defaultTextHeight : 75,
15861         /**
15862          * The maximum width in pixels of the message box (defaults to 600)
15863          * @type Number
15864          */
15865         maxWidth : 600,
15866         /**
15867          * The minimum width in pixels of the message box (defaults to 100)
15868          * @type Number
15869          */
15870         minWidth : 100,
15871         /**
15872          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15873          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15874          * @type Number
15875          */
15876         minProgressWidth : 250,
15877         /**
15878          * An object containing the default button text strings that can be overriden for localized language support.
15879          * Supported properties are: ok, cancel, yes and no.
15880          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15881          * @type Object
15882          */
15883         buttonText : {
15884             ok : "OK",
15885             cancel : "Cancel",
15886             yes : "Yes",
15887             no : "No"
15888         }
15889     };
15890 }();
15891
15892 /**
15893  * Shorthand for {@link Roo.MessageBox}
15894  */
15895 Roo.Msg = Roo.MessageBox;/*
15896  * Based on:
15897  * Ext JS Library 1.1.1
15898  * Copyright(c) 2006-2007, Ext JS, LLC.
15899  *
15900  * Originally Released Under LGPL - original licence link has changed is not relivant.
15901  *
15902  * Fork - LGPL
15903  * <script type="text/javascript">
15904  */
15905 /**
15906  * @class Roo.QuickTips
15907  * Provides attractive and customizable tooltips for any element.
15908  * @singleton
15909  */
15910 Roo.QuickTips = function(){
15911     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15912     var ce, bd, xy, dd;
15913     var visible = false, disabled = true, inited = false;
15914     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15915     
15916     var onOver = function(e){
15917         if(disabled){
15918             return;
15919         }
15920         var t = e.getTarget();
15921         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15922             return;
15923         }
15924         if(ce && t == ce.el){
15925             clearTimeout(hideProc);
15926             return;
15927         }
15928         if(t && tagEls[t.id]){
15929             tagEls[t.id].el = t;
15930             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15931             return;
15932         }
15933         var ttp, et = Roo.fly(t);
15934         var ns = cfg.namespace;
15935         if(tm.interceptTitles && t.title){
15936             ttp = t.title;
15937             t.qtip = ttp;
15938             t.removeAttribute("title");
15939             e.preventDefault();
15940         }else{
15941             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15942         }
15943         if(ttp){
15944             showProc = show.defer(tm.showDelay, tm, [{
15945                 el: t, 
15946                 text: ttp, 
15947                 width: et.getAttributeNS(ns, cfg.width),
15948                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15949                 title: et.getAttributeNS(ns, cfg.title),
15950                     cls: et.getAttributeNS(ns, cfg.cls)
15951             }]);
15952         }
15953     };
15954     
15955     var onOut = function(e){
15956         clearTimeout(showProc);
15957         var t = e.getTarget();
15958         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15959             hideProc = setTimeout(hide, tm.hideDelay);
15960         }
15961     };
15962     
15963     var onMove = function(e){
15964         if(disabled){
15965             return;
15966         }
15967         xy = e.getXY();
15968         xy[1] += 18;
15969         if(tm.trackMouse && ce){
15970             el.setXY(xy);
15971         }
15972     };
15973     
15974     var onDown = function(e){
15975         clearTimeout(showProc);
15976         clearTimeout(hideProc);
15977         if(!e.within(el)){
15978             if(tm.hideOnClick){
15979                 hide();
15980                 tm.disable();
15981                 tm.enable.defer(100, tm);
15982             }
15983         }
15984     };
15985     
15986     var getPad = function(){
15987         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15988     };
15989
15990     var show = function(o){
15991         if(disabled){
15992             return;
15993         }
15994         clearTimeout(dismissProc);
15995         ce = o;
15996         if(removeCls){ // in case manually hidden
15997             el.removeClass(removeCls);
15998             removeCls = null;
15999         }
16000         if(ce.cls){
16001             el.addClass(ce.cls);
16002             removeCls = ce.cls;
16003         }
16004         if(ce.title){
16005             tipTitle.update(ce.title);
16006             tipTitle.show();
16007         }else{
16008             tipTitle.update('');
16009             tipTitle.hide();
16010         }
16011         el.dom.style.width  = tm.maxWidth+'px';
16012         //tipBody.dom.style.width = '';
16013         tipBodyText.update(o.text);
16014         var p = getPad(), w = ce.width;
16015         if(!w){
16016             var td = tipBodyText.dom;
16017             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16018             if(aw > tm.maxWidth){
16019                 w = tm.maxWidth;
16020             }else if(aw < tm.minWidth){
16021                 w = tm.minWidth;
16022             }else{
16023                 w = aw;
16024             }
16025         }
16026         //tipBody.setWidth(w);
16027         el.setWidth(parseInt(w, 10) + p);
16028         if(ce.autoHide === false){
16029             close.setDisplayed(true);
16030             if(dd){
16031                 dd.unlock();
16032             }
16033         }else{
16034             close.setDisplayed(false);
16035             if(dd){
16036                 dd.lock();
16037             }
16038         }
16039         if(xy){
16040             el.avoidY = xy[1]-18;
16041             el.setXY(xy);
16042         }
16043         if(tm.animate){
16044             el.setOpacity(.1);
16045             el.setStyle("visibility", "visible");
16046             el.fadeIn({callback: afterShow});
16047         }else{
16048             afterShow();
16049         }
16050     };
16051     
16052     var afterShow = function(){
16053         if(ce){
16054             el.show();
16055             esc.enable();
16056             if(tm.autoDismiss && ce.autoHide !== false){
16057                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16058             }
16059         }
16060     };
16061     
16062     var hide = function(noanim){
16063         clearTimeout(dismissProc);
16064         clearTimeout(hideProc);
16065         ce = null;
16066         if(el.isVisible()){
16067             esc.disable();
16068             if(noanim !== true && tm.animate){
16069                 el.fadeOut({callback: afterHide});
16070             }else{
16071                 afterHide();
16072             } 
16073         }
16074     };
16075     
16076     var afterHide = function(){
16077         el.hide();
16078         if(removeCls){
16079             el.removeClass(removeCls);
16080             removeCls = null;
16081         }
16082     };
16083     
16084     return {
16085         /**
16086         * @cfg {Number} minWidth
16087         * The minimum width of the quick tip (defaults to 40)
16088         */
16089        minWidth : 40,
16090         /**
16091         * @cfg {Number} maxWidth
16092         * The maximum width of the quick tip (defaults to 300)
16093         */
16094        maxWidth : 300,
16095         /**
16096         * @cfg {Boolean} interceptTitles
16097         * True to automatically use the element's DOM title value if available (defaults to false)
16098         */
16099        interceptTitles : false,
16100         /**
16101         * @cfg {Boolean} trackMouse
16102         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16103         */
16104        trackMouse : false,
16105         /**
16106         * @cfg {Boolean} hideOnClick
16107         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16108         */
16109        hideOnClick : true,
16110         /**
16111         * @cfg {Number} showDelay
16112         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16113         */
16114        showDelay : 500,
16115         /**
16116         * @cfg {Number} hideDelay
16117         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16118         */
16119        hideDelay : 200,
16120         /**
16121         * @cfg {Boolean} autoHide
16122         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16123         * Used in conjunction with hideDelay.
16124         */
16125        autoHide : true,
16126         /**
16127         * @cfg {Boolean}
16128         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16129         * (defaults to true).  Used in conjunction with autoDismissDelay.
16130         */
16131        autoDismiss : true,
16132         /**
16133         * @cfg {Number}
16134         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16135         */
16136        autoDismissDelay : 5000,
16137        /**
16138         * @cfg {Boolean} animate
16139         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16140         */
16141        animate : false,
16142
16143        /**
16144         * @cfg {String} title
16145         * Title text to display (defaults to '').  This can be any valid HTML markup.
16146         */
16147         title: '',
16148        /**
16149         * @cfg {String} text
16150         * Body text to display (defaults to '').  This can be any valid HTML markup.
16151         */
16152         text : '',
16153        /**
16154         * @cfg {String} cls
16155         * A CSS class to apply to the base quick tip element (defaults to '').
16156         */
16157         cls : '',
16158        /**
16159         * @cfg {Number} width
16160         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16161         * minWidth or maxWidth.
16162         */
16163         width : null,
16164
16165     /**
16166      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16167      * or display QuickTips in a page.
16168      */
16169        init : function(){
16170           tm = Roo.QuickTips;
16171           cfg = tm.tagConfig;
16172           if(!inited){
16173               if(!Roo.isReady){ // allow calling of init() before onReady
16174                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16175                   return;
16176               }
16177               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16178               el.fxDefaults = {stopFx: true};
16179               // maximum custom styling
16180               //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>');
16181               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>');              
16182               tipTitle = el.child('h3');
16183               tipTitle.enableDisplayMode("block");
16184               tipBody = el.child('div.x-tip-bd');
16185               tipBodyText = el.child('div.x-tip-bd-inner');
16186               //bdLeft = el.child('div.x-tip-bd-left');
16187               //bdRight = el.child('div.x-tip-bd-right');
16188               close = el.child('div.x-tip-close');
16189               close.enableDisplayMode("block");
16190               close.on("click", hide);
16191               var d = Roo.get(document);
16192               d.on("mousedown", onDown);
16193               d.on("mouseover", onOver);
16194               d.on("mouseout", onOut);
16195               d.on("mousemove", onMove);
16196               esc = d.addKeyListener(27, hide);
16197               esc.disable();
16198               if(Roo.dd.DD){
16199                   dd = el.initDD("default", null, {
16200                       onDrag : function(){
16201                           el.sync();  
16202                       }
16203                   });
16204                   dd.setHandleElId(tipTitle.id);
16205                   dd.lock();
16206               }
16207               inited = true;
16208           }
16209           this.enable(); 
16210        },
16211
16212     /**
16213      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16214      * are supported:
16215      * <pre>
16216 Property    Type                   Description
16217 ----------  ---------------------  ------------------------------------------------------------------------
16218 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16219      * </ul>
16220      * @param {Object} config The config object
16221      */
16222        register : function(config){
16223            var cs = config instanceof Array ? config : arguments;
16224            for(var i = 0, len = cs.length; i < len; i++) {
16225                var c = cs[i];
16226                var target = c.target;
16227                if(target){
16228                    if(target instanceof Array){
16229                        for(var j = 0, jlen = target.length; j < jlen; j++){
16230                            tagEls[target[j]] = c;
16231                        }
16232                    }else{
16233                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16234                    }
16235                }
16236            }
16237        },
16238
16239     /**
16240      * Removes this quick tip from its element and destroys it.
16241      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16242      */
16243        unregister : function(el){
16244            delete tagEls[Roo.id(el)];
16245        },
16246
16247     /**
16248      * Enable this quick tip.
16249      */
16250        enable : function(){
16251            if(inited && disabled){
16252                locks.pop();
16253                if(locks.length < 1){
16254                    disabled = false;
16255                }
16256            }
16257        },
16258
16259     /**
16260      * Disable this quick tip.
16261      */
16262        disable : function(){
16263           disabled = true;
16264           clearTimeout(showProc);
16265           clearTimeout(hideProc);
16266           clearTimeout(dismissProc);
16267           if(ce){
16268               hide(true);
16269           }
16270           locks.push(1);
16271        },
16272
16273     /**
16274      * Returns true if the quick tip is enabled, else false.
16275      */
16276        isEnabled : function(){
16277             return !disabled;
16278        },
16279
16280         // private
16281        tagConfig : {
16282            namespace : "ext",
16283            attribute : "qtip",
16284            width : "width",
16285            target : "target",
16286            title : "qtitle",
16287            hide : "hide",
16288            cls : "qclass"
16289        }
16290    };
16291 }();
16292
16293 // backwards compat
16294 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16295  * Based on:
16296  * Ext JS Library 1.1.1
16297  * Copyright(c) 2006-2007, Ext JS, LLC.
16298  *
16299  * Originally Released Under LGPL - original licence link has changed is not relivant.
16300  *
16301  * Fork - LGPL
16302  * <script type="text/javascript">
16303  */
16304  
16305
16306 /**
16307  * @class Roo.tree.TreePanel
16308  * @extends Roo.data.Tree
16309
16310  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16311  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16312  * @cfg {Boolean} enableDD true to enable drag and drop
16313  * @cfg {Boolean} enableDrag true to enable just drag
16314  * @cfg {Boolean} enableDrop true to enable just drop
16315  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16316  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16317  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16318  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16319  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16320  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16321  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16322  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16323  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16324  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16325  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16326  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16327  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16328  * @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>
16329  * @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>
16330  * 
16331  * @constructor
16332  * @param {String/HTMLElement/Element} el The container element
16333  * @param {Object} config
16334  */
16335 Roo.tree.TreePanel = function(el, config){
16336     var root = false;
16337     var loader = false;
16338     if (config.root) {
16339         root = config.root;
16340         delete config.root;
16341     }
16342     if (config.loader) {
16343         loader = config.loader;
16344         delete config.loader;
16345     }
16346     
16347     Roo.apply(this, config);
16348     Roo.tree.TreePanel.superclass.constructor.call(this);
16349     this.el = Roo.get(el);
16350     this.el.addClass('x-tree');
16351     //console.log(root);
16352     if (root) {
16353         this.setRootNode( Roo.factory(root, Roo.tree));
16354     }
16355     if (loader) {
16356         this.loader = Roo.factory(loader, Roo.tree);
16357     }
16358    /**
16359     * Read-only. The id of the container element becomes this TreePanel's id.
16360     */
16361    this.id = this.el.id;
16362    this.addEvents({
16363         /**
16364         * @event beforeload
16365         * Fires before a node is loaded, return false to cancel
16366         * @param {Node} node The node being loaded
16367         */
16368         "beforeload" : true,
16369         /**
16370         * @event load
16371         * Fires when a node is loaded
16372         * @param {Node} node The node that was loaded
16373         */
16374         "load" : true,
16375         /**
16376         * @event textchange
16377         * Fires when the text for a node is changed
16378         * @param {Node} node The node
16379         * @param {String} text The new text
16380         * @param {String} oldText The old text
16381         */
16382         "textchange" : true,
16383         /**
16384         * @event beforeexpand
16385         * Fires before a node is expanded, return false to cancel.
16386         * @param {Node} node The node
16387         * @param {Boolean} deep
16388         * @param {Boolean} anim
16389         */
16390         "beforeexpand" : true,
16391         /**
16392         * @event beforecollapse
16393         * Fires before a node is collapsed, return false to cancel.
16394         * @param {Node} node The node
16395         * @param {Boolean} deep
16396         * @param {Boolean} anim
16397         */
16398         "beforecollapse" : true,
16399         /**
16400         * @event expand
16401         * Fires when a node is expanded
16402         * @param {Node} node The node
16403         */
16404         "expand" : true,
16405         /**
16406         * @event disabledchange
16407         * Fires when the disabled status of a node changes
16408         * @param {Node} node The node
16409         * @param {Boolean} disabled
16410         */
16411         "disabledchange" : true,
16412         /**
16413         * @event collapse
16414         * Fires when a node is collapsed
16415         * @param {Node} node The node
16416         */
16417         "collapse" : true,
16418         /**
16419         * @event beforeclick
16420         * Fires before click processing on a node. Return false to cancel the default action.
16421         * @param {Node} node The node
16422         * @param {Roo.EventObject} e The event object
16423         */
16424         "beforeclick":true,
16425         /**
16426         * @event checkchange
16427         * Fires when a node with a checkbox's checked property changes
16428         * @param {Node} this This node
16429         * @param {Boolean} checked
16430         */
16431         "checkchange":true,
16432         /**
16433         * @event click
16434         * Fires when a node is clicked
16435         * @param {Node} node The node
16436         * @param {Roo.EventObject} e The event object
16437         */
16438         "click":true,
16439         /**
16440         * @event dblclick
16441         * Fires when a node is double clicked
16442         * @param {Node} node The node
16443         * @param {Roo.EventObject} e The event object
16444         */
16445         "dblclick":true,
16446         /**
16447         * @event contextmenu
16448         * Fires when a node is right clicked
16449         * @param {Node} node The node
16450         * @param {Roo.EventObject} e The event object
16451         */
16452         "contextmenu":true,
16453         /**
16454         * @event beforechildrenrendered
16455         * Fires right before the child nodes for a node are rendered
16456         * @param {Node} node The node
16457         */
16458         "beforechildrenrendered":true,
16459        /**
16460              * @event startdrag
16461              * Fires when a node starts being dragged
16462              * @param {Roo.tree.TreePanel} this
16463              * @param {Roo.tree.TreeNode} node
16464              * @param {event} e The raw browser event
16465              */ 
16466             "startdrag" : true,
16467             /**
16468              * @event enddrag
16469              * Fires when a drag operation is complete
16470              * @param {Roo.tree.TreePanel} this
16471              * @param {Roo.tree.TreeNode} node
16472              * @param {event} e The raw browser event
16473              */
16474             "enddrag" : true,
16475             /**
16476              * @event dragdrop
16477              * Fires when a dragged node is dropped on a valid DD target
16478              * @param {Roo.tree.TreePanel} this
16479              * @param {Roo.tree.TreeNode} node
16480              * @param {DD} dd The dd it was dropped on
16481              * @param {event} e The raw browser event
16482              */
16483             "dragdrop" : true,
16484             /**
16485              * @event beforenodedrop
16486              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16487              * passed to handlers has the following properties:<br />
16488              * <ul style="padding:5px;padding-left:16px;">
16489              * <li>tree - The TreePanel</li>
16490              * <li>target - The node being targeted for the drop</li>
16491              * <li>data - The drag data from the drag source</li>
16492              * <li>point - The point of the drop - append, above or below</li>
16493              * <li>source - The drag source</li>
16494              * <li>rawEvent - Raw mouse event</li>
16495              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16496              * to be inserted by setting them on this object.</li>
16497              * <li>cancel - Set this to true to cancel the drop.</li>
16498              * </ul>
16499              * @param {Object} dropEvent
16500              */
16501             "beforenodedrop" : true,
16502             /**
16503              * @event nodedrop
16504              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16505              * passed to handlers has the following properties:<br />
16506              * <ul style="padding:5px;padding-left:16px;">
16507              * <li>tree - The TreePanel</li>
16508              * <li>target - The node being targeted for the drop</li>
16509              * <li>data - The drag data from the drag source</li>
16510              * <li>point - The point of the drop - append, above or below</li>
16511              * <li>source - The drag source</li>
16512              * <li>rawEvent - Raw mouse event</li>
16513              * <li>dropNode - Dropped node(s).</li>
16514              * </ul>
16515              * @param {Object} dropEvent
16516              */
16517             "nodedrop" : true,
16518              /**
16519              * @event nodedragover
16520              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16521              * passed to handlers has the following properties:<br />
16522              * <ul style="padding:5px;padding-left:16px;">
16523              * <li>tree - The TreePanel</li>
16524              * <li>target - The node being targeted for the drop</li>
16525              * <li>data - The drag data from the drag source</li>
16526              * <li>point - The point of the drop - append, above or below</li>
16527              * <li>source - The drag source</li>
16528              * <li>rawEvent - Raw mouse event</li>
16529              * <li>dropNode - Drop node(s) provided by the source.</li>
16530              * <li>cancel - Set this to true to signal drop not allowed.</li>
16531              * </ul>
16532              * @param {Object} dragOverEvent
16533              */
16534             "nodedragover" : true
16535         
16536    });
16537    if(this.singleExpand){
16538        this.on("beforeexpand", this.restrictExpand, this);
16539    }
16540 };
16541 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16542     rootVisible : true,
16543     animate: Roo.enableFx,
16544     lines : true,
16545     enableDD : false,
16546     hlDrop : Roo.enableFx,
16547   
16548     renderer: false,
16549     
16550     rendererTip: false,
16551     // private
16552     restrictExpand : function(node){
16553         var p = node.parentNode;
16554         if(p){
16555             if(p.expandedChild && p.expandedChild.parentNode == p){
16556                 p.expandedChild.collapse();
16557             }
16558             p.expandedChild = node;
16559         }
16560     },
16561
16562     // private override
16563     setRootNode : function(node){
16564         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16565         if(!this.rootVisible){
16566             node.ui = new Roo.tree.RootTreeNodeUI(node);
16567         }
16568         return node;
16569     },
16570
16571     /**
16572      * Returns the container element for this TreePanel
16573      */
16574     getEl : function(){
16575         return this.el;
16576     },
16577
16578     /**
16579      * Returns the default TreeLoader for this TreePanel
16580      */
16581     getLoader : function(){
16582         return this.loader;
16583     },
16584
16585     /**
16586      * Expand all nodes
16587      */
16588     expandAll : function(){
16589         this.root.expand(true);
16590     },
16591
16592     /**
16593      * Collapse all nodes
16594      */
16595     collapseAll : function(){
16596         this.root.collapse(true);
16597     },
16598
16599     /**
16600      * Returns the selection model used by this TreePanel
16601      */
16602     getSelectionModel : function(){
16603         if(!this.selModel){
16604             this.selModel = new Roo.tree.DefaultSelectionModel();
16605         }
16606         return this.selModel;
16607     },
16608
16609     /**
16610      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16611      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16612      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16613      * @return {Array}
16614      */
16615     getChecked : function(a, startNode){
16616         startNode = startNode || this.root;
16617         var r = [];
16618         var f = function(){
16619             if(this.attributes.checked){
16620                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16621             }
16622         }
16623         startNode.cascade(f);
16624         return r;
16625     },
16626
16627     /**
16628      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16629      * @param {String} path
16630      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16631      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16632      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16633      */
16634     expandPath : function(path, attr, callback){
16635         attr = attr || "id";
16636         var keys = path.split(this.pathSeparator);
16637         var curNode = this.root;
16638         if(curNode.attributes[attr] != keys[1]){ // invalid root
16639             if(callback){
16640                 callback(false, null);
16641             }
16642             return;
16643         }
16644         var index = 1;
16645         var f = function(){
16646             if(++index == keys.length){
16647                 if(callback){
16648                     callback(true, curNode);
16649                 }
16650                 return;
16651             }
16652             var c = curNode.findChild(attr, keys[index]);
16653             if(!c){
16654                 if(callback){
16655                     callback(false, curNode);
16656                 }
16657                 return;
16658             }
16659             curNode = c;
16660             c.expand(false, false, f);
16661         };
16662         curNode.expand(false, false, f);
16663     },
16664
16665     /**
16666      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16667      * @param {String} path
16668      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16669      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16670      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16671      */
16672     selectPath : function(path, attr, callback){
16673         attr = attr || "id";
16674         var keys = path.split(this.pathSeparator);
16675         var v = keys.pop();
16676         if(keys.length > 0){
16677             var f = function(success, node){
16678                 if(success && node){
16679                     var n = node.findChild(attr, v);
16680                     if(n){
16681                         n.select();
16682                         if(callback){
16683                             callback(true, n);
16684                         }
16685                     }else if(callback){
16686                         callback(false, n);
16687                     }
16688                 }else{
16689                     if(callback){
16690                         callback(false, n);
16691                     }
16692                 }
16693             };
16694             this.expandPath(keys.join(this.pathSeparator), attr, f);
16695         }else{
16696             this.root.select();
16697             if(callback){
16698                 callback(true, this.root);
16699             }
16700         }
16701     },
16702
16703     getTreeEl : function(){
16704         return this.el;
16705     },
16706
16707     /**
16708      * Trigger rendering of this TreePanel
16709      */
16710     render : function(){
16711         if (this.innerCt) {
16712             return this; // stop it rendering more than once!!
16713         }
16714         
16715         this.innerCt = this.el.createChild({tag:"ul",
16716                cls:"x-tree-root-ct " +
16717                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16718
16719         if(this.containerScroll){
16720             Roo.dd.ScrollManager.register(this.el);
16721         }
16722         if((this.enableDD || this.enableDrop) && !this.dropZone){
16723            /**
16724             * The dropZone used by this tree if drop is enabled
16725             * @type Roo.tree.TreeDropZone
16726             */
16727              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16728                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16729            });
16730         }
16731         if((this.enableDD || this.enableDrag) && !this.dragZone){
16732            /**
16733             * The dragZone used by this tree if drag is enabled
16734             * @type Roo.tree.TreeDragZone
16735             */
16736             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16737                ddGroup: this.ddGroup || "TreeDD",
16738                scroll: this.ddScroll
16739            });
16740         }
16741         this.getSelectionModel().init(this);
16742         if (!this.root) {
16743             console.log("ROOT not set in tree");
16744             return;
16745         }
16746         this.root.render();
16747         if(!this.rootVisible){
16748             this.root.renderChildren();
16749         }
16750         return this;
16751     }
16752 });/*
16753  * Based on:
16754  * Ext JS Library 1.1.1
16755  * Copyright(c) 2006-2007, Ext JS, LLC.
16756  *
16757  * Originally Released Under LGPL - original licence link has changed is not relivant.
16758  *
16759  * Fork - LGPL
16760  * <script type="text/javascript">
16761  */
16762  
16763
16764 /**
16765  * @class Roo.tree.DefaultSelectionModel
16766  * @extends Roo.util.Observable
16767  * The default single selection for a TreePanel.
16768  */
16769 Roo.tree.DefaultSelectionModel = function(){
16770    this.selNode = null;
16771    
16772    this.addEvents({
16773        /**
16774         * @event selectionchange
16775         * Fires when the selected node changes
16776         * @param {DefaultSelectionModel} this
16777         * @param {TreeNode} node the new selection
16778         */
16779        "selectionchange" : true,
16780
16781        /**
16782         * @event beforeselect
16783         * Fires before the selected node changes, return false to cancel the change
16784         * @param {DefaultSelectionModel} this
16785         * @param {TreeNode} node the new selection
16786         * @param {TreeNode} node the old selection
16787         */
16788        "beforeselect" : true
16789    });
16790 };
16791
16792 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16793     init : function(tree){
16794         this.tree = tree;
16795         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16796         tree.on("click", this.onNodeClick, this);
16797     },
16798     
16799     onNodeClick : function(node, e){
16800         if (e.ctrlKey && this.selNode == node)  {
16801             this.unselect(node);
16802             return;
16803         }
16804         this.select(node);
16805     },
16806     
16807     /**
16808      * Select a node.
16809      * @param {TreeNode} node The node to select
16810      * @return {TreeNode} The selected node
16811      */
16812     select : function(node){
16813         var last = this.selNode;
16814         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16815             if(last){
16816                 last.ui.onSelectedChange(false);
16817             }
16818             this.selNode = node;
16819             node.ui.onSelectedChange(true);
16820             this.fireEvent("selectionchange", this, node, last);
16821         }
16822         return node;
16823     },
16824     
16825     /**
16826      * Deselect a node.
16827      * @param {TreeNode} node The node to unselect
16828      */
16829     unselect : function(node){
16830         if(this.selNode == node){
16831             this.clearSelections();
16832         }    
16833     },
16834     
16835     /**
16836      * Clear all selections
16837      */
16838     clearSelections : function(){
16839         var n = this.selNode;
16840         if(n){
16841             n.ui.onSelectedChange(false);
16842             this.selNode = null;
16843             this.fireEvent("selectionchange", this, null);
16844         }
16845         return n;
16846     },
16847     
16848     /**
16849      * Get the selected node
16850      * @return {TreeNode} The selected node
16851      */
16852     getSelectedNode : function(){
16853         return this.selNode;    
16854     },
16855     
16856     /**
16857      * Returns true if the node is selected
16858      * @param {TreeNode} node The node to check
16859      * @return {Boolean}
16860      */
16861     isSelected : function(node){
16862         return this.selNode == node;  
16863     },
16864
16865     /**
16866      * Selects the node above the selected node in the tree, intelligently walking the nodes
16867      * @return TreeNode The new selection
16868      */
16869     selectPrevious : function(){
16870         var s = this.selNode || this.lastSelNode;
16871         if(!s){
16872             return null;
16873         }
16874         var ps = s.previousSibling;
16875         if(ps){
16876             if(!ps.isExpanded() || ps.childNodes.length < 1){
16877                 return this.select(ps);
16878             } else{
16879                 var lc = ps.lastChild;
16880                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16881                     lc = lc.lastChild;
16882                 }
16883                 return this.select(lc);
16884             }
16885         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16886             return this.select(s.parentNode);
16887         }
16888         return null;
16889     },
16890
16891     /**
16892      * Selects the node above the selected node in the tree, intelligently walking the nodes
16893      * @return TreeNode The new selection
16894      */
16895     selectNext : function(){
16896         var s = this.selNode || this.lastSelNode;
16897         if(!s){
16898             return null;
16899         }
16900         if(s.firstChild && s.isExpanded()){
16901              return this.select(s.firstChild);
16902          }else if(s.nextSibling){
16903              return this.select(s.nextSibling);
16904          }else if(s.parentNode){
16905             var newS = null;
16906             s.parentNode.bubble(function(){
16907                 if(this.nextSibling){
16908                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16909                     return false;
16910                 }
16911             });
16912             return newS;
16913          }
16914         return null;
16915     },
16916
16917     onKeyDown : function(e){
16918         var s = this.selNode || this.lastSelNode;
16919         // undesirable, but required
16920         var sm = this;
16921         if(!s){
16922             return;
16923         }
16924         var k = e.getKey();
16925         switch(k){
16926              case e.DOWN:
16927                  e.stopEvent();
16928                  this.selectNext();
16929              break;
16930              case e.UP:
16931                  e.stopEvent();
16932                  this.selectPrevious();
16933              break;
16934              case e.RIGHT:
16935                  e.preventDefault();
16936                  if(s.hasChildNodes()){
16937                      if(!s.isExpanded()){
16938                          s.expand();
16939                      }else if(s.firstChild){
16940                          this.select(s.firstChild, e);
16941                      }
16942                  }
16943              break;
16944              case e.LEFT:
16945                  e.preventDefault();
16946                  if(s.hasChildNodes() && s.isExpanded()){
16947                      s.collapse();
16948                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16949                      this.select(s.parentNode, e);
16950                  }
16951              break;
16952         };
16953     }
16954 });
16955
16956 /**
16957  * @class Roo.tree.MultiSelectionModel
16958  * @extends Roo.util.Observable
16959  * Multi selection for a TreePanel.
16960  */
16961 Roo.tree.MultiSelectionModel = function(){
16962    this.selNodes = [];
16963    this.selMap = {};
16964    this.addEvents({
16965        /**
16966         * @event selectionchange
16967         * Fires when the selected nodes change
16968         * @param {MultiSelectionModel} this
16969         * @param {Array} nodes Array of the selected nodes
16970         */
16971        "selectionchange" : true
16972    });
16973 };
16974
16975 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16976     init : function(tree){
16977         this.tree = tree;
16978         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16979         tree.on("click", this.onNodeClick, this);
16980     },
16981     
16982     onNodeClick : function(node, e){
16983         this.select(node, e, e.ctrlKey);
16984     },
16985     
16986     /**
16987      * Select a node.
16988      * @param {TreeNode} node The node to select
16989      * @param {EventObject} e (optional) An event associated with the selection
16990      * @param {Boolean} keepExisting True to retain existing selections
16991      * @return {TreeNode} The selected node
16992      */
16993     select : function(node, e, keepExisting){
16994         if(keepExisting !== true){
16995             this.clearSelections(true);
16996         }
16997         if(this.isSelected(node)){
16998             this.lastSelNode = node;
16999             return node;
17000         }
17001         this.selNodes.push(node);
17002         this.selMap[node.id] = node;
17003         this.lastSelNode = node;
17004         node.ui.onSelectedChange(true);
17005         this.fireEvent("selectionchange", this, this.selNodes);
17006         return node;
17007     },
17008     
17009     /**
17010      * Deselect a node.
17011      * @param {TreeNode} node The node to unselect
17012      */
17013     unselect : function(node){
17014         if(this.selMap[node.id]){
17015             node.ui.onSelectedChange(false);
17016             var sn = this.selNodes;
17017             var index = -1;
17018             if(sn.indexOf){
17019                 index = sn.indexOf(node);
17020             }else{
17021                 for(var i = 0, len = sn.length; i < len; i++){
17022                     if(sn[i] == node){
17023                         index = i;
17024                         break;
17025                     }
17026                 }
17027             }
17028             if(index != -1){
17029                 this.selNodes.splice(index, 1);
17030             }
17031             delete this.selMap[node.id];
17032             this.fireEvent("selectionchange", this, this.selNodes);
17033         }
17034     },
17035     
17036     /**
17037      * Clear all selections
17038      */
17039     clearSelections : function(suppressEvent){
17040         var sn = this.selNodes;
17041         if(sn.length > 0){
17042             for(var i = 0, len = sn.length; i < len; i++){
17043                 sn[i].ui.onSelectedChange(false);
17044             }
17045             this.selNodes = [];
17046             this.selMap = {};
17047             if(suppressEvent !== true){
17048                 this.fireEvent("selectionchange", this, this.selNodes);
17049             }
17050         }
17051     },
17052     
17053     /**
17054      * Returns true if the node is selected
17055      * @param {TreeNode} node The node to check
17056      * @return {Boolean}
17057      */
17058     isSelected : function(node){
17059         return this.selMap[node.id] ? true : false;  
17060     },
17061     
17062     /**
17063      * Returns an array of the selected nodes
17064      * @return {Array}
17065      */
17066     getSelectedNodes : function(){
17067         return this.selNodes;    
17068     },
17069
17070     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17071
17072     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17073
17074     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17075 });/*
17076  * Based on:
17077  * Ext JS Library 1.1.1
17078  * Copyright(c) 2006-2007, Ext JS, LLC.
17079  *
17080  * Originally Released Under LGPL - original licence link has changed is not relivant.
17081  *
17082  * Fork - LGPL
17083  * <script type="text/javascript">
17084  */
17085  
17086 /**
17087  * @class Roo.tree.TreeNode
17088  * @extends Roo.data.Node
17089  * @cfg {String} text The text for this node
17090  * @cfg {Boolean} expanded true to start the node expanded
17091  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17092  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17093  * @cfg {Boolean} disabled true to start the node disabled
17094  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17095  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17096  * @cfg {String} cls A css class to be added to the node
17097  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17098  * @cfg {String} href URL of the link used for the node (defaults to #)
17099  * @cfg {String} hrefTarget target frame for the link
17100  * @cfg {String} qtip An Ext QuickTip for the node
17101  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17102  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17103  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17104  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17105  * (defaults to undefined with no checkbox rendered)
17106  * @constructor
17107  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17108  */
17109 Roo.tree.TreeNode = function(attributes){
17110     attributes = attributes || {};
17111     if(typeof attributes == "string"){
17112         attributes = {text: attributes};
17113     }
17114     this.childrenRendered = false;
17115     this.rendered = false;
17116     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17117     this.expanded = attributes.expanded === true;
17118     this.isTarget = attributes.isTarget !== false;
17119     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17120     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17121
17122     /**
17123      * Read-only. The text for this node. To change it use setText().
17124      * @type String
17125      */
17126     this.text = attributes.text;
17127     /**
17128      * True if this node is disabled.
17129      * @type Boolean
17130      */
17131     this.disabled = attributes.disabled === true;
17132
17133     this.addEvents({
17134         /**
17135         * @event textchange
17136         * Fires when the text for this node is changed
17137         * @param {Node} this This node
17138         * @param {String} text The new text
17139         * @param {String} oldText The old text
17140         */
17141         "textchange" : true,
17142         /**
17143         * @event beforeexpand
17144         * Fires before this node is expanded, return false to cancel.
17145         * @param {Node} this This node
17146         * @param {Boolean} deep
17147         * @param {Boolean} anim
17148         */
17149         "beforeexpand" : true,
17150         /**
17151         * @event beforecollapse
17152         * Fires before this node is collapsed, return false to cancel.
17153         * @param {Node} this This node
17154         * @param {Boolean} deep
17155         * @param {Boolean} anim
17156         */
17157         "beforecollapse" : true,
17158         /**
17159         * @event expand
17160         * Fires when this node is expanded
17161         * @param {Node} this This node
17162         */
17163         "expand" : true,
17164         /**
17165         * @event disabledchange
17166         * Fires when the disabled status of this node changes
17167         * @param {Node} this This node
17168         * @param {Boolean} disabled
17169         */
17170         "disabledchange" : true,
17171         /**
17172         * @event collapse
17173         * Fires when this node is collapsed
17174         * @param {Node} this This node
17175         */
17176         "collapse" : true,
17177         /**
17178         * @event beforeclick
17179         * Fires before click processing. Return false to cancel the default action.
17180         * @param {Node} this This node
17181         * @param {Roo.EventObject} e The event object
17182         */
17183         "beforeclick":true,
17184         /**
17185         * @event checkchange
17186         * Fires when a node with a checkbox's checked property changes
17187         * @param {Node} this This node
17188         * @param {Boolean} checked
17189         */
17190         "checkchange":true,
17191         /**
17192         * @event click
17193         * Fires when this node is clicked
17194         * @param {Node} this This node
17195         * @param {Roo.EventObject} e The event object
17196         */
17197         "click":true,
17198         /**
17199         * @event dblclick
17200         * Fires when this node is double clicked
17201         * @param {Node} this This node
17202         * @param {Roo.EventObject} e The event object
17203         */
17204         "dblclick":true,
17205         /**
17206         * @event contextmenu
17207         * Fires when this node is right clicked
17208         * @param {Node} this This node
17209         * @param {Roo.EventObject} e The event object
17210         */
17211         "contextmenu":true,
17212         /**
17213         * @event beforechildrenrendered
17214         * Fires right before the child nodes for this node are rendered
17215         * @param {Node} this This node
17216         */
17217         "beforechildrenrendered":true
17218     });
17219
17220     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17221
17222     /**
17223      * Read-only. The UI for this node
17224      * @type TreeNodeUI
17225      */
17226     this.ui = new uiClass(this);
17227 };
17228 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17229     preventHScroll: true,
17230     /**
17231      * Returns true if this node is expanded
17232      * @return {Boolean}
17233      */
17234     isExpanded : function(){
17235         return this.expanded;
17236     },
17237
17238     /**
17239      * Returns the UI object for this node
17240      * @return {TreeNodeUI}
17241      */
17242     getUI : function(){
17243         return this.ui;
17244     },
17245
17246     // private override
17247     setFirstChild : function(node){
17248         var of = this.firstChild;
17249         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17250         if(this.childrenRendered && of && node != of){
17251             of.renderIndent(true, true);
17252         }
17253         if(this.rendered){
17254             this.renderIndent(true, true);
17255         }
17256     },
17257
17258     // private override
17259     setLastChild : function(node){
17260         var ol = this.lastChild;
17261         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17262         if(this.childrenRendered && ol && node != ol){
17263             ol.renderIndent(true, true);
17264         }
17265         if(this.rendered){
17266             this.renderIndent(true, true);
17267         }
17268     },
17269
17270     // these methods are overridden to provide lazy rendering support
17271     // private override
17272     appendChild : function(){
17273         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17274         if(node && this.childrenRendered){
17275             node.render();
17276         }
17277         this.ui.updateExpandIcon();
17278         return node;
17279     },
17280
17281     // private override
17282     removeChild : function(node){
17283         this.ownerTree.getSelectionModel().unselect(node);
17284         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17285         // if it's been rendered remove dom node
17286         if(this.childrenRendered){
17287             node.ui.remove();
17288         }
17289         if(this.childNodes.length < 1){
17290             this.collapse(false, false);
17291         }else{
17292             this.ui.updateExpandIcon();
17293         }
17294         if(!this.firstChild) {
17295             this.childrenRendered = false;
17296         }
17297         return node;
17298     },
17299
17300     // private override
17301     insertBefore : function(node, refNode){
17302         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17303         if(newNode && refNode && this.childrenRendered){
17304             node.render();
17305         }
17306         this.ui.updateExpandIcon();
17307         return newNode;
17308     },
17309
17310     /**
17311      * Sets the text for this node
17312      * @param {String} text
17313      */
17314     setText : function(text){
17315         var oldText = this.text;
17316         this.text = text;
17317         this.attributes.text = text;
17318         if(this.rendered){ // event without subscribing
17319             this.ui.onTextChange(this, text, oldText);
17320         }
17321         this.fireEvent("textchange", this, text, oldText);
17322     },
17323
17324     /**
17325      * Triggers selection of this node
17326      */
17327     select : function(){
17328         this.getOwnerTree().getSelectionModel().select(this);
17329     },
17330
17331     /**
17332      * Triggers deselection of this node
17333      */
17334     unselect : function(){
17335         this.getOwnerTree().getSelectionModel().unselect(this);
17336     },
17337
17338     /**
17339      * Returns true if this node is selected
17340      * @return {Boolean}
17341      */
17342     isSelected : function(){
17343         return this.getOwnerTree().getSelectionModel().isSelected(this);
17344     },
17345
17346     /**
17347      * Expand this node.
17348      * @param {Boolean} deep (optional) True to expand all children as well
17349      * @param {Boolean} anim (optional) false to cancel the default animation
17350      * @param {Function} callback (optional) A callback to be called when
17351      * expanding this node completes (does not wait for deep expand to complete).
17352      * Called with 1 parameter, this node.
17353      */
17354     expand : function(deep, anim, callback){
17355         if(!this.expanded){
17356             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17357                 return;
17358             }
17359             if(!this.childrenRendered){
17360                 this.renderChildren();
17361             }
17362             this.expanded = true;
17363             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17364                 this.ui.animExpand(function(){
17365                     this.fireEvent("expand", this);
17366                     if(typeof callback == "function"){
17367                         callback(this);
17368                     }
17369                     if(deep === true){
17370                         this.expandChildNodes(true);
17371                     }
17372                 }.createDelegate(this));
17373                 return;
17374             }else{
17375                 this.ui.expand();
17376                 this.fireEvent("expand", this);
17377                 if(typeof callback == "function"){
17378                     callback(this);
17379                 }
17380             }
17381         }else{
17382            if(typeof callback == "function"){
17383                callback(this);
17384            }
17385         }
17386         if(deep === true){
17387             this.expandChildNodes(true);
17388         }
17389     },
17390
17391     isHiddenRoot : function(){
17392         return this.isRoot && !this.getOwnerTree().rootVisible;
17393     },
17394
17395     /**
17396      * Collapse this node.
17397      * @param {Boolean} deep (optional) True to collapse all children as well
17398      * @param {Boolean} anim (optional) false to cancel the default animation
17399      */
17400     collapse : function(deep, anim){
17401         if(this.expanded && !this.isHiddenRoot()){
17402             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17403                 return;
17404             }
17405             this.expanded = false;
17406             if((this.getOwnerTree().animate && anim !== false) || anim){
17407                 this.ui.animCollapse(function(){
17408                     this.fireEvent("collapse", this);
17409                     if(deep === true){
17410                         this.collapseChildNodes(true);
17411                     }
17412                 }.createDelegate(this));
17413                 return;
17414             }else{
17415                 this.ui.collapse();
17416                 this.fireEvent("collapse", this);
17417             }
17418         }
17419         if(deep === true){
17420             var cs = this.childNodes;
17421             for(var i = 0, len = cs.length; i < len; i++) {
17422                 cs[i].collapse(true, false);
17423             }
17424         }
17425     },
17426
17427     // private
17428     delayedExpand : function(delay){
17429         if(!this.expandProcId){
17430             this.expandProcId = this.expand.defer(delay, this);
17431         }
17432     },
17433
17434     // private
17435     cancelExpand : function(){
17436         if(this.expandProcId){
17437             clearTimeout(this.expandProcId);
17438         }
17439         this.expandProcId = false;
17440     },
17441
17442     /**
17443      * Toggles expanded/collapsed state of the node
17444      */
17445     toggle : function(){
17446         if(this.expanded){
17447             this.collapse();
17448         }else{
17449             this.expand();
17450         }
17451     },
17452
17453     /**
17454      * Ensures all parent nodes are expanded
17455      */
17456     ensureVisible : function(callback){
17457         var tree = this.getOwnerTree();
17458         tree.expandPath(this.parentNode.getPath(), false, function(){
17459             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17460             Roo.callback(callback);
17461         }.createDelegate(this));
17462     },
17463
17464     /**
17465      * Expand all child nodes
17466      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17467      */
17468     expandChildNodes : function(deep){
17469         var cs = this.childNodes;
17470         for(var i = 0, len = cs.length; i < len; i++) {
17471                 cs[i].expand(deep);
17472         }
17473     },
17474
17475     /**
17476      * Collapse all child nodes
17477      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17478      */
17479     collapseChildNodes : function(deep){
17480         var cs = this.childNodes;
17481         for(var i = 0, len = cs.length; i < len; i++) {
17482                 cs[i].collapse(deep);
17483         }
17484     },
17485
17486     /**
17487      * Disables this node
17488      */
17489     disable : function(){
17490         this.disabled = true;
17491         this.unselect();
17492         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17493             this.ui.onDisableChange(this, true);
17494         }
17495         this.fireEvent("disabledchange", this, true);
17496     },
17497
17498     /**
17499      * Enables this node
17500      */
17501     enable : function(){
17502         this.disabled = false;
17503         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17504             this.ui.onDisableChange(this, false);
17505         }
17506         this.fireEvent("disabledchange", this, false);
17507     },
17508
17509     // private
17510     renderChildren : function(suppressEvent){
17511         if(suppressEvent !== false){
17512             this.fireEvent("beforechildrenrendered", this);
17513         }
17514         var cs = this.childNodes;
17515         for(var i = 0, len = cs.length; i < len; i++){
17516             cs[i].render(true);
17517         }
17518         this.childrenRendered = true;
17519     },
17520
17521     // private
17522     sort : function(fn, scope){
17523         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17524         if(this.childrenRendered){
17525             var cs = this.childNodes;
17526             for(var i = 0, len = cs.length; i < len; i++){
17527                 cs[i].render(true);
17528             }
17529         }
17530     },
17531
17532     // private
17533     render : function(bulkRender){
17534         this.ui.render(bulkRender);
17535         if(!this.rendered){
17536             this.rendered = true;
17537             if(this.expanded){
17538                 this.expanded = false;
17539                 this.expand(false, false);
17540             }
17541         }
17542     },
17543
17544     // private
17545     renderIndent : function(deep, refresh){
17546         if(refresh){
17547             this.ui.childIndent = null;
17548         }
17549         this.ui.renderIndent();
17550         if(deep === true && this.childrenRendered){
17551             var cs = this.childNodes;
17552             for(var i = 0, len = cs.length; i < len; i++){
17553                 cs[i].renderIndent(true, refresh);
17554             }
17555         }
17556     }
17557 });/*
17558  * Based on:
17559  * Ext JS Library 1.1.1
17560  * Copyright(c) 2006-2007, Ext JS, LLC.
17561  *
17562  * Originally Released Under LGPL - original licence link has changed is not relivant.
17563  *
17564  * Fork - LGPL
17565  * <script type="text/javascript">
17566  */
17567  
17568 /**
17569  * @class Roo.tree.AsyncTreeNode
17570  * @extends Roo.tree.TreeNode
17571  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17572  * @constructor
17573  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17574  */
17575  Roo.tree.AsyncTreeNode = function(config){
17576     this.loaded = false;
17577     this.loading = false;
17578     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17579     /**
17580     * @event beforeload
17581     * Fires before this node is loaded, return false to cancel
17582     * @param {Node} this This node
17583     */
17584     this.addEvents({'beforeload':true, 'load': true});
17585     /**
17586     * @event load
17587     * Fires when this node is loaded
17588     * @param {Node} this This node
17589     */
17590     /**
17591      * The loader used by this node (defaults to using the tree's defined loader)
17592      * @type TreeLoader
17593      * @property loader
17594      */
17595 };
17596 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17597     expand : function(deep, anim, callback){
17598         if(this.loading){ // if an async load is already running, waiting til it's done
17599             var timer;
17600             var f = function(){
17601                 if(!this.loading){ // done loading
17602                     clearInterval(timer);
17603                     this.expand(deep, anim, callback);
17604                 }
17605             }.createDelegate(this);
17606             timer = setInterval(f, 200);
17607             return;
17608         }
17609         if(!this.loaded){
17610             if(this.fireEvent("beforeload", this) === false){
17611                 return;
17612             }
17613             this.loading = true;
17614             this.ui.beforeLoad(this);
17615             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17616             if(loader){
17617                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17618                 return;
17619             }
17620         }
17621         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17622     },
17623     
17624     /**
17625      * Returns true if this node is currently loading
17626      * @return {Boolean}
17627      */
17628     isLoading : function(){
17629         return this.loading;  
17630     },
17631     
17632     loadComplete : function(deep, anim, callback){
17633         this.loading = false;
17634         this.loaded = true;
17635         this.ui.afterLoad(this);
17636         this.fireEvent("load", this);
17637         this.expand(deep, anim, callback);
17638     },
17639     
17640     /**
17641      * Returns true if this node has been loaded
17642      * @return {Boolean}
17643      */
17644     isLoaded : function(){
17645         return this.loaded;
17646     },
17647     
17648     hasChildNodes : function(){
17649         if(!this.isLeaf() && !this.loaded){
17650             return true;
17651         }else{
17652             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17653         }
17654     },
17655
17656     /**
17657      * Trigger a reload for this node
17658      * @param {Function} callback
17659      */
17660     reload : function(callback){
17661         this.collapse(false, false);
17662         while(this.firstChild){
17663             this.removeChild(this.firstChild);
17664         }
17665         this.childrenRendered = false;
17666         this.loaded = false;
17667         if(this.isHiddenRoot()){
17668             this.expanded = false;
17669         }
17670         this.expand(false, false, callback);
17671     }
17672 });/*
17673  * Based on:
17674  * Ext JS Library 1.1.1
17675  * Copyright(c) 2006-2007, Ext JS, LLC.
17676  *
17677  * Originally Released Under LGPL - original licence link has changed is not relivant.
17678  *
17679  * Fork - LGPL
17680  * <script type="text/javascript">
17681  */
17682  
17683 /**
17684  * @class Roo.tree.TreeNodeUI
17685  * @constructor
17686  * @param {Object} node The node to render
17687  * The TreeNode UI implementation is separate from the
17688  * tree implementation. Unless you are customizing the tree UI,
17689  * you should never have to use this directly.
17690  */
17691 Roo.tree.TreeNodeUI = function(node){
17692     this.node = node;
17693     this.rendered = false;
17694     this.animating = false;
17695     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17696 };
17697
17698 Roo.tree.TreeNodeUI.prototype = {
17699     removeChild : function(node){
17700         if(this.rendered){
17701             this.ctNode.removeChild(node.ui.getEl());
17702         }
17703     },
17704
17705     beforeLoad : function(){
17706          this.addClass("x-tree-node-loading");
17707     },
17708
17709     afterLoad : function(){
17710          this.removeClass("x-tree-node-loading");
17711     },
17712
17713     onTextChange : function(node, text, oldText){
17714         if(this.rendered){
17715             this.textNode.innerHTML = text;
17716         }
17717     },
17718
17719     onDisableChange : function(node, state){
17720         this.disabled = state;
17721         if(state){
17722             this.addClass("x-tree-node-disabled");
17723         }else{
17724             this.removeClass("x-tree-node-disabled");
17725         }
17726     },
17727
17728     onSelectedChange : function(state){
17729         if(state){
17730             this.focus();
17731             this.addClass("x-tree-selected");
17732         }else{
17733             //this.blur();
17734             this.removeClass("x-tree-selected");
17735         }
17736     },
17737
17738     onMove : function(tree, node, oldParent, newParent, index, refNode){
17739         this.childIndent = null;
17740         if(this.rendered){
17741             var targetNode = newParent.ui.getContainer();
17742             if(!targetNode){//target not rendered
17743                 this.holder = document.createElement("div");
17744                 this.holder.appendChild(this.wrap);
17745                 return;
17746             }
17747             var insertBefore = refNode ? refNode.ui.getEl() : null;
17748             if(insertBefore){
17749                 targetNode.insertBefore(this.wrap, insertBefore);
17750             }else{
17751                 targetNode.appendChild(this.wrap);
17752             }
17753             this.node.renderIndent(true);
17754         }
17755     },
17756
17757     addClass : function(cls){
17758         if(this.elNode){
17759             Roo.fly(this.elNode).addClass(cls);
17760         }
17761     },
17762
17763     removeClass : function(cls){
17764         if(this.elNode){
17765             Roo.fly(this.elNode).removeClass(cls);
17766         }
17767     },
17768
17769     remove : function(){
17770         if(this.rendered){
17771             this.holder = document.createElement("div");
17772             this.holder.appendChild(this.wrap);
17773         }
17774     },
17775
17776     fireEvent : function(){
17777         return this.node.fireEvent.apply(this.node, arguments);
17778     },
17779
17780     initEvents : function(){
17781         this.node.on("move", this.onMove, this);
17782         var E = Roo.EventManager;
17783         var a = this.anchor;
17784
17785         var el = Roo.fly(a, '_treeui');
17786
17787         if(Roo.isOpera){ // opera render bug ignores the CSS
17788             el.setStyle("text-decoration", "none");
17789         }
17790
17791         el.on("click", this.onClick, this);
17792         el.on("dblclick", this.onDblClick, this);
17793
17794         if(this.checkbox){
17795             Roo.EventManager.on(this.checkbox,
17796                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17797         }
17798
17799         el.on("contextmenu", this.onContextMenu, this);
17800
17801         var icon = Roo.fly(this.iconNode);
17802         icon.on("click", this.onClick, this);
17803         icon.on("dblclick", this.onDblClick, this);
17804         icon.on("contextmenu", this.onContextMenu, this);
17805         E.on(this.ecNode, "click", this.ecClick, this, true);
17806
17807         if(this.node.disabled){
17808             this.addClass("x-tree-node-disabled");
17809         }
17810         if(this.node.hidden){
17811             this.addClass("x-tree-node-disabled");
17812         }
17813         var ot = this.node.getOwnerTree();
17814         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17815         if(dd && (!this.node.isRoot || ot.rootVisible)){
17816             Roo.dd.Registry.register(this.elNode, {
17817                 node: this.node,
17818                 handles: this.getDDHandles(),
17819                 isHandle: false
17820             });
17821         }
17822     },
17823
17824     getDDHandles : function(){
17825         return [this.iconNode, this.textNode];
17826     },
17827
17828     hide : function(){
17829         if(this.rendered){
17830             this.wrap.style.display = "none";
17831         }
17832     },
17833
17834     show : function(){
17835         if(this.rendered){
17836             this.wrap.style.display = "";
17837         }
17838     },
17839
17840     onContextMenu : function(e){
17841         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17842             e.preventDefault();
17843             this.focus();
17844             this.fireEvent("contextmenu", this.node, e);
17845         }
17846     },
17847
17848     onClick : function(e){
17849         if(this.dropping){
17850             e.stopEvent();
17851             return;
17852         }
17853         if(this.fireEvent("beforeclick", this.node, e) !== false){
17854             if(!this.disabled && this.node.attributes.href){
17855                 this.fireEvent("click", this.node, e);
17856                 return;
17857             }
17858             e.preventDefault();
17859             if(this.disabled){
17860                 return;
17861             }
17862
17863             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17864                 this.node.toggle();
17865             }
17866
17867             this.fireEvent("click", this.node, e);
17868         }else{
17869             e.stopEvent();
17870         }
17871     },
17872
17873     onDblClick : function(e){
17874         e.preventDefault();
17875         if(this.disabled){
17876             return;
17877         }
17878         if(this.checkbox){
17879             this.toggleCheck();
17880         }
17881         if(!this.animating && this.node.hasChildNodes()){
17882             this.node.toggle();
17883         }
17884         this.fireEvent("dblclick", this.node, e);
17885     },
17886
17887     onCheckChange : function(){
17888         var checked = this.checkbox.checked;
17889         this.node.attributes.checked = checked;
17890         this.fireEvent('checkchange', this.node, checked);
17891     },
17892
17893     ecClick : function(e){
17894         if(!this.animating && this.node.hasChildNodes()){
17895             this.node.toggle();
17896         }
17897     },
17898
17899     startDrop : function(){
17900         this.dropping = true;
17901     },
17902
17903     // delayed drop so the click event doesn't get fired on a drop
17904     endDrop : function(){
17905        setTimeout(function(){
17906            this.dropping = false;
17907        }.createDelegate(this), 50);
17908     },
17909
17910     expand : function(){
17911         this.updateExpandIcon();
17912         this.ctNode.style.display = "";
17913     },
17914
17915     focus : function(){
17916         if(!this.node.preventHScroll){
17917             try{this.anchor.focus();
17918             }catch(e){}
17919         }else if(!Roo.isIE){
17920             try{
17921                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17922                 var l = noscroll.scrollLeft;
17923                 this.anchor.focus();
17924                 noscroll.scrollLeft = l;
17925             }catch(e){}
17926         }
17927     },
17928
17929     toggleCheck : function(value){
17930         var cb = this.checkbox;
17931         if(cb){
17932             cb.checked = (value === undefined ? !cb.checked : value);
17933         }
17934     },
17935
17936     blur : function(){
17937         try{
17938             this.anchor.blur();
17939         }catch(e){}
17940     },
17941
17942     animExpand : function(callback){
17943         var ct = Roo.get(this.ctNode);
17944         ct.stopFx();
17945         if(!this.node.hasChildNodes()){
17946             this.updateExpandIcon();
17947             this.ctNode.style.display = "";
17948             Roo.callback(callback);
17949             return;
17950         }
17951         this.animating = true;
17952         this.updateExpandIcon();
17953
17954         ct.slideIn('t', {
17955            callback : function(){
17956                this.animating = false;
17957                Roo.callback(callback);
17958             },
17959             scope: this,
17960             duration: this.node.ownerTree.duration || .25
17961         });
17962     },
17963
17964     highlight : function(){
17965         var tree = this.node.getOwnerTree();
17966         Roo.fly(this.wrap).highlight(
17967             tree.hlColor || "C3DAF9",
17968             {endColor: tree.hlBaseColor}
17969         );
17970     },
17971
17972     collapse : function(){
17973         this.updateExpandIcon();
17974         this.ctNode.style.display = "none";
17975     },
17976
17977     animCollapse : function(callback){
17978         var ct = Roo.get(this.ctNode);
17979         ct.enableDisplayMode('block');
17980         ct.stopFx();
17981
17982         this.animating = true;
17983         this.updateExpandIcon();
17984
17985         ct.slideOut('t', {
17986             callback : function(){
17987                this.animating = false;
17988                Roo.callback(callback);
17989             },
17990             scope: this,
17991             duration: this.node.ownerTree.duration || .25
17992         });
17993     },
17994
17995     getContainer : function(){
17996         return this.ctNode;
17997     },
17998
17999     getEl : function(){
18000         return this.wrap;
18001     },
18002
18003     appendDDGhost : function(ghostNode){
18004         ghostNode.appendChild(this.elNode.cloneNode(true));
18005     },
18006
18007     getDDRepairXY : function(){
18008         return Roo.lib.Dom.getXY(this.iconNode);
18009     },
18010
18011     onRender : function(){
18012         this.render();
18013     },
18014
18015     render : function(bulkRender){
18016         var n = this.node, a = n.attributes;
18017         var targetNode = n.parentNode ?
18018               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18019
18020         if(!this.rendered){
18021             this.rendered = true;
18022
18023             this.renderElements(n, a, targetNode, bulkRender);
18024
18025             if(a.qtip){
18026                if(this.textNode.setAttributeNS){
18027                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18028                    if(a.qtipTitle){
18029                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18030                    }
18031                }else{
18032                    this.textNode.setAttribute("ext:qtip", a.qtip);
18033                    if(a.qtipTitle){
18034                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18035                    }
18036                }
18037             }else if(a.qtipCfg){
18038                 a.qtipCfg.target = Roo.id(this.textNode);
18039                 Roo.QuickTips.register(a.qtipCfg);
18040             }
18041             this.initEvents();
18042             if(!this.node.expanded){
18043                 this.updateExpandIcon();
18044             }
18045         }else{
18046             if(bulkRender === true) {
18047                 targetNode.appendChild(this.wrap);
18048             }
18049         }
18050     },
18051
18052     renderElements : function(n, a, targetNode, bulkRender){
18053         // add some indent caching, this helps performance when rendering a large tree
18054         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18055         var t = n.getOwnerTree();
18056         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18057         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18058         var cb = typeof a.checked == 'boolean';
18059         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18060         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18061             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18062             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18063             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18064             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18065             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18066              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18067                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18068             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18069             "</li>"];
18070
18071         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18072             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18073                                 n.nextSibling.ui.getEl(), buf.join(""));
18074         }else{
18075             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18076         }
18077
18078         this.elNode = this.wrap.childNodes[0];
18079         this.ctNode = this.wrap.childNodes[1];
18080         var cs = this.elNode.childNodes;
18081         this.indentNode = cs[0];
18082         this.ecNode = cs[1];
18083         this.iconNode = cs[2];
18084         var index = 3;
18085         if(cb){
18086             this.checkbox = cs[3];
18087             index++;
18088         }
18089         this.anchor = cs[index];
18090         this.textNode = cs[index].firstChild;
18091     },
18092
18093     getAnchor : function(){
18094         return this.anchor;
18095     },
18096
18097     getTextEl : function(){
18098         return this.textNode;
18099     },
18100
18101     getIconEl : function(){
18102         return this.iconNode;
18103     },
18104
18105     isChecked : function(){
18106         return this.checkbox ? this.checkbox.checked : false;
18107     },
18108
18109     updateExpandIcon : function(){
18110         if(this.rendered){
18111             var n = this.node, c1, c2;
18112             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18113             var hasChild = n.hasChildNodes();
18114             if(hasChild){
18115                 if(n.expanded){
18116                     cls += "-minus";
18117                     c1 = "x-tree-node-collapsed";
18118                     c2 = "x-tree-node-expanded";
18119                 }else{
18120                     cls += "-plus";
18121                     c1 = "x-tree-node-expanded";
18122                     c2 = "x-tree-node-collapsed";
18123                 }
18124                 if(this.wasLeaf){
18125                     this.removeClass("x-tree-node-leaf");
18126                     this.wasLeaf = false;
18127                 }
18128                 if(this.c1 != c1 || this.c2 != c2){
18129                     Roo.fly(this.elNode).replaceClass(c1, c2);
18130                     this.c1 = c1; this.c2 = c2;
18131                 }
18132             }else{
18133                 if(!this.wasLeaf){
18134                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18135                     delete this.c1;
18136                     delete this.c2;
18137                     this.wasLeaf = true;
18138                 }
18139             }
18140             var ecc = "x-tree-ec-icon "+cls;
18141             if(this.ecc != ecc){
18142                 this.ecNode.className = ecc;
18143                 this.ecc = ecc;
18144             }
18145         }
18146     },
18147
18148     getChildIndent : function(){
18149         if(!this.childIndent){
18150             var buf = [];
18151             var p = this.node;
18152             while(p){
18153                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18154                     if(!p.isLast()) {
18155                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18156                     } else {
18157                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18158                     }
18159                 }
18160                 p = p.parentNode;
18161             }
18162             this.childIndent = buf.join("");
18163         }
18164         return this.childIndent;
18165     },
18166
18167     renderIndent : function(){
18168         if(this.rendered){
18169             var indent = "";
18170             var p = this.node.parentNode;
18171             if(p){
18172                 indent = p.ui.getChildIndent();
18173             }
18174             if(this.indentMarkup != indent){ // don't rerender if not required
18175                 this.indentNode.innerHTML = indent;
18176                 this.indentMarkup = indent;
18177             }
18178             this.updateExpandIcon();
18179         }
18180     }
18181 };
18182
18183 Roo.tree.RootTreeNodeUI = function(){
18184     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18185 };
18186 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18187     render : function(){
18188         if(!this.rendered){
18189             var targetNode = this.node.ownerTree.innerCt.dom;
18190             this.node.expanded = true;
18191             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18192             this.wrap = this.ctNode = targetNode.firstChild;
18193         }
18194     },
18195     collapse : function(){
18196     },
18197     expand : function(){
18198     }
18199 });/*
18200  * Based on:
18201  * Ext JS Library 1.1.1
18202  * Copyright(c) 2006-2007, Ext JS, LLC.
18203  *
18204  * Originally Released Under LGPL - original licence link has changed is not relivant.
18205  *
18206  * Fork - LGPL
18207  * <script type="text/javascript">
18208  */
18209 /**
18210  * @class Roo.tree.TreeLoader
18211  * @extends Roo.util.Observable
18212  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18213  * nodes from a specified URL. The response must be a javascript Array definition
18214  * who's elements are node definition objects. eg:
18215  * <pre><code>
18216    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18217     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18218 </code></pre>
18219  * <br><br>
18220  * A server request is sent, and child nodes are loaded only when a node is expanded.
18221  * The loading node's id is passed to the server under the parameter name "node" to
18222  * enable the server to produce the correct child nodes.
18223  * <br><br>
18224  * To pass extra parameters, an event handler may be attached to the "beforeload"
18225  * event, and the parameters specified in the TreeLoader's baseParams property:
18226  * <pre><code>
18227     myTreeLoader.on("beforeload", function(treeLoader, node) {
18228         this.baseParams.category = node.attributes.category;
18229     }, this);
18230 </code></pre><
18231  * This would pass an HTTP parameter called "category" to the server containing
18232  * the value of the Node's "category" attribute.
18233  * @constructor
18234  * Creates a new Treeloader.
18235  * @param {Object} config A config object containing config properties.
18236  */
18237 Roo.tree.TreeLoader = function(config){
18238     this.baseParams = {};
18239     this.requestMethod = "POST";
18240     Roo.apply(this, config);
18241
18242     this.addEvents({
18243     
18244         /**
18245          * @event beforeload
18246          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18247          * @param {Object} This TreeLoader object.
18248          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18249          * @param {Object} callback The callback function specified in the {@link #load} call.
18250          */
18251         beforeload : true,
18252         /**
18253          * @event load
18254          * Fires when the node has been successfuly loaded.
18255          * @param {Object} This TreeLoader object.
18256          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18257          * @param {Object} response The response object containing the data from the server.
18258          */
18259         load : true,
18260         /**
18261          * @event loadexception
18262          * Fires if the network request failed.
18263          * @param {Object} This TreeLoader object.
18264          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18265          * @param {Object} response The response object containing the data from the server.
18266          */
18267         loadexception : true,
18268         /**
18269          * @event create
18270          * Fires before a node is created, enabling you to return custom Node types 
18271          * @param {Object} This TreeLoader object.
18272          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18273          */
18274         create : true
18275     });
18276
18277     Roo.tree.TreeLoader.superclass.constructor.call(this);
18278 };
18279
18280 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18281     /**
18282     * @cfg {String} dataUrl The URL from which to request a Json string which
18283     * specifies an array of node definition object representing the child nodes
18284     * to be loaded.
18285     */
18286     /**
18287     * @cfg {Object} baseParams (optional) An object containing properties which
18288     * specify HTTP parameters to be passed to each request for child nodes.
18289     */
18290     /**
18291     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18292     * created by this loader. If the attributes sent by the server have an attribute in this object,
18293     * they take priority.
18294     */
18295     /**
18296     * @cfg {Object} uiProviders (optional) An object containing properties which
18297     * 
18298     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18299     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18300     * <i>uiProvider</i> attribute of a returned child node is a string rather
18301     * than a reference to a TreeNodeUI implementation, this that string value
18302     * is used as a property name in the uiProviders object. You can define the provider named
18303     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18304     */
18305     uiProviders : {},
18306
18307     /**
18308     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18309     * child nodes before loading.
18310     */
18311     clearOnLoad : true,
18312
18313     /**
18314     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18315     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18316     * Grid query { data : [ .....] }
18317     */
18318     
18319     root : false,
18320      /**
18321     * @cfg {String} queryParam (optional) 
18322     * Name of the query as it will be passed on the querystring (defaults to 'node')
18323     * eg. the request will be ?node=[id]
18324     */
18325     
18326     
18327     queryParam: false,
18328     
18329     /**
18330      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18331      * This is called automatically when a node is expanded, but may be used to reload
18332      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18333      * @param {Roo.tree.TreeNode} node
18334      * @param {Function} callback
18335      */
18336     load : function(node, callback){
18337         if(this.clearOnLoad){
18338             while(node.firstChild){
18339                 node.removeChild(node.firstChild);
18340             }
18341         }
18342         if(node.attributes.children){ // preloaded json children
18343             var cs = node.attributes.children;
18344             for(var i = 0, len = cs.length; i < len; i++){
18345                 node.appendChild(this.createNode(cs[i]));
18346             }
18347             if(typeof callback == "function"){
18348                 callback();
18349             }
18350         }else if(this.dataUrl){
18351             this.requestData(node, callback);
18352         }
18353     },
18354
18355     getParams: function(node){
18356         var buf = [], bp = this.baseParams;
18357         for(var key in bp){
18358             if(typeof bp[key] != "function"){
18359                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18360             }
18361         }
18362         var n = this.queryParam === false ? 'node' : this.queryParam;
18363         buf.push(n + "=", encodeURIComponent(node.id));
18364         return buf.join("");
18365     },
18366
18367     requestData : function(node, callback){
18368         if(this.fireEvent("beforeload", this, node, callback) !== false){
18369             this.transId = Roo.Ajax.request({
18370                 method:this.requestMethod,
18371                 url: this.dataUrl||this.url,
18372                 success: this.handleResponse,
18373                 failure: this.handleFailure,
18374                 scope: this,
18375                 argument: {callback: callback, node: node},
18376                 params: this.getParams(node)
18377             });
18378         }else{
18379             // if the load is cancelled, make sure we notify
18380             // the node that we are done
18381             if(typeof callback == "function"){
18382                 callback();
18383             }
18384         }
18385     },
18386
18387     isLoading : function(){
18388         return this.transId ? true : false;
18389     },
18390
18391     abort : function(){
18392         if(this.isLoading()){
18393             Roo.Ajax.abort(this.transId);
18394         }
18395     },
18396
18397     // private
18398     createNode : function(attr){
18399         // apply baseAttrs, nice idea Corey!
18400         if(this.baseAttrs){
18401             Roo.applyIf(attr, this.baseAttrs);
18402         }
18403         if(this.applyLoader !== false){
18404             attr.loader = this;
18405         }
18406         // uiProvider = depreciated..
18407         
18408         if(typeof(attr.uiProvider) == 'string'){
18409            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18410                 /**  eval:var:attr */ eval(attr.uiProvider);
18411         }
18412         if(typeof(this.uiProviders['default']) != 'undefined') {
18413             attr.uiProvider = this.uiProviders['default'];
18414         }
18415         
18416         this.fireEvent('create', this, attr);
18417         
18418         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18419         return(attr.leaf ?
18420                         new Roo.tree.TreeNode(attr) :
18421                         new Roo.tree.AsyncTreeNode(attr));
18422     },
18423
18424     processResponse : function(response, node, callback){
18425         var json = response.responseText;
18426         try {
18427             
18428             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18429             if (this.root !== false) {
18430                 o = o[this.root];
18431             }
18432             
18433             for(var i = 0, len = o.length; i < len; i++){
18434                 var n = this.createNode(o[i]);
18435                 if(n){
18436                     node.appendChild(n);
18437                 }
18438             }
18439             if(typeof callback == "function"){
18440                 callback(this, node);
18441             }
18442         }catch(e){
18443             this.handleFailure(response);
18444         }
18445     },
18446
18447     handleResponse : function(response){
18448         this.transId = false;
18449         var a = response.argument;
18450         this.processResponse(response, a.node, a.callback);
18451         this.fireEvent("load", this, a.node, response);
18452     },
18453
18454     handleFailure : function(response){
18455         this.transId = false;
18456         var a = response.argument;
18457         this.fireEvent("loadexception", this, a.node, response);
18458         if(typeof a.callback == "function"){
18459             a.callback(this, a.node);
18460         }
18461     }
18462 });/*
18463  * Based on:
18464  * Ext JS Library 1.1.1
18465  * Copyright(c) 2006-2007, Ext JS, LLC.
18466  *
18467  * Originally Released Under LGPL - original licence link has changed is not relivant.
18468  *
18469  * Fork - LGPL
18470  * <script type="text/javascript">
18471  */
18472
18473 /**
18474 * @class Roo.tree.TreeFilter
18475 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18476 * @param {TreePanel} tree
18477 * @param {Object} config (optional)
18478  */
18479 Roo.tree.TreeFilter = function(tree, config){
18480     this.tree = tree;
18481     this.filtered = {};
18482     Roo.apply(this, config);
18483 };
18484
18485 Roo.tree.TreeFilter.prototype = {
18486     clearBlank:false,
18487     reverse:false,
18488     autoClear:false,
18489     remove:false,
18490
18491      /**
18492      * Filter the data by a specific attribute.
18493      * @param {String/RegExp} value Either string that the attribute value
18494      * should start with or a RegExp to test against the attribute
18495      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18496      * @param {TreeNode} startNode (optional) The node to start the filter at.
18497      */
18498     filter : function(value, attr, startNode){
18499         attr = attr || "text";
18500         var f;
18501         if(typeof value == "string"){
18502             var vlen = value.length;
18503             // auto clear empty filter
18504             if(vlen == 0 && this.clearBlank){
18505                 this.clear();
18506                 return;
18507             }
18508             value = value.toLowerCase();
18509             f = function(n){
18510                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18511             };
18512         }else if(value.exec){ // regex?
18513             f = function(n){
18514                 return value.test(n.attributes[attr]);
18515             };
18516         }else{
18517             throw 'Illegal filter type, must be string or regex';
18518         }
18519         this.filterBy(f, null, startNode);
18520         },
18521
18522     /**
18523      * Filter by a function. The passed function will be called with each
18524      * node in the tree (or from the startNode). If the function returns true, the node is kept
18525      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18526      * @param {Function} fn The filter function
18527      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18528      */
18529     filterBy : function(fn, scope, startNode){
18530         startNode = startNode || this.tree.root;
18531         if(this.autoClear){
18532             this.clear();
18533         }
18534         var af = this.filtered, rv = this.reverse;
18535         var f = function(n){
18536             if(n == startNode){
18537                 return true;
18538             }
18539             if(af[n.id]){
18540                 return false;
18541             }
18542             var m = fn.call(scope || n, n);
18543             if(!m || rv){
18544                 af[n.id] = n;
18545                 n.ui.hide();
18546                 return false;
18547             }
18548             return true;
18549         };
18550         startNode.cascade(f);
18551         if(this.remove){
18552            for(var id in af){
18553                if(typeof id != "function"){
18554                    var n = af[id];
18555                    if(n && n.parentNode){
18556                        n.parentNode.removeChild(n);
18557                    }
18558                }
18559            }
18560         }
18561     },
18562
18563     /**
18564      * Clears the current filter. Note: with the "remove" option
18565      * set a filter cannot be cleared.
18566      */
18567     clear : function(){
18568         var t = this.tree;
18569         var af = this.filtered;
18570         for(var id in af){
18571             if(typeof id != "function"){
18572                 var n = af[id];
18573                 if(n){
18574                     n.ui.show();
18575                 }
18576             }
18577         }
18578         this.filtered = {};
18579     }
18580 };
18581 /*
18582  * Based on:
18583  * Ext JS Library 1.1.1
18584  * Copyright(c) 2006-2007, Ext JS, LLC.
18585  *
18586  * Originally Released Under LGPL - original licence link has changed is not relivant.
18587  *
18588  * Fork - LGPL
18589  * <script type="text/javascript">
18590  */
18591  
18592
18593 /**
18594  * @class Roo.tree.TreeSorter
18595  * Provides sorting of nodes in a TreePanel
18596  * 
18597  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18598  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18599  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18600  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18601  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18602  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18603  * @constructor
18604  * @param {TreePanel} tree
18605  * @param {Object} config
18606  */
18607 Roo.tree.TreeSorter = function(tree, config){
18608     Roo.apply(this, config);
18609     tree.on("beforechildrenrendered", this.doSort, this);
18610     tree.on("append", this.updateSort, this);
18611     tree.on("insert", this.updateSort, this);
18612     
18613     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18614     var p = this.property || "text";
18615     var sortType = this.sortType;
18616     var fs = this.folderSort;
18617     var cs = this.caseSensitive === true;
18618     var leafAttr = this.leafAttr || 'leaf';
18619
18620     this.sortFn = function(n1, n2){
18621         if(fs){
18622             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18623                 return 1;
18624             }
18625             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18626                 return -1;
18627             }
18628         }
18629         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18630         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18631         if(v1 < v2){
18632                         return dsc ? +1 : -1;
18633                 }else if(v1 > v2){
18634                         return dsc ? -1 : +1;
18635         }else{
18636                 return 0;
18637         }
18638     };
18639 };
18640
18641 Roo.tree.TreeSorter.prototype = {
18642     doSort : function(node){
18643         node.sort(this.sortFn);
18644     },
18645     
18646     compareNodes : function(n1, n2){
18647         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18648     },
18649     
18650     updateSort : function(tree, node){
18651         if(node.childrenRendered){
18652             this.doSort.defer(1, this, [node]);
18653         }
18654     }
18655 };/*
18656  * Based on:
18657  * Ext JS Library 1.1.1
18658  * Copyright(c) 2006-2007, Ext JS, LLC.
18659  *
18660  * Originally Released Under LGPL - original licence link has changed is not relivant.
18661  *
18662  * Fork - LGPL
18663  * <script type="text/javascript">
18664  */
18665
18666 if(Roo.dd.DropZone){
18667     
18668 Roo.tree.TreeDropZone = function(tree, config){
18669     this.allowParentInsert = false;
18670     this.allowContainerDrop = false;
18671     this.appendOnly = false;
18672     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18673     this.tree = tree;
18674     this.lastInsertClass = "x-tree-no-status";
18675     this.dragOverData = {};
18676 };
18677
18678 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18679     ddGroup : "TreeDD",
18680     
18681     expandDelay : 1000,
18682     
18683     expandNode : function(node){
18684         if(node.hasChildNodes() && !node.isExpanded()){
18685             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18686         }
18687     },
18688     
18689     queueExpand : function(node){
18690         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18691     },
18692     
18693     cancelExpand : function(){
18694         if(this.expandProcId){
18695             clearTimeout(this.expandProcId);
18696             this.expandProcId = false;
18697         }
18698     },
18699     
18700     isValidDropPoint : function(n, pt, dd, e, data){
18701         if(!n || !data){ return false; }
18702         var targetNode = n.node;
18703         var dropNode = data.node;
18704         // default drop rules
18705         if(!(targetNode && targetNode.isTarget && pt)){
18706             return false;
18707         }
18708         if(pt == "append" && targetNode.allowChildren === false){
18709             return false;
18710         }
18711         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18712             return false;
18713         }
18714         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18715             return false;
18716         }
18717         // reuse the object
18718         var overEvent = this.dragOverData;
18719         overEvent.tree = this.tree;
18720         overEvent.target = targetNode;
18721         overEvent.data = data;
18722         overEvent.point = pt;
18723         overEvent.source = dd;
18724         overEvent.rawEvent = e;
18725         overEvent.dropNode = dropNode;
18726         overEvent.cancel = false;  
18727         var result = this.tree.fireEvent("nodedragover", overEvent);
18728         return overEvent.cancel === false && result !== false;
18729     },
18730     
18731     getDropPoint : function(e, n, dd){
18732         var tn = n.node;
18733         if(tn.isRoot){
18734             return tn.allowChildren !== false ? "append" : false; // always append for root
18735         }
18736         var dragEl = n.ddel;
18737         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18738         var y = Roo.lib.Event.getPageY(e);
18739         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18740         
18741         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18742         var noAppend = tn.allowChildren === false;
18743         if(this.appendOnly || tn.parentNode.allowChildren === false){
18744             return noAppend ? false : "append";
18745         }
18746         var noBelow = false;
18747         if(!this.allowParentInsert){
18748             noBelow = tn.hasChildNodes() && tn.isExpanded();
18749         }
18750         var q = (b - t) / (noAppend ? 2 : 3);
18751         if(y >= t && y < (t + q)){
18752             return "above";
18753         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18754             return "below";
18755         }else{
18756             return "append";
18757         }
18758     },
18759     
18760     onNodeEnter : function(n, dd, e, data){
18761         this.cancelExpand();
18762     },
18763     
18764     onNodeOver : function(n, dd, e, data){
18765         var pt = this.getDropPoint(e, n, dd);
18766         var node = n.node;
18767         
18768         // auto node expand check
18769         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18770             this.queueExpand(node);
18771         }else if(pt != "append"){
18772             this.cancelExpand();
18773         }
18774         
18775         // set the insert point style on the target node
18776         var returnCls = this.dropNotAllowed;
18777         if(this.isValidDropPoint(n, pt, dd, e, data)){
18778            if(pt){
18779                var el = n.ddel;
18780                var cls;
18781                if(pt == "above"){
18782                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18783                    cls = "x-tree-drag-insert-above";
18784                }else if(pt == "below"){
18785                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18786                    cls = "x-tree-drag-insert-below";
18787                }else{
18788                    returnCls = "x-tree-drop-ok-append";
18789                    cls = "x-tree-drag-append";
18790                }
18791                if(this.lastInsertClass != cls){
18792                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18793                    this.lastInsertClass = cls;
18794                }
18795            }
18796        }
18797        return returnCls;
18798     },
18799     
18800     onNodeOut : function(n, dd, e, data){
18801         this.cancelExpand();
18802         this.removeDropIndicators(n);
18803     },
18804     
18805     onNodeDrop : function(n, dd, e, data){
18806         var point = this.getDropPoint(e, n, dd);
18807         var targetNode = n.node;
18808         targetNode.ui.startDrop();
18809         if(!this.isValidDropPoint(n, point, dd, e, data)){
18810             targetNode.ui.endDrop();
18811             return false;
18812         }
18813         // first try to find the drop node
18814         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18815         var dropEvent = {
18816             tree : this.tree,
18817             target: targetNode,
18818             data: data,
18819             point: point,
18820             source: dd,
18821             rawEvent: e,
18822             dropNode: dropNode,
18823             cancel: !dropNode   
18824         };
18825         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18826         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18827             targetNode.ui.endDrop();
18828             return false;
18829         }
18830         // allow target changing
18831         targetNode = dropEvent.target;
18832         if(point == "append" && !targetNode.isExpanded()){
18833             targetNode.expand(false, null, function(){
18834                 this.completeDrop(dropEvent);
18835             }.createDelegate(this));
18836         }else{
18837             this.completeDrop(dropEvent);
18838         }
18839         return true;
18840     },
18841     
18842     completeDrop : function(de){
18843         var ns = de.dropNode, p = de.point, t = de.target;
18844         if(!(ns instanceof Array)){
18845             ns = [ns];
18846         }
18847         var n;
18848         for(var i = 0, len = ns.length; i < len; i++){
18849             n = ns[i];
18850             if(p == "above"){
18851                 t.parentNode.insertBefore(n, t);
18852             }else if(p == "below"){
18853                 t.parentNode.insertBefore(n, t.nextSibling);
18854             }else{
18855                 t.appendChild(n);
18856             }
18857         }
18858         n.ui.focus();
18859         if(this.tree.hlDrop){
18860             n.ui.highlight();
18861         }
18862         t.ui.endDrop();
18863         this.tree.fireEvent("nodedrop", de);
18864     },
18865     
18866     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18867         if(this.tree.hlDrop){
18868             dropNode.ui.focus();
18869             dropNode.ui.highlight();
18870         }
18871         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18872     },
18873     
18874     getTree : function(){
18875         return this.tree;
18876     },
18877     
18878     removeDropIndicators : function(n){
18879         if(n && n.ddel){
18880             var el = n.ddel;
18881             Roo.fly(el).removeClass([
18882                     "x-tree-drag-insert-above",
18883                     "x-tree-drag-insert-below",
18884                     "x-tree-drag-append"]);
18885             this.lastInsertClass = "_noclass";
18886         }
18887     },
18888     
18889     beforeDragDrop : function(target, e, id){
18890         this.cancelExpand();
18891         return true;
18892     },
18893     
18894     afterRepair : function(data){
18895         if(data && Roo.enableFx){
18896             data.node.ui.highlight();
18897         }
18898         this.hideProxy();
18899     }    
18900 });
18901
18902 }
18903 /*
18904  * Based on:
18905  * Ext JS Library 1.1.1
18906  * Copyright(c) 2006-2007, Ext JS, LLC.
18907  *
18908  * Originally Released Under LGPL - original licence link has changed is not relivant.
18909  *
18910  * Fork - LGPL
18911  * <script type="text/javascript">
18912  */
18913  
18914
18915 if(Roo.dd.DragZone){
18916 Roo.tree.TreeDragZone = function(tree, config){
18917     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18918     this.tree = tree;
18919 };
18920
18921 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18922     ddGroup : "TreeDD",
18923     
18924     onBeforeDrag : function(data, e){
18925         var n = data.node;
18926         return n && n.draggable && !n.disabled;
18927     },
18928     
18929     onInitDrag : function(e){
18930         var data = this.dragData;
18931         this.tree.getSelectionModel().select(data.node);
18932         this.proxy.update("");
18933         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18934         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18935     },
18936     
18937     getRepairXY : function(e, data){
18938         return data.node.ui.getDDRepairXY();
18939     },
18940     
18941     onEndDrag : function(data, e){
18942         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18943     },
18944     
18945     onValidDrop : function(dd, e, id){
18946         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18947         this.hideProxy();
18948     },
18949     
18950     beforeInvalidDrop : function(e, id){
18951         // this scrolls the original position back into view
18952         var sm = this.tree.getSelectionModel();
18953         sm.clearSelections();
18954         sm.select(this.dragData.node);
18955     }
18956 });
18957 }/*
18958  * Based on:
18959  * Ext JS Library 1.1.1
18960  * Copyright(c) 2006-2007, Ext JS, LLC.
18961  *
18962  * Originally Released Under LGPL - original licence link has changed is not relivant.
18963  *
18964  * Fork - LGPL
18965  * <script type="text/javascript">
18966  */
18967 /**
18968  * @class Roo.tree.TreeEditor
18969  * @extends Roo.Editor
18970  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18971  * as the editor field.
18972  * @constructor
18973  * @param {TreePanel} tree
18974  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18975  */
18976 Roo.tree.TreeEditor = function(tree, config){
18977     config = config || {};
18978     var field = config.events ? config : new Roo.form.TextField(config);
18979     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18980
18981     this.tree = tree;
18982
18983     tree.on('beforeclick', this.beforeNodeClick, this);
18984     tree.getTreeEl().on('mousedown', this.hide, this);
18985     this.on('complete', this.updateNode, this);
18986     this.on('beforestartedit', this.fitToTree, this);
18987     this.on('startedit', this.bindScroll, this, {delay:10});
18988     this.on('specialkey', this.onSpecialKey, this);
18989 };
18990
18991 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18992     /**
18993      * @cfg {String} alignment
18994      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18995      */
18996     alignment: "l-l",
18997     // inherit
18998     autoSize: false,
18999     /**
19000      * @cfg {Boolean} hideEl
19001      * True to hide the bound element while the editor is displayed (defaults to false)
19002      */
19003     hideEl : false,
19004     /**
19005      * @cfg {String} cls
19006      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19007      */
19008     cls: "x-small-editor x-tree-editor",
19009     /**
19010      * @cfg {Boolean} shim
19011      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19012      */
19013     shim:false,
19014     // inherit
19015     shadow:"frame",
19016     /**
19017      * @cfg {Number} maxWidth
19018      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19019      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19020      * scroll and client offsets into account prior to each edit.
19021      */
19022     maxWidth: 250,
19023
19024     editDelay : 350,
19025
19026     // private
19027     fitToTree : function(ed, el){
19028         var td = this.tree.getTreeEl().dom, nd = el.dom;
19029         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19030             td.scrollLeft = nd.offsetLeft;
19031         }
19032         var w = Math.min(
19033                 this.maxWidth,
19034                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19035         this.setSize(w, '');
19036     },
19037
19038     // private
19039     triggerEdit : function(node){
19040         this.completeEdit();
19041         this.editNode = node;
19042         this.startEdit(node.ui.textNode, node.text);
19043     },
19044
19045     // private
19046     bindScroll : function(){
19047         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19048     },
19049
19050     // private
19051     beforeNodeClick : function(node, e){
19052         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19053         this.lastClick = new Date();
19054         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19055             e.stopEvent();
19056             this.triggerEdit(node);
19057             return false;
19058         }
19059     },
19060
19061     // private
19062     updateNode : function(ed, value){
19063         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19064         this.editNode.setText(value);
19065     },
19066
19067     // private
19068     onHide : function(){
19069         Roo.tree.TreeEditor.superclass.onHide.call(this);
19070         if(this.editNode){
19071             this.editNode.ui.focus();
19072         }
19073     },
19074
19075     // private
19076     onSpecialKey : function(field, e){
19077         var k = e.getKey();
19078         if(k == e.ESC){
19079             e.stopEvent();
19080             this.cancelEdit();
19081         }else if(k == e.ENTER && !e.hasModifier()){
19082             e.stopEvent();
19083             this.completeEdit();
19084         }
19085     }
19086 });//<Script type="text/javascript">
19087 /*
19088  * Based on:
19089  * Ext JS Library 1.1.1
19090  * Copyright(c) 2006-2007, Ext JS, LLC.
19091  *
19092  * Originally Released Under LGPL - original licence link has changed is not relivant.
19093  *
19094  * Fork - LGPL
19095  * <script type="text/javascript">
19096  */
19097  
19098 /**
19099  * Not documented??? - probably should be...
19100  */
19101
19102 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19103     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19104     
19105     renderElements : function(n, a, targetNode, bulkRender){
19106         //consel.log("renderElements?");
19107         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19108
19109         var t = n.getOwnerTree();
19110         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19111         
19112         var cols = t.columns;
19113         var bw = t.borderWidth;
19114         var c = cols[0];
19115         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19116          var cb = typeof a.checked == "boolean";
19117         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19118         var colcls = 'x-t-' + tid + '-c0';
19119         var buf = [
19120             '<li class="x-tree-node">',
19121             
19122                 
19123                 '<div class="x-tree-node-el ', a.cls,'">',
19124                     // extran...
19125                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19126                 
19127                 
19128                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19129                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19130                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19131                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19132                            (a.iconCls ? ' '+a.iconCls : ''),
19133                            '" unselectable="on" />',
19134                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19135                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19136                              
19137                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19138                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19139                             '<span unselectable="on" qtip="' + tx + '">',
19140                              tx,
19141                              '</span></a>' ,
19142                     '</div>',
19143                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19144                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19145                  ];
19146         for(var i = 1, len = cols.length; i < len; i++){
19147             c = cols[i];
19148             colcls = 'x-t-' + tid + '-c' +i;
19149             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19150             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19151                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19152                       "</div>");
19153          }
19154          
19155          buf.push(
19156             '</a>',
19157             '<div class="x-clear"></div></div>',
19158             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19159             "</li>");
19160         
19161         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19162             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19163                                 n.nextSibling.ui.getEl(), buf.join(""));
19164         }else{
19165             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19166         }
19167         var el = this.wrap.firstChild;
19168         this.elRow = el;
19169         this.elNode = el.firstChild;
19170         this.ranchor = el.childNodes[1];
19171         this.ctNode = this.wrap.childNodes[1];
19172         var cs = el.firstChild.childNodes;
19173         this.indentNode = cs[0];
19174         this.ecNode = cs[1];
19175         this.iconNode = cs[2];
19176         var index = 3;
19177         if(cb){
19178             this.checkbox = cs[3];
19179             index++;
19180         }
19181         this.anchor = cs[index];
19182         
19183         this.textNode = cs[index].firstChild;
19184         
19185         //el.on("click", this.onClick, this);
19186         //el.on("dblclick", this.onDblClick, this);
19187         
19188         
19189        // console.log(this);
19190     },
19191     initEvents : function(){
19192         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19193         
19194             
19195         var a = this.ranchor;
19196
19197         var el = Roo.get(a);
19198
19199         if(Roo.isOpera){ // opera render bug ignores the CSS
19200             el.setStyle("text-decoration", "none");
19201         }
19202
19203         el.on("click", this.onClick, this);
19204         el.on("dblclick", this.onDblClick, this);
19205         el.on("contextmenu", this.onContextMenu, this);
19206         
19207     },
19208     
19209     /*onSelectedChange : function(state){
19210         if(state){
19211             this.focus();
19212             this.addClass("x-tree-selected");
19213         }else{
19214             //this.blur();
19215             this.removeClass("x-tree-selected");
19216         }
19217     },*/
19218     addClass : function(cls){
19219         if(this.elRow){
19220             Roo.fly(this.elRow).addClass(cls);
19221         }
19222         
19223     },
19224     
19225     
19226     removeClass : function(cls){
19227         if(this.elRow){
19228             Roo.fly(this.elRow).removeClass(cls);
19229         }
19230     }
19231
19232     
19233     
19234 });//<Script type="text/javascript">
19235
19236 /*
19237  * Based on:
19238  * Ext JS Library 1.1.1
19239  * Copyright(c) 2006-2007, Ext JS, LLC.
19240  *
19241  * Originally Released Under LGPL - original licence link has changed is not relivant.
19242  *
19243  * Fork - LGPL
19244  * <script type="text/javascript">
19245  */
19246  
19247
19248 /**
19249  * @class Roo.tree.ColumnTree
19250  * @extends Roo.data.TreePanel
19251  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19252  * @cfg {int} borderWidth  compined right/left border allowance
19253  * @constructor
19254  * @param {String/HTMLElement/Element} el The container element
19255  * @param {Object} config
19256  */
19257 Roo.tree.ColumnTree =  function(el, config)
19258 {
19259    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19260    this.addEvents({
19261         /**
19262         * @event resize
19263         * Fire this event on a container when it resizes
19264         * @param {int} w Width
19265         * @param {int} h Height
19266         */
19267        "resize" : true
19268     });
19269     this.on('resize', this.onResize, this);
19270 };
19271
19272 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19273     //lines:false,
19274     
19275     
19276     borderWidth: Roo.isBorderBox ? 0 : 2, 
19277     headEls : false,
19278     
19279     render : function(){
19280         // add the header.....
19281        
19282         Roo.tree.ColumnTree.superclass.render.apply(this);
19283         
19284         this.el.addClass('x-column-tree');
19285         
19286         this.headers = this.el.createChild(
19287             {cls:'x-tree-headers'},this.innerCt.dom);
19288    
19289         var cols = this.columns, c;
19290         var totalWidth = 0;
19291         this.headEls = [];
19292         var  len = cols.length;
19293         for(var i = 0; i < len; i++){
19294              c = cols[i];
19295              totalWidth += c.width;
19296             this.headEls.push(this.headers.createChild({
19297                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19298                  cn: {
19299                      cls:'x-tree-hd-text',
19300                      html: c.header
19301                  },
19302                  style:'width:'+(c.width-this.borderWidth)+'px;'
19303              }));
19304         }
19305         this.headers.createChild({cls:'x-clear'});
19306         // prevent floats from wrapping when clipped
19307         this.headers.setWidth(totalWidth);
19308         //this.innerCt.setWidth(totalWidth);
19309         this.innerCt.setStyle({ overflow: 'auto' });
19310         this.onResize(this.width, this.height);
19311              
19312         
19313     },
19314     onResize : function(w,h)
19315     {
19316         this.height = h;
19317         this.width = w;
19318         // resize cols..
19319         this.innerCt.setWidth(this.width);
19320         this.innerCt.setHeight(this.height-20);
19321         
19322         // headers...
19323         var cols = this.columns, c;
19324         var totalWidth = 0;
19325         var expEl = false;
19326         var len = cols.length;
19327         for(var i = 0; i < len; i++){
19328             c = cols[i];
19329             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19330                 // it's the expander..
19331                 expEl  = this.headEls[i];
19332                 continue;
19333             }
19334             totalWidth += c.width;
19335             
19336         }
19337         if (expEl) {
19338             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19339         }
19340         this.headers.setWidth(w-20);
19341
19342         
19343         
19344         
19345     }
19346 });
19347 /*
19348  * Based on:
19349  * Ext JS Library 1.1.1
19350  * Copyright(c) 2006-2007, Ext JS, LLC.
19351  *
19352  * Originally Released Under LGPL - original licence link has changed is not relivant.
19353  *
19354  * Fork - LGPL
19355  * <script type="text/javascript">
19356  */
19357  
19358 /**
19359  * @class Roo.menu.Menu
19360  * @extends Roo.util.Observable
19361  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19362  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19363  * @constructor
19364  * Creates a new Menu
19365  * @param {Object} config Configuration options
19366  */
19367 Roo.menu.Menu = function(config){
19368     Roo.apply(this, config);
19369     this.id = this.id || Roo.id();
19370     this.addEvents({
19371         /**
19372          * @event beforeshow
19373          * Fires before this menu is displayed
19374          * @param {Roo.menu.Menu} this
19375          */
19376         beforeshow : true,
19377         /**
19378          * @event beforehide
19379          * Fires before this menu is hidden
19380          * @param {Roo.menu.Menu} this
19381          */
19382         beforehide : true,
19383         /**
19384          * @event show
19385          * Fires after this menu is displayed
19386          * @param {Roo.menu.Menu} this
19387          */
19388         show : true,
19389         /**
19390          * @event hide
19391          * Fires after this menu is hidden
19392          * @param {Roo.menu.Menu} this
19393          */
19394         hide : true,
19395         /**
19396          * @event click
19397          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19398          * @param {Roo.menu.Menu} this
19399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19400          * @param {Roo.EventObject} e
19401          */
19402         click : true,
19403         /**
19404          * @event mouseover
19405          * Fires when the mouse is hovering over this menu
19406          * @param {Roo.menu.Menu} this
19407          * @param {Roo.EventObject} e
19408          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19409          */
19410         mouseover : true,
19411         /**
19412          * @event mouseout
19413          * Fires when the mouse exits this menu
19414          * @param {Roo.menu.Menu} this
19415          * @param {Roo.EventObject} e
19416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19417          */
19418         mouseout : true,
19419         /**
19420          * @event itemclick
19421          * Fires when a menu item contained in this menu is clicked
19422          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19423          * @param {Roo.EventObject} e
19424          */
19425         itemclick: true
19426     });
19427     if (this.registerMenu) {
19428         Roo.menu.MenuMgr.register(this);
19429     }
19430     
19431     var mis = this.items;
19432     this.items = new Roo.util.MixedCollection();
19433     if(mis){
19434         this.add.apply(this, mis);
19435     }
19436 };
19437
19438 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19439     /**
19440      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19441      */
19442     minWidth : 120,
19443     /**
19444      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19445      * for bottom-right shadow (defaults to "sides")
19446      */
19447     shadow : "sides",
19448     /**
19449      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19450      * this menu (defaults to "tl-tr?")
19451      */
19452     subMenuAlign : "tl-tr?",
19453     /**
19454      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19455      * relative to its element of origin (defaults to "tl-bl?")
19456      */
19457     defaultAlign : "tl-bl?",
19458     /**
19459      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19460      */
19461     allowOtherMenus : false,
19462     /**
19463      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19464      */
19465     registerMenu : true,
19466
19467     hidden:true,
19468
19469     // private
19470     render : function(){
19471         if(this.el){
19472             return;
19473         }
19474         var el = this.el = new Roo.Layer({
19475             cls: "x-menu",
19476             shadow:this.shadow,
19477             constrain: false,
19478             parentEl: this.parentEl || document.body,
19479             zindex:15000
19480         });
19481
19482         this.keyNav = new Roo.menu.MenuNav(this);
19483
19484         if(this.plain){
19485             el.addClass("x-menu-plain");
19486         }
19487         if(this.cls){
19488             el.addClass(this.cls);
19489         }
19490         // generic focus element
19491         this.focusEl = el.createChild({
19492             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19493         });
19494         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19495         ul.on("click", this.onClick, this);
19496         ul.on("mouseover", this.onMouseOver, this);
19497         ul.on("mouseout", this.onMouseOut, this);
19498         this.items.each(function(item){
19499             var li = document.createElement("li");
19500             li.className = "x-menu-list-item";
19501             ul.dom.appendChild(li);
19502             item.render(li, this);
19503         }, this);
19504         this.ul = ul;
19505         this.autoWidth();
19506     },
19507
19508     // private
19509     autoWidth : function(){
19510         var el = this.el, ul = this.ul;
19511         if(!el){
19512             return;
19513         }
19514         var w = this.width;
19515         if(w){
19516             el.setWidth(w);
19517         }else if(Roo.isIE){
19518             el.setWidth(this.minWidth);
19519             var t = el.dom.offsetWidth; // force recalc
19520             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19521         }
19522     },
19523
19524     // private
19525     delayAutoWidth : function(){
19526         if(this.rendered){
19527             if(!this.awTask){
19528                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19529             }
19530             this.awTask.delay(20);
19531         }
19532     },
19533
19534     // private
19535     findTargetItem : function(e){
19536         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19537         if(t && t.menuItemId){
19538             return this.items.get(t.menuItemId);
19539         }
19540     },
19541
19542     // private
19543     onClick : function(e){
19544         var t;
19545         if(t = this.findTargetItem(e)){
19546             t.onClick(e);
19547             this.fireEvent("click", this, t, e);
19548         }
19549     },
19550
19551     // private
19552     setActiveItem : function(item, autoExpand){
19553         if(item != this.activeItem){
19554             if(this.activeItem){
19555                 this.activeItem.deactivate();
19556             }
19557             this.activeItem = item;
19558             item.activate(autoExpand);
19559         }else if(autoExpand){
19560             item.expandMenu();
19561         }
19562     },
19563
19564     // private
19565     tryActivate : function(start, step){
19566         var items = this.items;
19567         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19568             var item = items.get(i);
19569             if(!item.disabled && item.canActivate){
19570                 this.setActiveItem(item, false);
19571                 return item;
19572             }
19573         }
19574         return false;
19575     },
19576
19577     // private
19578     onMouseOver : function(e){
19579         var t;
19580         if(t = this.findTargetItem(e)){
19581             if(t.canActivate && !t.disabled){
19582                 this.setActiveItem(t, true);
19583             }
19584         }
19585         this.fireEvent("mouseover", this, e, t);
19586     },
19587
19588     // private
19589     onMouseOut : function(e){
19590         var t;
19591         if(t = this.findTargetItem(e)){
19592             if(t == this.activeItem && t.shouldDeactivate(e)){
19593                 this.activeItem.deactivate();
19594                 delete this.activeItem;
19595             }
19596         }
19597         this.fireEvent("mouseout", this, e, t);
19598     },
19599
19600     /**
19601      * Read-only.  Returns true if the menu is currently displayed, else false.
19602      * @type Boolean
19603      */
19604     isVisible : function(){
19605         return this.el && !this.hidden;
19606     },
19607
19608     /**
19609      * Displays this menu relative to another element
19610      * @param {String/HTMLElement/Roo.Element} element The element to align to
19611      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19612      * the element (defaults to this.defaultAlign)
19613      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19614      */
19615     show : function(el, pos, parentMenu){
19616         this.parentMenu = parentMenu;
19617         if(!this.el){
19618             this.render();
19619         }
19620         this.fireEvent("beforeshow", this);
19621         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19622     },
19623
19624     /**
19625      * Displays this menu at a specific xy position
19626      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19627      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19628      */
19629     showAt : function(xy, parentMenu, /* private: */_e){
19630         this.parentMenu = parentMenu;
19631         if(!this.el){
19632             this.render();
19633         }
19634         if(_e !== false){
19635             this.fireEvent("beforeshow", this);
19636             xy = this.el.adjustForConstraints(xy);
19637         }
19638         this.el.setXY(xy);
19639         this.el.show();
19640         this.hidden = false;
19641         this.focus();
19642         this.fireEvent("show", this);
19643     },
19644
19645     focus : function(){
19646         if(!this.hidden){
19647             this.doFocus.defer(50, this);
19648         }
19649     },
19650
19651     doFocus : function(){
19652         if(!this.hidden){
19653             this.focusEl.focus();
19654         }
19655     },
19656
19657     /**
19658      * Hides this menu and optionally all parent menus
19659      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19660      */
19661     hide : function(deep){
19662         if(this.el && this.isVisible()){
19663             this.fireEvent("beforehide", this);
19664             if(this.activeItem){
19665                 this.activeItem.deactivate();
19666                 this.activeItem = null;
19667             }
19668             this.el.hide();
19669             this.hidden = true;
19670             this.fireEvent("hide", this);
19671         }
19672         if(deep === true && this.parentMenu){
19673             this.parentMenu.hide(true);
19674         }
19675     },
19676
19677     /**
19678      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19679      * Any of the following are valid:
19680      * <ul>
19681      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19682      * <li>An HTMLElement object which will be converted to a menu item</li>
19683      * <li>A menu item config object that will be created as a new menu item</li>
19684      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19685      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19686      * </ul>
19687      * Usage:
19688      * <pre><code>
19689 // Create the menu
19690 var menu = new Roo.menu.Menu();
19691
19692 // Create a menu item to add by reference
19693 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19694
19695 // Add a bunch of items at once using different methods.
19696 // Only the last item added will be returned.
19697 var item = menu.add(
19698     menuItem,                // add existing item by ref
19699     'Dynamic Item',          // new TextItem
19700     '-',                     // new separator
19701     { text: 'Config Item' }  // new item by config
19702 );
19703 </code></pre>
19704      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19705      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19706      */
19707     add : function(){
19708         var a = arguments, l = a.length, item;
19709         for(var i = 0; i < l; i++){
19710             var el = a[i];
19711             if ((typeof(el) == "object") && el.xtype && el.xns) {
19712                 el = Roo.factory(el, Roo.menu);
19713             }
19714             
19715             if(el.render){ // some kind of Item
19716                 item = this.addItem(el);
19717             }else if(typeof el == "string"){ // string
19718                 if(el == "separator" || el == "-"){
19719                     item = this.addSeparator();
19720                 }else{
19721                     item = this.addText(el);
19722                 }
19723             }else if(el.tagName || el.el){ // element
19724                 item = this.addElement(el);
19725             }else if(typeof el == "object"){ // must be menu item config?
19726                 item = this.addMenuItem(el);
19727             }
19728         }
19729         return item;
19730     },
19731
19732     /**
19733      * Returns this menu's underlying {@link Roo.Element} object
19734      * @return {Roo.Element} The element
19735      */
19736     getEl : function(){
19737         if(!this.el){
19738             this.render();
19739         }
19740         return this.el;
19741     },
19742
19743     /**
19744      * Adds a separator bar to the menu
19745      * @return {Roo.menu.Item} The menu item that was added
19746      */
19747     addSeparator : function(){
19748         return this.addItem(new Roo.menu.Separator());
19749     },
19750
19751     /**
19752      * Adds an {@link Roo.Element} object to the menu
19753      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19754      * @return {Roo.menu.Item} The menu item that was added
19755      */
19756     addElement : function(el){
19757         return this.addItem(new Roo.menu.BaseItem(el));
19758     },
19759
19760     /**
19761      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19762      * @param {Roo.menu.Item} item The menu item to add
19763      * @return {Roo.menu.Item} The menu item that was added
19764      */
19765     addItem : function(item){
19766         this.items.add(item);
19767         if(this.ul){
19768             var li = document.createElement("li");
19769             li.className = "x-menu-list-item";
19770             this.ul.dom.appendChild(li);
19771             item.render(li, this);
19772             this.delayAutoWidth();
19773         }
19774         return item;
19775     },
19776
19777     /**
19778      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19779      * @param {Object} config A MenuItem config object
19780      * @return {Roo.menu.Item} The menu item that was added
19781      */
19782     addMenuItem : function(config){
19783         if(!(config instanceof Roo.menu.Item)){
19784             if(typeof config.checked == "boolean"){ // must be check menu item config?
19785                 config = new Roo.menu.CheckItem(config);
19786             }else{
19787                 config = new Roo.menu.Item(config);
19788             }
19789         }
19790         return this.addItem(config);
19791     },
19792
19793     /**
19794      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19795      * @param {String} text The text to display in the menu item
19796      * @return {Roo.menu.Item} The menu item that was added
19797      */
19798     addText : function(text){
19799         return this.addItem(new Roo.menu.TextItem({ text : text }));
19800     },
19801
19802     /**
19803      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19804      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19805      * @param {Roo.menu.Item} item The menu item to add
19806      * @return {Roo.menu.Item} The menu item that was added
19807      */
19808     insert : function(index, item){
19809         this.items.insert(index, item);
19810         if(this.ul){
19811             var li = document.createElement("li");
19812             li.className = "x-menu-list-item";
19813             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19814             item.render(li, this);
19815             this.delayAutoWidth();
19816         }
19817         return item;
19818     },
19819
19820     /**
19821      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19822      * @param {Roo.menu.Item} item The menu item to remove
19823      */
19824     remove : function(item){
19825         this.items.removeKey(item.id);
19826         item.destroy();
19827     },
19828
19829     /**
19830      * Removes and destroys all items in the menu
19831      */
19832     removeAll : function(){
19833         var f;
19834         while(f = this.items.first()){
19835             this.remove(f);
19836         }
19837     }
19838 });
19839
19840 // MenuNav is a private utility class used internally by the Menu
19841 Roo.menu.MenuNav = function(menu){
19842     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19843     this.scope = this.menu = menu;
19844 };
19845
19846 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19847     doRelay : function(e, h){
19848         var k = e.getKey();
19849         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19850             this.menu.tryActivate(0, 1);
19851             return false;
19852         }
19853         return h.call(this.scope || this, e, this.menu);
19854     },
19855
19856     up : function(e, m){
19857         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19858             m.tryActivate(m.items.length-1, -1);
19859         }
19860     },
19861
19862     down : function(e, m){
19863         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19864             m.tryActivate(0, 1);
19865         }
19866     },
19867
19868     right : function(e, m){
19869         if(m.activeItem){
19870             m.activeItem.expandMenu(true);
19871         }
19872     },
19873
19874     left : function(e, m){
19875         m.hide();
19876         if(m.parentMenu && m.parentMenu.activeItem){
19877             m.parentMenu.activeItem.activate();
19878         }
19879     },
19880
19881     enter : function(e, m){
19882         if(m.activeItem){
19883             e.stopPropagation();
19884             m.activeItem.onClick(e);
19885             m.fireEvent("click", this, m.activeItem);
19886             return true;
19887         }
19888     }
19889 });/*
19890  * Based on:
19891  * Ext JS Library 1.1.1
19892  * Copyright(c) 2006-2007, Ext JS, LLC.
19893  *
19894  * Originally Released Under LGPL - original licence link has changed is not relivant.
19895  *
19896  * Fork - LGPL
19897  * <script type="text/javascript">
19898  */
19899  
19900 /**
19901  * @class Roo.menu.MenuMgr
19902  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19903  * @singleton
19904  */
19905 Roo.menu.MenuMgr = function(){
19906    var menus, active, groups = {}, attached = false, lastShow = new Date();
19907
19908    // private - called when first menu is created
19909    function init(){
19910        menus = {};
19911        active = new Roo.util.MixedCollection();
19912        Roo.get(document).addKeyListener(27, function(){
19913            if(active.length > 0){
19914                hideAll();
19915            }
19916        });
19917    }
19918
19919    // private
19920    function hideAll(){
19921        if(active && active.length > 0){
19922            var c = active.clone();
19923            c.each(function(m){
19924                m.hide();
19925            });
19926        }
19927    }
19928
19929    // private
19930    function onHide(m){
19931        active.remove(m);
19932        if(active.length < 1){
19933            Roo.get(document).un("mousedown", onMouseDown);
19934            attached = false;
19935        }
19936    }
19937
19938    // private
19939    function onShow(m){
19940        var last = active.last();
19941        lastShow = new Date();
19942        active.add(m);
19943        if(!attached){
19944            Roo.get(document).on("mousedown", onMouseDown);
19945            attached = true;
19946        }
19947        if(m.parentMenu){
19948           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19949           m.parentMenu.activeChild = m;
19950        }else if(last && last.isVisible()){
19951           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19952        }
19953    }
19954
19955    // private
19956    function onBeforeHide(m){
19957        if(m.activeChild){
19958            m.activeChild.hide();
19959        }
19960        if(m.autoHideTimer){
19961            clearTimeout(m.autoHideTimer);
19962            delete m.autoHideTimer;
19963        }
19964    }
19965
19966    // private
19967    function onBeforeShow(m){
19968        var pm = m.parentMenu;
19969        if(!pm && !m.allowOtherMenus){
19970            hideAll();
19971        }else if(pm && pm.activeChild && active != m){
19972            pm.activeChild.hide();
19973        }
19974    }
19975
19976    // private
19977    function onMouseDown(e){
19978        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19979            hideAll();
19980        }
19981    }
19982
19983    // private
19984    function onBeforeCheck(mi, state){
19985        if(state){
19986            var g = groups[mi.group];
19987            for(var i = 0, l = g.length; i < l; i++){
19988                if(g[i] != mi){
19989                    g[i].setChecked(false);
19990                }
19991            }
19992        }
19993    }
19994
19995    return {
19996
19997        /**
19998         * Hides all menus that are currently visible
19999         */
20000        hideAll : function(){
20001             hideAll();  
20002        },
20003
20004        // private
20005        register : function(menu){
20006            if(!menus){
20007                init();
20008            }
20009            menus[menu.id] = menu;
20010            menu.on("beforehide", onBeforeHide);
20011            menu.on("hide", onHide);
20012            menu.on("beforeshow", onBeforeShow);
20013            menu.on("show", onShow);
20014            var g = menu.group;
20015            if(g && menu.events["checkchange"]){
20016                if(!groups[g]){
20017                    groups[g] = [];
20018                }
20019                groups[g].push(menu);
20020                menu.on("checkchange", onCheck);
20021            }
20022        },
20023
20024         /**
20025          * Returns a {@link Roo.menu.Menu} object
20026          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20027          * be used to generate and return a new Menu instance.
20028          */
20029        get : function(menu){
20030            if(typeof menu == "string"){ // menu id
20031                return menus[menu];
20032            }else if(menu.events){  // menu instance
20033                return menu;
20034            }else if(typeof menu.length == 'number'){ // array of menu items?
20035                return new Roo.menu.Menu({items:menu});
20036            }else{ // otherwise, must be a config
20037                return new Roo.menu.Menu(menu);
20038            }
20039        },
20040
20041        // private
20042        unregister : function(menu){
20043            delete menus[menu.id];
20044            menu.un("beforehide", onBeforeHide);
20045            menu.un("hide", onHide);
20046            menu.un("beforeshow", onBeforeShow);
20047            menu.un("show", onShow);
20048            var g = menu.group;
20049            if(g && menu.events["checkchange"]){
20050                groups[g].remove(menu);
20051                menu.un("checkchange", onCheck);
20052            }
20053        },
20054
20055        // private
20056        registerCheckable : function(menuItem){
20057            var g = menuItem.group;
20058            if(g){
20059                if(!groups[g]){
20060                    groups[g] = [];
20061                }
20062                groups[g].push(menuItem);
20063                menuItem.on("beforecheckchange", onBeforeCheck);
20064            }
20065        },
20066
20067        // private
20068        unregisterCheckable : function(menuItem){
20069            var g = menuItem.group;
20070            if(g){
20071                groups[g].remove(menuItem);
20072                menuItem.un("beforecheckchange", onBeforeCheck);
20073            }
20074        }
20075    };
20076 }();/*
20077  * Based on:
20078  * Ext JS Library 1.1.1
20079  * Copyright(c) 2006-2007, Ext JS, LLC.
20080  *
20081  * Originally Released Under LGPL - original licence link has changed is not relivant.
20082  *
20083  * Fork - LGPL
20084  * <script type="text/javascript">
20085  */
20086  
20087
20088 /**
20089  * @class Roo.menu.BaseItem
20090  * @extends Roo.Component
20091  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20092  * management and base configuration options shared by all menu components.
20093  * @constructor
20094  * Creates a new BaseItem
20095  * @param {Object} config Configuration options
20096  */
20097 Roo.menu.BaseItem = function(config){
20098     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20099
20100     this.addEvents({
20101         /**
20102          * @event click
20103          * Fires when this item is clicked
20104          * @param {Roo.menu.BaseItem} this
20105          * @param {Roo.EventObject} e
20106          */
20107         click: true,
20108         /**
20109          * @event activate
20110          * Fires when this item is activated
20111          * @param {Roo.menu.BaseItem} this
20112          */
20113         activate : true,
20114         /**
20115          * @event deactivate
20116          * Fires when this item is deactivated
20117          * @param {Roo.menu.BaseItem} this
20118          */
20119         deactivate : true
20120     });
20121
20122     if(this.handler){
20123         this.on("click", this.handler, this.scope, true);
20124     }
20125 };
20126
20127 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20128     /**
20129      * @cfg {Function} handler
20130      * A function that will handle the click event of this menu item (defaults to undefined)
20131      */
20132     /**
20133      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20134      */
20135     canActivate : false,
20136     /**
20137      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20138      */
20139     activeClass : "x-menu-item-active",
20140     /**
20141      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20142      */
20143     hideOnClick : true,
20144     /**
20145      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20146      */
20147     hideDelay : 100,
20148
20149     // private
20150     ctype: "Roo.menu.BaseItem",
20151
20152     // private
20153     actionMode : "container",
20154
20155     // private
20156     render : function(container, parentMenu){
20157         this.parentMenu = parentMenu;
20158         Roo.menu.BaseItem.superclass.render.call(this, container);
20159         this.container.menuItemId = this.id;
20160     },
20161
20162     // private
20163     onRender : function(container, position){
20164         this.el = Roo.get(this.el);
20165         container.dom.appendChild(this.el.dom);
20166     },
20167
20168     // private
20169     onClick : function(e){
20170         if(!this.disabled && this.fireEvent("click", this, e) !== false
20171                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20172             this.handleClick(e);
20173         }else{
20174             e.stopEvent();
20175         }
20176     },
20177
20178     // private
20179     activate : function(){
20180         if(this.disabled){
20181             return false;
20182         }
20183         var li = this.container;
20184         li.addClass(this.activeClass);
20185         this.region = li.getRegion().adjust(2, 2, -2, -2);
20186         this.fireEvent("activate", this);
20187         return true;
20188     },
20189
20190     // private
20191     deactivate : function(){
20192         this.container.removeClass(this.activeClass);
20193         this.fireEvent("deactivate", this);
20194     },
20195
20196     // private
20197     shouldDeactivate : function(e){
20198         return !this.region || !this.region.contains(e.getPoint());
20199     },
20200
20201     // private
20202     handleClick : function(e){
20203         if(this.hideOnClick){
20204             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20205         }
20206     },
20207
20208     // private
20209     expandMenu : function(autoActivate){
20210         // do nothing
20211     },
20212
20213     // private
20214     hideMenu : function(){
20215         // do nothing
20216     }
20217 });/*
20218  * Based on:
20219  * Ext JS Library 1.1.1
20220  * Copyright(c) 2006-2007, Ext JS, LLC.
20221  *
20222  * Originally Released Under LGPL - original licence link has changed is not relivant.
20223  *
20224  * Fork - LGPL
20225  * <script type="text/javascript">
20226  */
20227  
20228 /**
20229  * @class Roo.menu.Adapter
20230  * @extends Roo.menu.BaseItem
20231  * 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.
20232  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20233  * @constructor
20234  * Creates a new Adapter
20235  * @param {Object} config Configuration options
20236  */
20237 Roo.menu.Adapter = function(component, config){
20238     Roo.menu.Adapter.superclass.constructor.call(this, config);
20239     this.component = component;
20240 };
20241 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20242     // private
20243     canActivate : true,
20244
20245     // private
20246     onRender : function(container, position){
20247         this.component.render(container);
20248         this.el = this.component.getEl();
20249     },
20250
20251     // private
20252     activate : function(){
20253         if(this.disabled){
20254             return false;
20255         }
20256         this.component.focus();
20257         this.fireEvent("activate", this);
20258         return true;
20259     },
20260
20261     // private
20262     deactivate : function(){
20263         this.fireEvent("deactivate", this);
20264     },
20265
20266     // private
20267     disable : function(){
20268         this.component.disable();
20269         Roo.menu.Adapter.superclass.disable.call(this);
20270     },
20271
20272     // private
20273     enable : function(){
20274         this.component.enable();
20275         Roo.menu.Adapter.superclass.enable.call(this);
20276     }
20277 });/*
20278  * Based on:
20279  * Ext JS Library 1.1.1
20280  * Copyright(c) 2006-2007, Ext JS, LLC.
20281  *
20282  * Originally Released Under LGPL - original licence link has changed is not relivant.
20283  *
20284  * Fork - LGPL
20285  * <script type="text/javascript">
20286  */
20287
20288 /**
20289  * @class Roo.menu.TextItem
20290  * @extends Roo.menu.BaseItem
20291  * Adds a static text string to a menu, usually used as either a heading or group separator.
20292  * Note: old style constructor with text is still supported.
20293  * 
20294  * @constructor
20295  * Creates a new TextItem
20296  * @param {Object} cfg Configuration
20297  */
20298 Roo.menu.TextItem = function(cfg){
20299     if (typeof(cfg) == 'string') {
20300         this.text = cfg;
20301     } else {
20302         Roo.apply(this,cfg);
20303     }
20304     
20305     Roo.menu.TextItem.superclass.constructor.call(this);
20306 };
20307
20308 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20309     /**
20310      * @cfg {Boolean} text Text to show on item.
20311      */
20312     text : '',
20313     
20314     /**
20315      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20316      */
20317     hideOnClick : false,
20318     /**
20319      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20320      */
20321     itemCls : "x-menu-text",
20322
20323     // private
20324     onRender : function(){
20325         var s = document.createElement("span");
20326         s.className = this.itemCls;
20327         s.innerHTML = this.text;
20328         this.el = s;
20329         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20330     }
20331 });/*
20332  * Based on:
20333  * Ext JS Library 1.1.1
20334  * Copyright(c) 2006-2007, Ext JS, LLC.
20335  *
20336  * Originally Released Under LGPL - original licence link has changed is not relivant.
20337  *
20338  * Fork - LGPL
20339  * <script type="text/javascript">
20340  */
20341
20342 /**
20343  * @class Roo.menu.Separator
20344  * @extends Roo.menu.BaseItem
20345  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20346  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20347  * @constructor
20348  * @param {Object} config Configuration options
20349  */
20350 Roo.menu.Separator = function(config){
20351     Roo.menu.Separator.superclass.constructor.call(this, config);
20352 };
20353
20354 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20355     /**
20356      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20357      */
20358     itemCls : "x-menu-sep",
20359     /**
20360      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20361      */
20362     hideOnClick : false,
20363
20364     // private
20365     onRender : function(li){
20366         var s = document.createElement("span");
20367         s.className = this.itemCls;
20368         s.innerHTML = "&#160;";
20369         this.el = s;
20370         li.addClass("x-menu-sep-li");
20371         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20372     }
20373 });/*
20374  * Based on:
20375  * Ext JS Library 1.1.1
20376  * Copyright(c) 2006-2007, Ext JS, LLC.
20377  *
20378  * Originally Released Under LGPL - original licence link has changed is not relivant.
20379  *
20380  * Fork - LGPL
20381  * <script type="text/javascript">
20382  */
20383 /**
20384  * @class Roo.menu.Item
20385  * @extends Roo.menu.BaseItem
20386  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20387  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20388  * activation and click handling.
20389  * @constructor
20390  * Creates a new Item
20391  * @param {Object} config Configuration options
20392  */
20393 Roo.menu.Item = function(config){
20394     Roo.menu.Item.superclass.constructor.call(this, config);
20395     if(this.menu){
20396         this.menu = Roo.menu.MenuMgr.get(this.menu);
20397     }
20398 };
20399 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20400     
20401     /**
20402      * @cfg {String} text
20403      * The text to show on the menu item.
20404      */
20405     text: '',
20406      /**
20407      * @cfg {String} HTML to render in menu
20408      * The text to show on the menu item (HTML version).
20409      */
20410     html: '',
20411     /**
20412      * @cfg {String} icon
20413      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20414      */
20415     icon: undefined,
20416     /**
20417      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20418      */
20419     itemCls : "x-menu-item",
20420     /**
20421      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20422      */
20423     canActivate : true,
20424     /**
20425      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20426      */
20427     showDelay: 200,
20428     // doc'd in BaseItem
20429     hideDelay: 200,
20430
20431     // private
20432     ctype: "Roo.menu.Item",
20433     
20434     // private
20435     onRender : function(container, position){
20436         var el = document.createElement("a");
20437         el.hideFocus = true;
20438         el.unselectable = "on";
20439         el.href = this.href || "#";
20440         if(this.hrefTarget){
20441             el.target = this.hrefTarget;
20442         }
20443         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20444         
20445         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20446         
20447         el.innerHTML = String.format(
20448                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20449                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20450         this.el = el;
20451         Roo.menu.Item.superclass.onRender.call(this, container, position);
20452     },
20453
20454     /**
20455      * Sets the text to display in this menu item
20456      * @param {String} text The text to display
20457      * @param {Boolean} isHTML true to indicate text is pure html.
20458      */
20459     setText : function(text, isHTML){
20460         if (isHTML) {
20461             this.html = text;
20462         } else {
20463             this.text = text;
20464             this.html = '';
20465         }
20466         if(this.rendered){
20467             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20468      
20469             this.el.update(String.format(
20470                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20471                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20472             this.parentMenu.autoWidth();
20473         }
20474     },
20475
20476     // private
20477     handleClick : function(e){
20478         if(!this.href){ // if no link defined, stop the event automatically
20479             e.stopEvent();
20480         }
20481         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20482     },
20483
20484     // private
20485     activate : function(autoExpand){
20486         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20487             this.focus();
20488             if(autoExpand){
20489                 this.expandMenu();
20490             }
20491         }
20492         return true;
20493     },
20494
20495     // private
20496     shouldDeactivate : function(e){
20497         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20498             if(this.menu && this.menu.isVisible()){
20499                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20500             }
20501             return true;
20502         }
20503         return false;
20504     },
20505
20506     // private
20507     deactivate : function(){
20508         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20509         this.hideMenu();
20510     },
20511
20512     // private
20513     expandMenu : function(autoActivate){
20514         if(!this.disabled && this.menu){
20515             clearTimeout(this.hideTimer);
20516             delete this.hideTimer;
20517             if(!this.menu.isVisible() && !this.showTimer){
20518                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20519             }else if (this.menu.isVisible() && autoActivate){
20520                 this.menu.tryActivate(0, 1);
20521             }
20522         }
20523     },
20524
20525     // private
20526     deferExpand : function(autoActivate){
20527         delete this.showTimer;
20528         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20529         if(autoActivate){
20530             this.menu.tryActivate(0, 1);
20531         }
20532     },
20533
20534     // private
20535     hideMenu : function(){
20536         clearTimeout(this.showTimer);
20537         delete this.showTimer;
20538         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20539             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20540         }
20541     },
20542
20543     // private
20544     deferHide : function(){
20545         delete this.hideTimer;
20546         this.menu.hide();
20547     }
20548 });/*
20549  * Based on:
20550  * Ext JS Library 1.1.1
20551  * Copyright(c) 2006-2007, Ext JS, LLC.
20552  *
20553  * Originally Released Under LGPL - original licence link has changed is not relivant.
20554  *
20555  * Fork - LGPL
20556  * <script type="text/javascript">
20557  */
20558  
20559 /**
20560  * @class Roo.menu.CheckItem
20561  * @extends Roo.menu.Item
20562  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20563  * @constructor
20564  * Creates a new CheckItem
20565  * @param {Object} config Configuration options
20566  */
20567 Roo.menu.CheckItem = function(config){
20568     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20569     this.addEvents({
20570         /**
20571          * @event beforecheckchange
20572          * Fires before the checked value is set, providing an opportunity to cancel if needed
20573          * @param {Roo.menu.CheckItem} this
20574          * @param {Boolean} checked The new checked value that will be set
20575          */
20576         "beforecheckchange" : true,
20577         /**
20578          * @event checkchange
20579          * Fires after the checked value has been set
20580          * @param {Roo.menu.CheckItem} this
20581          * @param {Boolean} checked The checked value that was set
20582          */
20583         "checkchange" : true
20584     });
20585     if(this.checkHandler){
20586         this.on('checkchange', this.checkHandler, this.scope);
20587     }
20588 };
20589 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20590     /**
20591      * @cfg {String} group
20592      * All check items with the same group name will automatically be grouped into a single-select
20593      * radio button group (defaults to '')
20594      */
20595     /**
20596      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20597      */
20598     itemCls : "x-menu-item x-menu-check-item",
20599     /**
20600      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20601      */
20602     groupClass : "x-menu-group-item",
20603
20604     /**
20605      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20606      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20607      * initialized with checked = true will be rendered as checked.
20608      */
20609     checked: false,
20610
20611     // private
20612     ctype: "Roo.menu.CheckItem",
20613
20614     // private
20615     onRender : function(c){
20616         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20617         if(this.group){
20618             this.el.addClass(this.groupClass);
20619         }
20620         Roo.menu.MenuMgr.registerCheckable(this);
20621         if(this.checked){
20622             this.checked = false;
20623             this.setChecked(true, true);
20624         }
20625     },
20626
20627     // private
20628     destroy : function(){
20629         if(this.rendered){
20630             Roo.menu.MenuMgr.unregisterCheckable(this);
20631         }
20632         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20633     },
20634
20635     /**
20636      * Set the checked state of this item
20637      * @param {Boolean} checked The new checked value
20638      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20639      */
20640     setChecked : function(state, suppressEvent){
20641         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20642             if(this.container){
20643                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20644             }
20645             this.checked = state;
20646             if(suppressEvent !== true){
20647                 this.fireEvent("checkchange", this, state);
20648             }
20649         }
20650     },
20651
20652     // private
20653     handleClick : function(e){
20654        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20655            this.setChecked(!this.checked);
20656        }
20657        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20658     }
20659 });/*
20660  * Based on:
20661  * Ext JS Library 1.1.1
20662  * Copyright(c) 2006-2007, Ext JS, LLC.
20663  *
20664  * Originally Released Under LGPL - original licence link has changed is not relivant.
20665  *
20666  * Fork - LGPL
20667  * <script type="text/javascript">
20668  */
20669  
20670 /**
20671  * @class Roo.menu.DateItem
20672  * @extends Roo.menu.Adapter
20673  * A menu item that wraps the {@link Roo.DatPicker} component.
20674  * @constructor
20675  * Creates a new DateItem
20676  * @param {Object} config Configuration options
20677  */
20678 Roo.menu.DateItem = function(config){
20679     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20680     /** The Roo.DatePicker object @type Roo.DatePicker */
20681     this.picker = this.component;
20682     this.addEvents({select: true});
20683     
20684     this.picker.on("render", function(picker){
20685         picker.getEl().swallowEvent("click");
20686         picker.container.addClass("x-menu-date-item");
20687     });
20688
20689     this.picker.on("select", this.onSelect, this);
20690 };
20691
20692 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20693     // private
20694     onSelect : function(picker, date){
20695         this.fireEvent("select", this, date, picker);
20696         Roo.menu.DateItem.superclass.handleClick.call(this);
20697     }
20698 });/*
20699  * Based on:
20700  * Ext JS Library 1.1.1
20701  * Copyright(c) 2006-2007, Ext JS, LLC.
20702  *
20703  * Originally Released Under LGPL - original licence link has changed is not relivant.
20704  *
20705  * Fork - LGPL
20706  * <script type="text/javascript">
20707  */
20708  
20709 /**
20710  * @class Roo.menu.ColorItem
20711  * @extends Roo.menu.Adapter
20712  * A menu item that wraps the {@link Roo.ColorPalette} component.
20713  * @constructor
20714  * Creates a new ColorItem
20715  * @param {Object} config Configuration options
20716  */
20717 Roo.menu.ColorItem = function(config){
20718     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20719     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20720     this.palette = this.component;
20721     this.relayEvents(this.palette, ["select"]);
20722     if(this.selectHandler){
20723         this.on('select', this.selectHandler, this.scope);
20724     }
20725 };
20726 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20727  * Based on:
20728  * Ext JS Library 1.1.1
20729  * Copyright(c) 2006-2007, Ext JS, LLC.
20730  *
20731  * Originally Released Under LGPL - original licence link has changed is not relivant.
20732  *
20733  * Fork - LGPL
20734  * <script type="text/javascript">
20735  */
20736  
20737
20738 /**
20739  * @class Roo.menu.DateMenu
20740  * @extends Roo.menu.Menu
20741  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20742  * @constructor
20743  * Creates a new DateMenu
20744  * @param {Object} config Configuration options
20745  */
20746 Roo.menu.DateMenu = function(config){
20747     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20748     this.plain = true;
20749     var di = new Roo.menu.DateItem(config);
20750     this.add(di);
20751     /**
20752      * The {@link Roo.DatePicker} instance for this DateMenu
20753      * @type DatePicker
20754      */
20755     this.picker = di.picker;
20756     /**
20757      * @event select
20758      * @param {DatePicker} picker
20759      * @param {Date} date
20760      */
20761     this.relayEvents(di, ["select"]);
20762
20763     this.on('beforeshow', function(){
20764         if(this.picker){
20765             this.picker.hideMonthPicker(true);
20766         }
20767     }, this);
20768 };
20769 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20770     cls:'x-date-menu'
20771 });/*
20772  * Based on:
20773  * Ext JS Library 1.1.1
20774  * Copyright(c) 2006-2007, Ext JS, LLC.
20775  *
20776  * Originally Released Under LGPL - original licence link has changed is not relivant.
20777  *
20778  * Fork - LGPL
20779  * <script type="text/javascript">
20780  */
20781  
20782
20783 /**
20784  * @class Roo.menu.ColorMenu
20785  * @extends Roo.menu.Menu
20786  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20787  * @constructor
20788  * Creates a new ColorMenu
20789  * @param {Object} config Configuration options
20790  */
20791 Roo.menu.ColorMenu = function(config){
20792     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20793     this.plain = true;
20794     var ci = new Roo.menu.ColorItem(config);
20795     this.add(ci);
20796     /**
20797      * The {@link Roo.ColorPalette} instance for this ColorMenu
20798      * @type ColorPalette
20799      */
20800     this.palette = ci.palette;
20801     /**
20802      * @event select
20803      * @param {ColorPalette} palette
20804      * @param {String} color
20805      */
20806     this.relayEvents(ci, ["select"]);
20807 };
20808 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20809  * Based on:
20810  * Ext JS Library 1.1.1
20811  * Copyright(c) 2006-2007, Ext JS, LLC.
20812  *
20813  * Originally Released Under LGPL - original licence link has changed is not relivant.
20814  *
20815  * Fork - LGPL
20816  * <script type="text/javascript">
20817  */
20818  
20819 /**
20820  * @class Roo.form.Field
20821  * @extends Roo.BoxComponent
20822  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20823  * @constructor
20824  * Creates a new Field
20825  * @param {Object} config Configuration options
20826  */
20827 Roo.form.Field = function(config){
20828     Roo.form.Field.superclass.constructor.call(this, config);
20829 };
20830
20831 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20832     /**
20833      * @cfg {String} fieldLabel Label to use when rendering a form.
20834      */
20835        /**
20836      * @cfg {String} qtip Mouse over tip
20837      */
20838      
20839     /**
20840      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20841      */
20842     invalidClass : "x-form-invalid",
20843     /**
20844      * @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")
20845      */
20846     invalidText : "The value in this field is invalid",
20847     /**
20848      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20849      */
20850     focusClass : "x-form-focus",
20851     /**
20852      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20853       automatic validation (defaults to "keyup").
20854      */
20855     validationEvent : "keyup",
20856     /**
20857      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20858      */
20859     validateOnBlur : true,
20860     /**
20861      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20862      */
20863     validationDelay : 250,
20864     /**
20865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20866      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20867      */
20868     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20869     /**
20870      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20871      */
20872     fieldClass : "x-form-field",
20873     /**
20874      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20875      *<pre>
20876 Value         Description
20877 -----------   ----------------------------------------------------------------------
20878 qtip          Display a quick tip when the user hovers over the field
20879 title         Display a default browser title attribute popup
20880 under         Add a block div beneath the field containing the error text
20881 side          Add an error icon to the right of the field with a popup on hover
20882 [element id]  Add the error text directly to the innerHTML of the specified element
20883 </pre>
20884      */
20885     msgTarget : 'qtip',
20886     /**
20887      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20888      */
20889     msgFx : 'normal',
20890
20891     /**
20892      * @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.
20893      */
20894     readOnly : false,
20895
20896     /**
20897      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20898      */
20899     disabled : false,
20900
20901     /**
20902      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20903      */
20904     inputType : undefined,
20905     
20906     /**
20907      * @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).
20908          */
20909         tabIndex : undefined,
20910         
20911     // private
20912     isFormField : true,
20913
20914     // private
20915     hasFocus : false,
20916     /**
20917      * @property {Roo.Element} fieldEl
20918      * Element Containing the rendered Field (with label etc.)
20919      */
20920     /**
20921      * @cfg {Mixed} value A value to initialize this field with.
20922      */
20923     value : undefined,
20924
20925     /**
20926      * @cfg {String} name The field's HTML name attribute.
20927      */
20928     /**
20929      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20930      */
20931
20932         // private ??
20933         initComponent : function(){
20934         Roo.form.Field.superclass.initComponent.call(this);
20935         this.addEvents({
20936             /**
20937              * @event focus
20938              * Fires when this field receives input focus.
20939              * @param {Roo.form.Field} this
20940              */
20941             focus : true,
20942             /**
20943              * @event blur
20944              * Fires when this field loses input focus.
20945              * @param {Roo.form.Field} this
20946              */
20947             blur : true,
20948             /**
20949              * @event specialkey
20950              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20951              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20952              * @param {Roo.form.Field} this
20953              * @param {Roo.EventObject} e The event object
20954              */
20955             specialkey : true,
20956             /**
20957              * @event change
20958              * Fires just before the field blurs if the field value has changed.
20959              * @param {Roo.form.Field} this
20960              * @param {Mixed} newValue The new value
20961              * @param {Mixed} oldValue The original value
20962              */
20963             change : true,
20964             /**
20965              * @event invalid
20966              * Fires after the field has been marked as invalid.
20967              * @param {Roo.form.Field} this
20968              * @param {String} msg The validation message
20969              */
20970             invalid : true,
20971             /**
20972              * @event valid
20973              * Fires after the field has been validated with no errors.
20974              * @param {Roo.form.Field} this
20975              */
20976             valid : true,
20977              /**
20978              * @event keyup
20979              * Fires after the key up
20980              * @param {Roo.form.Field} this
20981              * @param {Roo.EventObject}  e The event Object
20982              */
20983             keyup : true
20984         });
20985     },
20986
20987     /**
20988      * Returns the name attribute of the field if available
20989      * @return {String} name The field name
20990      */
20991     getName: function(){
20992          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20993     },
20994
20995     // private
20996     onRender : function(ct, position){
20997         Roo.form.Field.superclass.onRender.call(this, ct, position);
20998         if(!this.el){
20999             var cfg = this.getAutoCreate();
21000             if(!cfg.name){
21001                 cfg.name = this.name || this.id;
21002             }
21003             if(this.inputType){
21004                 cfg.type = this.inputType;
21005             }
21006             this.el = ct.createChild(cfg, position);
21007         }
21008         var type = this.el.dom.type;
21009         if(type){
21010             if(type == 'password'){
21011                 type = 'text';
21012             }
21013             this.el.addClass('x-form-'+type);
21014         }
21015         if(this.readOnly){
21016             this.el.dom.readOnly = true;
21017         }
21018         if(this.tabIndex !== undefined){
21019             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21020         }
21021
21022         this.el.addClass([this.fieldClass, this.cls]);
21023         this.initValue();
21024     },
21025
21026     /**
21027      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21028      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21029      * @return {Roo.form.Field} this
21030      */
21031     applyTo : function(target){
21032         this.allowDomMove = false;
21033         this.el = Roo.get(target);
21034         this.render(this.el.dom.parentNode);
21035         return this;
21036     },
21037
21038     // private
21039     initValue : function(){
21040         if(this.value !== undefined){
21041             this.setValue(this.value);
21042         }else if(this.el.dom.value.length > 0){
21043             this.setValue(this.el.dom.value);
21044         }
21045     },
21046
21047     /**
21048      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21049      */
21050     isDirty : function() {
21051         if(this.disabled) {
21052             return false;
21053         }
21054         return String(this.getValue()) !== String(this.originalValue);
21055     },
21056
21057     // private
21058     afterRender : function(){
21059         Roo.form.Field.superclass.afterRender.call(this);
21060         this.initEvents();
21061     },
21062
21063     // private
21064     fireKey : function(e){
21065         //Roo.log('field ' + e.getKey());
21066         if(e.isNavKeyPress()){
21067             this.fireEvent("specialkey", this, e);
21068         }
21069     },
21070
21071     /**
21072      * Resets the current field value to the originally loaded value and clears any validation messages
21073      */
21074     reset : function(){
21075         this.setValue(this.originalValue);
21076         this.clearInvalid();
21077     },
21078
21079     // private
21080     initEvents : function(){
21081         // safari killled keypress - so keydown is now used..
21082         this.el.on("keydown" , this.fireKey,  this);
21083         this.el.on("focus", this.onFocus,  this);
21084         this.el.on("blur", this.onBlur,  this);
21085         this.el.relayEvent('keyup', this);
21086
21087         // reference to original value for reset
21088         this.originalValue = this.getValue();
21089     },
21090
21091     // private
21092     onFocus : function(){
21093         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21094             this.el.addClass(this.focusClass);
21095         }
21096         if(!this.hasFocus){
21097             this.hasFocus = true;
21098             this.startValue = this.getValue();
21099             this.fireEvent("focus", this);
21100         }
21101     },
21102
21103     beforeBlur : Roo.emptyFn,
21104
21105     // private
21106     onBlur : function(){
21107         this.beforeBlur();
21108         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21109             this.el.removeClass(this.focusClass);
21110         }
21111         this.hasFocus = false;
21112         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21113             this.validate();
21114         }
21115         var v = this.getValue();
21116         if(String(v) !== String(this.startValue)){
21117             this.fireEvent('change', this, v, this.startValue);
21118         }
21119         this.fireEvent("blur", this);
21120     },
21121
21122     /**
21123      * Returns whether or not the field value is currently valid
21124      * @param {Boolean} preventMark True to disable marking the field invalid
21125      * @return {Boolean} True if the value is valid, else false
21126      */
21127     isValid : function(preventMark){
21128         if(this.disabled){
21129             return true;
21130         }
21131         var restore = this.preventMark;
21132         this.preventMark = preventMark === true;
21133         var v = this.validateValue(this.processValue(this.getRawValue()));
21134         this.preventMark = restore;
21135         return v;
21136     },
21137
21138     /**
21139      * Validates the field value
21140      * @return {Boolean} True if the value is valid, else false
21141      */
21142     validate : function(){
21143         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21144             this.clearInvalid();
21145             return true;
21146         }
21147         return false;
21148     },
21149
21150     processValue : function(value){
21151         return value;
21152     },
21153
21154     // private
21155     // Subclasses should provide the validation implementation by overriding this
21156     validateValue : function(value){
21157         return true;
21158     },
21159
21160     /**
21161      * Mark this field as invalid
21162      * @param {String} msg The validation message
21163      */
21164     markInvalid : function(msg){
21165         if(!this.rendered || this.preventMark){ // not rendered
21166             return;
21167         }
21168         this.el.addClass(this.invalidClass);
21169         msg = msg || this.invalidText;
21170         switch(this.msgTarget){
21171             case 'qtip':
21172                 this.el.dom.qtip = msg;
21173                 this.el.dom.qclass = 'x-form-invalid-tip';
21174                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21175                     Roo.QuickTips.enable();
21176                 }
21177                 break;
21178             case 'title':
21179                 this.el.dom.title = msg;
21180                 break;
21181             case 'under':
21182                 if(!this.errorEl){
21183                     var elp = this.el.findParent('.x-form-element', 5, true);
21184                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21185                     this.errorEl.setWidth(elp.getWidth(true)-20);
21186                 }
21187                 this.errorEl.update(msg);
21188                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21189                 break;
21190             case 'side':
21191                 if(!this.errorIcon){
21192                     var elp = this.el.findParent('.x-form-element', 5, true);
21193                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21194                 }
21195                 this.alignErrorIcon();
21196                 this.errorIcon.dom.qtip = msg;
21197                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21198                 this.errorIcon.show();
21199                 this.on('resize', this.alignErrorIcon, this);
21200                 break;
21201             default:
21202                 var t = Roo.getDom(this.msgTarget);
21203                 t.innerHTML = msg;
21204                 t.style.display = this.msgDisplay;
21205                 break;
21206         }
21207         this.fireEvent('invalid', this, msg);
21208     },
21209
21210     // private
21211     alignErrorIcon : function(){
21212         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21213     },
21214
21215     /**
21216      * Clear any invalid styles/messages for this field
21217      */
21218     clearInvalid : function(){
21219         if(!this.rendered || this.preventMark){ // not rendered
21220             return;
21221         }
21222         this.el.removeClass(this.invalidClass);
21223         switch(this.msgTarget){
21224             case 'qtip':
21225                 this.el.dom.qtip = '';
21226                 break;
21227             case 'title':
21228                 this.el.dom.title = '';
21229                 break;
21230             case 'under':
21231                 if(this.errorEl){
21232                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21233                 }
21234                 break;
21235             case 'side':
21236                 if(this.errorIcon){
21237                     this.errorIcon.dom.qtip = '';
21238                     this.errorIcon.hide();
21239                     this.un('resize', this.alignErrorIcon, this);
21240                 }
21241                 break;
21242             default:
21243                 var t = Roo.getDom(this.msgTarget);
21244                 t.innerHTML = '';
21245                 t.style.display = 'none';
21246                 break;
21247         }
21248         this.fireEvent('valid', this);
21249     },
21250
21251     /**
21252      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21253      * @return {Mixed} value The field value
21254      */
21255     getRawValue : function(){
21256         var v = this.el.getValue();
21257         if(v === this.emptyText){
21258             v = '';
21259         }
21260         return v;
21261     },
21262
21263     /**
21264      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21265      * @return {Mixed} value The field value
21266      */
21267     getValue : function(){
21268         var v = this.el.getValue();
21269         if(v === this.emptyText || v === undefined){
21270             v = '';
21271         }
21272         return v;
21273     },
21274
21275     /**
21276      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21277      * @param {Mixed} value The value to set
21278      */
21279     setRawValue : function(v){
21280         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21281     },
21282
21283     /**
21284      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21285      * @param {Mixed} value The value to set
21286      */
21287     setValue : function(v){
21288         this.value = v;
21289         if(this.rendered){
21290             this.el.dom.value = (v === null || v === undefined ? '' : v);
21291             this.validate();
21292         }
21293     },
21294
21295     adjustSize : function(w, h){
21296         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21297         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21298         return s;
21299     },
21300
21301     adjustWidth : function(tag, w){
21302         tag = tag.toLowerCase();
21303         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21304             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21305                 if(tag == 'input'){
21306                     return w + 2;
21307                 }
21308                 if(tag = 'textarea'){
21309                     return w-2;
21310                 }
21311             }else if(Roo.isOpera){
21312                 if(tag == 'input'){
21313                     return w + 2;
21314                 }
21315                 if(tag = 'textarea'){
21316                     return w-2;
21317                 }
21318             }
21319         }
21320         return w;
21321     }
21322 });
21323
21324
21325 // anything other than normal should be considered experimental
21326 Roo.form.Field.msgFx = {
21327     normal : {
21328         show: function(msgEl, f){
21329             msgEl.setDisplayed('block');
21330         },
21331
21332         hide : function(msgEl, f){
21333             msgEl.setDisplayed(false).update('');
21334         }
21335     },
21336
21337     slide : {
21338         show: function(msgEl, f){
21339             msgEl.slideIn('t', {stopFx:true});
21340         },
21341
21342         hide : function(msgEl, f){
21343             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21344         }
21345     },
21346
21347     slideRight : {
21348         show: function(msgEl, f){
21349             msgEl.fixDisplay();
21350             msgEl.alignTo(f.el, 'tl-tr');
21351             msgEl.slideIn('l', {stopFx:true});
21352         },
21353
21354         hide : function(msgEl, f){
21355             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21356         }
21357     }
21358 };/*
21359  * Based on:
21360  * Ext JS Library 1.1.1
21361  * Copyright(c) 2006-2007, Ext JS, LLC.
21362  *
21363  * Originally Released Under LGPL - original licence link has changed is not relivant.
21364  *
21365  * Fork - LGPL
21366  * <script type="text/javascript">
21367  */
21368  
21369
21370 /**
21371  * @class Roo.form.TextField
21372  * @extends Roo.form.Field
21373  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21374  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21375  * @constructor
21376  * Creates a new TextField
21377  * @param {Object} config Configuration options
21378  */
21379 Roo.form.TextField = function(config){
21380     Roo.form.TextField.superclass.constructor.call(this, config);
21381     this.addEvents({
21382         /**
21383          * @event autosize
21384          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21385          * according to the default logic, but this event provides a hook for the developer to apply additional
21386          * logic at runtime to resize the field if needed.
21387              * @param {Roo.form.Field} this This text field
21388              * @param {Number} width The new field width
21389              */
21390         autosize : true
21391     });
21392 };
21393
21394 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21395     /**
21396      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21397      */
21398     grow : false,
21399     /**
21400      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21401      */
21402     growMin : 30,
21403     /**
21404      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21405      */
21406     growMax : 800,
21407     /**
21408      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21409      */
21410     vtype : null,
21411     /**
21412      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21413      */
21414     maskRe : null,
21415     /**
21416      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21417      */
21418     disableKeyFilter : false,
21419     /**
21420      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21421      */
21422     allowBlank : true,
21423     /**
21424      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21425      */
21426     minLength : 0,
21427     /**
21428      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21429      */
21430     maxLength : Number.MAX_VALUE,
21431     /**
21432      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21433      */
21434     minLengthText : "The minimum length for this field is {0}",
21435     /**
21436      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21437      */
21438     maxLengthText : "The maximum length for this field is {0}",
21439     /**
21440      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21441      */
21442     selectOnFocus : false,
21443     /**
21444      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21445      */
21446     blankText : "This field is required",
21447     /**
21448      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21449      * If available, this function will be called only after the basic validators all return true, and will be passed the
21450      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21451      */
21452     validator : null,
21453     /**
21454      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21455      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21456      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21457      */
21458     regex : null,
21459     /**
21460      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21461      */
21462     regexText : "",
21463     /**
21464      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21465      */
21466     emptyText : null,
21467     /**
21468      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21469      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21470      */
21471     emptyClass : 'x-form-empty-field',
21472
21473     // private
21474     initEvents : function(){
21475         Roo.form.TextField.superclass.initEvents.call(this);
21476         if(this.validationEvent == 'keyup'){
21477             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21478             this.el.on('keyup', this.filterValidation, this);
21479         }
21480         else if(this.validationEvent !== false){
21481             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21482         }
21483         if(this.selectOnFocus || this.emptyText){
21484             this.on("focus", this.preFocus, this);
21485             if(this.emptyText){
21486                 this.on('blur', this.postBlur, this);
21487                 this.applyEmptyText();
21488             }
21489         }
21490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21491             this.el.on("keypress", this.filterKeys, this);
21492         }
21493         if(this.grow){
21494             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21495             this.el.on("click", this.autoSize,  this);
21496         }
21497     },
21498
21499     processValue : function(value){
21500         if(this.stripCharsRe){
21501             var newValue = value.replace(this.stripCharsRe, '');
21502             if(newValue !== value){
21503                 this.setRawValue(newValue);
21504                 return newValue;
21505             }
21506         }
21507         return value;
21508     },
21509
21510     filterValidation : function(e){
21511         if(!e.isNavKeyPress()){
21512             this.validationTask.delay(this.validationDelay);
21513         }
21514     },
21515
21516     // private
21517     onKeyUp : function(e){
21518         if(!e.isNavKeyPress()){
21519             this.autoSize();
21520         }
21521     },
21522
21523     /**
21524      * Resets the current field value to the originally-loaded value and clears any validation messages.
21525      * Also adds emptyText and emptyClass if the original value was blank.
21526      */
21527     reset : function(){
21528         Roo.form.TextField.superclass.reset.call(this);
21529         this.applyEmptyText();
21530     },
21531
21532     applyEmptyText : function(){
21533         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21534             this.setRawValue(this.emptyText);
21535             this.el.addClass(this.emptyClass);
21536         }
21537     },
21538
21539     // private
21540     preFocus : function(){
21541         if(this.emptyText){
21542             if(this.el.dom.value == this.emptyText){
21543                 this.setRawValue('');
21544             }
21545             this.el.removeClass(this.emptyClass);
21546         }
21547         if(this.selectOnFocus){
21548             this.el.dom.select();
21549         }
21550     },
21551
21552     // private
21553     postBlur : function(){
21554         this.applyEmptyText();
21555     },
21556
21557     // private
21558     filterKeys : function(e){
21559         var k = e.getKey();
21560         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21561             return;
21562         }
21563         var c = e.getCharCode(), cc = String.fromCharCode(c);
21564         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21565             return;
21566         }
21567         if(!this.maskRe.test(cc)){
21568             e.stopEvent();
21569         }
21570     },
21571
21572     setValue : function(v){
21573         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21574             this.el.removeClass(this.emptyClass);
21575         }
21576         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21577         this.applyEmptyText();
21578         this.autoSize();
21579     },
21580
21581     /**
21582      * Validates a value according to the field's validation rules and marks the field as invalid
21583      * if the validation fails
21584      * @param {Mixed} value The value to validate
21585      * @return {Boolean} True if the value is valid, else false
21586      */
21587     validateValue : function(value){
21588         if(value.length < 1 || value === this.emptyText){ // if it's blank
21589              if(this.allowBlank){
21590                 this.clearInvalid();
21591                 return true;
21592              }else{
21593                 this.markInvalid(this.blankText);
21594                 return false;
21595              }
21596         }
21597         if(value.length < this.minLength){
21598             this.markInvalid(String.format(this.minLengthText, this.minLength));
21599             return false;
21600         }
21601         if(value.length > this.maxLength){
21602             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21603             return false;
21604         }
21605         if(this.vtype){
21606             var vt = Roo.form.VTypes;
21607             if(!vt[this.vtype](value, this)){
21608                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21609                 return false;
21610             }
21611         }
21612         if(typeof this.validator == "function"){
21613             var msg = this.validator(value);
21614             if(msg !== true){
21615                 this.markInvalid(msg);
21616                 return false;
21617             }
21618         }
21619         if(this.regex && !this.regex.test(value)){
21620             this.markInvalid(this.regexText);
21621             return false;
21622         }
21623         return true;
21624     },
21625
21626     /**
21627      * Selects text in this field
21628      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21629      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21630      */
21631     selectText : function(start, end){
21632         var v = this.getRawValue();
21633         if(v.length > 0){
21634             start = start === undefined ? 0 : start;
21635             end = end === undefined ? v.length : end;
21636             var d = this.el.dom;
21637             if(d.setSelectionRange){
21638                 d.setSelectionRange(start, end);
21639             }else if(d.createTextRange){
21640                 var range = d.createTextRange();
21641                 range.moveStart("character", start);
21642                 range.moveEnd("character", v.length-end);
21643                 range.select();
21644             }
21645         }
21646     },
21647
21648     /**
21649      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21650      * This only takes effect if grow = true, and fires the autosize event.
21651      */
21652     autoSize : function(){
21653         if(!this.grow || !this.rendered){
21654             return;
21655         }
21656         if(!this.metrics){
21657             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21658         }
21659         var el = this.el;
21660         var v = el.dom.value;
21661         var d = document.createElement('div');
21662         d.appendChild(document.createTextNode(v));
21663         v = d.innerHTML;
21664         d = null;
21665         v += "&#160;";
21666         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21667         this.el.setWidth(w);
21668         this.fireEvent("autosize", this, w);
21669     }
21670 });/*
21671  * Based on:
21672  * Ext JS Library 1.1.1
21673  * Copyright(c) 2006-2007, Ext JS, LLC.
21674  *
21675  * Originally Released Under LGPL - original licence link has changed is not relivant.
21676  *
21677  * Fork - LGPL
21678  * <script type="text/javascript">
21679  */
21680  
21681 /**
21682  * @class Roo.form.Hidden
21683  * @extends Roo.form.TextField
21684  * Simple Hidden element used on forms 
21685  * 
21686  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21687  * 
21688  * @constructor
21689  * Creates a new Hidden form element.
21690  * @param {Object} config Configuration options
21691  */
21692
21693
21694
21695 // easy hidden field...
21696 Roo.form.Hidden = function(config){
21697     Roo.form.Hidden.superclass.constructor.call(this, config);
21698 };
21699   
21700 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21701     fieldLabel:      '',
21702     inputType:      'hidden',
21703     width:          50,
21704     allowBlank:     true,
21705     labelSeparator: '',
21706     hidden:         true,
21707     itemCls :       'x-form-item-display-none'
21708
21709
21710 });
21711
21712
21713 /*
21714  * Based on:
21715  * Ext JS Library 1.1.1
21716  * Copyright(c) 2006-2007, Ext JS, LLC.
21717  *
21718  * Originally Released Under LGPL - original licence link has changed is not relivant.
21719  *
21720  * Fork - LGPL
21721  * <script type="text/javascript">
21722  */
21723  
21724 /**
21725  * @class Roo.form.TriggerField
21726  * @extends Roo.form.TextField
21727  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21728  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21729  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21730  * for which you can provide a custom implementation.  For example:
21731  * <pre><code>
21732 var trigger = new Roo.form.TriggerField();
21733 trigger.onTriggerClick = myTriggerFn;
21734 trigger.applyTo('my-field');
21735 </code></pre>
21736  *
21737  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21738  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21739  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21740  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21741  * @constructor
21742  * Create a new TriggerField.
21743  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21744  * to the base TextField)
21745  */
21746 Roo.form.TriggerField = function(config){
21747     this.mimicing = false;
21748     Roo.form.TriggerField.superclass.constructor.call(this, config);
21749 };
21750
21751 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21752     /**
21753      * @cfg {String} triggerClass A CSS class to apply to the trigger
21754      */
21755     /**
21756      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21757      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21758      */
21759     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21760     /**
21761      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21762      */
21763     hideTrigger:false,
21764
21765     /** @cfg {Boolean} grow @hide */
21766     /** @cfg {Number} growMin @hide */
21767     /** @cfg {Number} growMax @hide */
21768
21769     /**
21770      * @hide 
21771      * @method
21772      */
21773     autoSize: Roo.emptyFn,
21774     // private
21775     monitorTab : true,
21776     // private
21777     deferHeight : true,
21778
21779     
21780     actionMode : 'wrap',
21781     // private
21782     onResize : function(w, h){
21783         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21784         if(typeof w == 'number'){
21785             var x = w - this.trigger.getWidth();
21786             this.el.setWidth(this.adjustWidth('input', x));
21787             this.trigger.setStyle('left', x+'px');
21788         }
21789     },
21790
21791     // private
21792     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21793
21794     // private
21795     getResizeEl : function(){
21796         return this.wrap;
21797     },
21798
21799     // private
21800     getPositionEl : function(){
21801         return this.wrap;
21802     },
21803
21804     // private
21805     alignErrorIcon : function(){
21806         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21807     },
21808
21809     // private
21810     onRender : function(ct, position){
21811         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21812         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21813         this.trigger = this.wrap.createChild(this.triggerConfig ||
21814                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21815         if(this.hideTrigger){
21816             this.trigger.setDisplayed(false);
21817         }
21818         this.initTrigger();
21819         if(!this.width){
21820             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21821         }
21822     },
21823
21824     // private
21825     initTrigger : function(){
21826         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21827         this.trigger.addClassOnOver('x-form-trigger-over');
21828         this.trigger.addClassOnClick('x-form-trigger-click');
21829     },
21830
21831     // private
21832     onDestroy : function(){
21833         if(this.trigger){
21834             this.trigger.removeAllListeners();
21835             this.trigger.remove();
21836         }
21837         if(this.wrap){
21838             this.wrap.remove();
21839         }
21840         Roo.form.TriggerField.superclass.onDestroy.call(this);
21841     },
21842
21843     // private
21844     onFocus : function(){
21845         Roo.form.TriggerField.superclass.onFocus.call(this);
21846         if(!this.mimicing){
21847             this.wrap.addClass('x-trigger-wrap-focus');
21848             this.mimicing = true;
21849             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21850             if(this.monitorTab){
21851                 this.el.on("keydown", this.checkTab, this);
21852             }
21853         }
21854     },
21855
21856     // private
21857     checkTab : function(e){
21858         if(e.getKey() == e.TAB){
21859             this.triggerBlur();
21860         }
21861     },
21862
21863     // private
21864     onBlur : function(){
21865         // do nothing
21866     },
21867
21868     // private
21869     mimicBlur : function(e, t){
21870         if(!this.wrap.contains(t) && this.validateBlur()){
21871             this.triggerBlur();
21872         }
21873     },
21874
21875     // private
21876     triggerBlur : function(){
21877         this.mimicing = false;
21878         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21879         if(this.monitorTab){
21880             this.el.un("keydown", this.checkTab, this);
21881         }
21882         this.wrap.removeClass('x-trigger-wrap-focus');
21883         Roo.form.TriggerField.superclass.onBlur.call(this);
21884     },
21885
21886     // private
21887     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21888     validateBlur : function(e, t){
21889         return true;
21890     },
21891
21892     // private
21893     onDisable : function(){
21894         Roo.form.TriggerField.superclass.onDisable.call(this);
21895         if(this.wrap){
21896             this.wrap.addClass('x-item-disabled');
21897         }
21898     },
21899
21900     // private
21901     onEnable : function(){
21902         Roo.form.TriggerField.superclass.onEnable.call(this);
21903         if(this.wrap){
21904             this.wrap.removeClass('x-item-disabled');
21905         }
21906     },
21907
21908     // private
21909     onShow : function(){
21910         var ae = this.getActionEl();
21911         
21912         if(ae){
21913             ae.dom.style.display = '';
21914             ae.dom.style.visibility = 'visible';
21915         }
21916     },
21917
21918     // private
21919     
21920     onHide : function(){
21921         var ae = this.getActionEl();
21922         ae.dom.style.display = 'none';
21923     },
21924
21925     /**
21926      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21927      * by an implementing function.
21928      * @method
21929      * @param {EventObject} e
21930      */
21931     onTriggerClick : Roo.emptyFn
21932 });
21933
21934 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21935 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21936 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21937 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21938     initComponent : function(){
21939         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21940
21941         this.triggerConfig = {
21942             tag:'span', cls:'x-form-twin-triggers', cn:[
21943             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21944             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21945         ]};
21946     },
21947
21948     getTrigger : function(index){
21949         return this.triggers[index];
21950     },
21951
21952     initTrigger : function(){
21953         var ts = this.trigger.select('.x-form-trigger', true);
21954         this.wrap.setStyle('overflow', 'hidden');
21955         var triggerField = this;
21956         ts.each(function(t, all, index){
21957             t.hide = function(){
21958                 var w = triggerField.wrap.getWidth();
21959                 this.dom.style.display = 'none';
21960                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21961             };
21962             t.show = function(){
21963                 var w = triggerField.wrap.getWidth();
21964                 this.dom.style.display = '';
21965                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21966             };
21967             var triggerIndex = 'Trigger'+(index+1);
21968
21969             if(this['hide'+triggerIndex]){
21970                 t.dom.style.display = 'none';
21971             }
21972             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21973             t.addClassOnOver('x-form-trigger-over');
21974             t.addClassOnClick('x-form-trigger-click');
21975         }, this);
21976         this.triggers = ts.elements;
21977     },
21978
21979     onTrigger1Click : Roo.emptyFn,
21980     onTrigger2Click : Roo.emptyFn
21981 });/*
21982  * Based on:
21983  * Ext JS Library 1.1.1
21984  * Copyright(c) 2006-2007, Ext JS, LLC.
21985  *
21986  * Originally Released Under LGPL - original licence link has changed is not relivant.
21987  *
21988  * Fork - LGPL
21989  * <script type="text/javascript">
21990  */
21991  
21992 /**
21993  * @class Roo.form.TextArea
21994  * @extends Roo.form.TextField
21995  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21996  * support for auto-sizing.
21997  * @constructor
21998  * Creates a new TextArea
21999  * @param {Object} config Configuration options
22000  */
22001 Roo.form.TextArea = function(config){
22002     Roo.form.TextArea.superclass.constructor.call(this, config);
22003     // these are provided exchanges for backwards compat
22004     // minHeight/maxHeight were replaced by growMin/growMax to be
22005     // compatible with TextField growing config values
22006     if(this.minHeight !== undefined){
22007         this.growMin = this.minHeight;
22008     }
22009     if(this.maxHeight !== undefined){
22010         this.growMax = this.maxHeight;
22011     }
22012 };
22013
22014 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22015     /**
22016      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22017      */
22018     growMin : 60,
22019     /**
22020      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22021      */
22022     growMax: 1000,
22023     /**
22024      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22025      * in the field (equivalent to setting overflow: hidden, defaults to false)
22026      */
22027     preventScrollbars: false,
22028     /**
22029      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22030      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22031      */
22032
22033     // private
22034     onRender : function(ct, position){
22035         if(!this.el){
22036             this.defaultAutoCreate = {
22037                 tag: "textarea",
22038                 style:"width:300px;height:60px;",
22039                 autocomplete: "off"
22040             };
22041         }
22042         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22043         if(this.grow){
22044             this.textSizeEl = Roo.DomHelper.append(document.body, {
22045                 tag: "pre", cls: "x-form-grow-sizer"
22046             });
22047             if(this.preventScrollbars){
22048                 this.el.setStyle("overflow", "hidden");
22049             }
22050             this.el.setHeight(this.growMin);
22051         }
22052     },
22053
22054     onDestroy : function(){
22055         if(this.textSizeEl){
22056             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22057         }
22058         Roo.form.TextArea.superclass.onDestroy.call(this);
22059     },
22060
22061     // private
22062     onKeyUp : function(e){
22063         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22064             this.autoSize();
22065         }
22066     },
22067
22068     /**
22069      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22070      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22071      */
22072     autoSize : function(){
22073         if(!this.grow || !this.textSizeEl){
22074             return;
22075         }
22076         var el = this.el;
22077         var v = el.dom.value;
22078         var ts = this.textSizeEl;
22079
22080         ts.innerHTML = '';
22081         ts.appendChild(document.createTextNode(v));
22082         v = ts.innerHTML;
22083
22084         Roo.fly(ts).setWidth(this.el.getWidth());
22085         if(v.length < 1){
22086             v = "&#160;&#160;";
22087         }else{
22088             if(Roo.isIE){
22089                 v = v.replace(/\n/g, '<p>&#160;</p>');
22090             }
22091             v += "&#160;\n&#160;";
22092         }
22093         ts.innerHTML = v;
22094         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22095         if(h != this.lastHeight){
22096             this.lastHeight = h;
22097             this.el.setHeight(h);
22098             this.fireEvent("autosize", this, h);
22099         }
22100     }
22101 });/*
22102  * Based on:
22103  * Ext JS Library 1.1.1
22104  * Copyright(c) 2006-2007, Ext JS, LLC.
22105  *
22106  * Originally Released Under LGPL - original licence link has changed is not relivant.
22107  *
22108  * Fork - LGPL
22109  * <script type="text/javascript">
22110  */
22111  
22112
22113 /**
22114  * @class Roo.form.NumberField
22115  * @extends Roo.form.TextField
22116  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22117  * @constructor
22118  * Creates a new NumberField
22119  * @param {Object} config Configuration options
22120  */
22121 Roo.form.NumberField = function(config){
22122     Roo.form.NumberField.superclass.constructor.call(this, config);
22123 };
22124
22125 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22126     /**
22127      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22128      */
22129     fieldClass: "x-form-field x-form-num-field",
22130     /**
22131      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22132      */
22133     allowDecimals : true,
22134     /**
22135      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22136      */
22137     decimalSeparator : ".",
22138     /**
22139      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22140      */
22141     decimalPrecision : 2,
22142     /**
22143      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22144      */
22145     allowNegative : true,
22146     /**
22147      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22148      */
22149     minValue : Number.NEGATIVE_INFINITY,
22150     /**
22151      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22152      */
22153     maxValue : Number.MAX_VALUE,
22154     /**
22155      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22156      */
22157     minText : "The minimum value for this field is {0}",
22158     /**
22159      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22160      */
22161     maxText : "The maximum value for this field is {0}",
22162     /**
22163      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22164      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22165      */
22166     nanText : "{0} is not a valid number",
22167
22168     // private
22169     initEvents : function(){
22170         Roo.form.NumberField.superclass.initEvents.call(this);
22171         var allowed = "0123456789";
22172         if(this.allowDecimals){
22173             allowed += this.decimalSeparator;
22174         }
22175         if(this.allowNegative){
22176             allowed += "-";
22177         }
22178         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22179         var keyPress = function(e){
22180             var k = e.getKey();
22181             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22182                 return;
22183             }
22184             var c = e.getCharCode();
22185             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22186                 e.stopEvent();
22187             }
22188         };
22189         this.el.on("keypress", keyPress, this);
22190     },
22191
22192     // private
22193     validateValue : function(value){
22194         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22195             return false;
22196         }
22197         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22198              return true;
22199         }
22200         var num = this.parseValue(value);
22201         if(isNaN(num)){
22202             this.markInvalid(String.format(this.nanText, value));
22203             return false;
22204         }
22205         if(num < this.minValue){
22206             this.markInvalid(String.format(this.minText, this.minValue));
22207             return false;
22208         }
22209         if(num > this.maxValue){
22210             this.markInvalid(String.format(this.maxText, this.maxValue));
22211             return false;
22212         }
22213         return true;
22214     },
22215
22216     getValue : function(){
22217         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22218     },
22219
22220     // private
22221     parseValue : function(value){
22222         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22223         return isNaN(value) ? '' : value;
22224     },
22225
22226     // private
22227     fixPrecision : function(value){
22228         var nan = isNaN(value);
22229         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22230             return nan ? '' : value;
22231         }
22232         return parseFloat(value).toFixed(this.decimalPrecision);
22233     },
22234
22235     setValue : function(v){
22236         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22237     },
22238
22239     // private
22240     decimalPrecisionFcn : function(v){
22241         return Math.floor(v);
22242     },
22243
22244     beforeBlur : function(){
22245         var v = this.parseValue(this.getRawValue());
22246         if(v){
22247             this.setValue(this.fixPrecision(v));
22248         }
22249     }
22250 });/*
22251  * Based on:
22252  * Ext JS Library 1.1.1
22253  * Copyright(c) 2006-2007, Ext JS, LLC.
22254  *
22255  * Originally Released Under LGPL - original licence link has changed is not relivant.
22256  *
22257  * Fork - LGPL
22258  * <script type="text/javascript">
22259  */
22260  
22261 /**
22262  * @class Roo.form.DateField
22263  * @extends Roo.form.TriggerField
22264  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22265 * @constructor
22266 * Create a new DateField
22267 * @param {Object} config
22268  */
22269 Roo.form.DateField = function(config){
22270     Roo.form.DateField.superclass.constructor.call(this, config);
22271     
22272       this.addEvents({
22273          
22274         /**
22275          * @event select
22276          * Fires when a date is selected
22277              * @param {Roo.form.DateField} combo This combo box
22278              * @param {Date} date The date selected
22279              */
22280         'select' : true
22281          
22282     });
22283     
22284     
22285     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22286     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22287     this.ddMatch = null;
22288     if(this.disabledDates){
22289         var dd = this.disabledDates;
22290         var re = "(?:";
22291         for(var i = 0; i < dd.length; i++){
22292             re += dd[i];
22293             if(i != dd.length-1) re += "|";
22294         }
22295         this.ddMatch = new RegExp(re + ")");
22296     }
22297 };
22298
22299 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22300     /**
22301      * @cfg {String} format
22302      * The default date format string which can be overriden for localization support.  The format must be
22303      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22304      */
22305     format : "m/d/y",
22306     /**
22307      * @cfg {String} altFormats
22308      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22309      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22310      */
22311     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22312     /**
22313      * @cfg {Array} disabledDays
22314      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22315      */
22316     disabledDays : null,
22317     /**
22318      * @cfg {String} disabledDaysText
22319      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22320      */
22321     disabledDaysText : "Disabled",
22322     /**
22323      * @cfg {Array} disabledDates
22324      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22325      * expression so they are very powerful. Some examples:
22326      * <ul>
22327      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22328      * <li>["03/08", "09/16"] would disable those days for every year</li>
22329      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22330      * <li>["03/../2006"] would disable every day in March 2006</li>
22331      * <li>["^03"] would disable every day in every March</li>
22332      * </ul>
22333      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22334      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22335      */
22336     disabledDates : null,
22337     /**
22338      * @cfg {String} disabledDatesText
22339      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22340      */
22341     disabledDatesText : "Disabled",
22342     /**
22343      * @cfg {Date/String} minValue
22344      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22345      * valid format (defaults to null).
22346      */
22347     minValue : null,
22348     /**
22349      * @cfg {Date/String} maxValue
22350      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22351      * valid format (defaults to null).
22352      */
22353     maxValue : null,
22354     /**
22355      * @cfg {String} minText
22356      * The error text to display when the date in the cell is before minValue (defaults to
22357      * 'The date in this field must be after {minValue}').
22358      */
22359     minText : "The date in this field must be equal to or after {0}",
22360     /**
22361      * @cfg {String} maxText
22362      * The error text to display when the date in the cell is after maxValue (defaults to
22363      * 'The date in this field must be before {maxValue}').
22364      */
22365     maxText : "The date in this field must be equal to or before {0}",
22366     /**
22367      * @cfg {String} invalidText
22368      * The error text to display when the date in the field is invalid (defaults to
22369      * '{value} is not a valid date - it must be in the format {format}').
22370      */
22371     invalidText : "{0} is not a valid date - it must be in the format {1}",
22372     /**
22373      * @cfg {String} triggerClass
22374      * An additional CSS class used to style the trigger button.  The trigger will always get the
22375      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22376      * which displays a calendar icon).
22377      */
22378     triggerClass : 'x-form-date-trigger',
22379     
22380
22381     /**
22382      * @cfg {bool} useIso
22383      * if enabled, then the date field will use a hidden field to store the 
22384      * real value as iso formated date. default (false)
22385      */ 
22386     useIso : false,
22387     /**
22388      * @cfg {String/Object} autoCreate
22389      * A DomHelper element spec, or true for a default element spec (defaults to
22390      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22391      */ 
22392     // private
22393     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22394     
22395     // private
22396     hiddenField: false,
22397     
22398     onRender : function(ct, position)
22399     {
22400         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22401         if (this.useIso) {
22402             this.el.dom.removeAttribute('name'); 
22403             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22404                     'before', true);
22405             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22406             // prevent input submission
22407             this.hiddenName = this.name;
22408         }
22409             
22410             
22411     },
22412     
22413     // private
22414     validateValue : function(value)
22415     {
22416         value = this.formatDate(value);
22417         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22418             return false;
22419         }
22420         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22421              return true;
22422         }
22423         var svalue = value;
22424         value = this.parseDate(value);
22425         if(!value){
22426             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22427             return false;
22428         }
22429         var time = value.getTime();
22430         if(this.minValue && time < this.minValue.getTime()){
22431             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22432             return false;
22433         }
22434         if(this.maxValue && time > this.maxValue.getTime()){
22435             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22436             return false;
22437         }
22438         if(this.disabledDays){
22439             var day = value.getDay();
22440             for(var i = 0; i < this.disabledDays.length; i++) {
22441                 if(day === this.disabledDays[i]){
22442                     this.markInvalid(this.disabledDaysText);
22443                     return false;
22444                 }
22445             }
22446         }
22447         var fvalue = this.formatDate(value);
22448         if(this.ddMatch && this.ddMatch.test(fvalue)){
22449             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22450             return false;
22451         }
22452         return true;
22453     },
22454
22455     // private
22456     // Provides logic to override the default TriggerField.validateBlur which just returns true
22457     validateBlur : function(){
22458         return !this.menu || !this.menu.isVisible();
22459     },
22460
22461     /**
22462      * Returns the current date value of the date field.
22463      * @return {Date} The date value
22464      */
22465     getValue : function(){
22466         
22467         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22468     },
22469
22470     /**
22471      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22472      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22473      * (the default format used is "m/d/y").
22474      * <br />Usage:
22475      * <pre><code>
22476 //All of these calls set the same date value (May 4, 2006)
22477
22478 //Pass a date object:
22479 var dt = new Date('5/4/06');
22480 dateField.setValue(dt);
22481
22482 //Pass a date string (default format):
22483 dateField.setValue('5/4/06');
22484
22485 //Pass a date string (custom format):
22486 dateField.format = 'Y-m-d';
22487 dateField.setValue('2006-5-4');
22488 </code></pre>
22489      * @param {String/Date} date The date or valid date string
22490      */
22491     setValue : function(date){
22492         if (this.hiddenField) {
22493             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22494         }
22495         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22496     },
22497
22498     // private
22499     parseDate : function(value){
22500         if(!value || value instanceof Date){
22501             return value;
22502         }
22503         var v = Date.parseDate(value, this.format);
22504         if(!v && this.altFormats){
22505             if(!this.altFormatsArray){
22506                 this.altFormatsArray = this.altFormats.split("|");
22507             }
22508             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22509                 v = Date.parseDate(value, this.altFormatsArray[i]);
22510             }
22511         }
22512         return v;
22513     },
22514
22515     // private
22516     formatDate : function(date, fmt){
22517         return (!date || !(date instanceof Date)) ?
22518                date : date.dateFormat(fmt || this.format);
22519     },
22520
22521     // private
22522     menuListeners : {
22523         select: function(m, d){
22524             this.setValue(d);
22525             this.fireEvent('select', this, d);
22526         },
22527         show : function(){ // retain focus styling
22528             this.onFocus();
22529         },
22530         hide : function(){
22531             this.focus.defer(10, this);
22532             var ml = this.menuListeners;
22533             this.menu.un("select", ml.select,  this);
22534             this.menu.un("show", ml.show,  this);
22535             this.menu.un("hide", ml.hide,  this);
22536         }
22537     },
22538
22539     // private
22540     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22541     onTriggerClick : function(){
22542         if(this.disabled){
22543             return;
22544         }
22545         if(this.menu == null){
22546             this.menu = new Roo.menu.DateMenu();
22547         }
22548         Roo.apply(this.menu.picker,  {
22549             showClear: this.allowBlank,
22550             minDate : this.minValue,
22551             maxDate : this.maxValue,
22552             disabledDatesRE : this.ddMatch,
22553             disabledDatesText : this.disabledDatesText,
22554             disabledDays : this.disabledDays,
22555             disabledDaysText : this.disabledDaysText,
22556             format : this.format,
22557             minText : String.format(this.minText, this.formatDate(this.minValue)),
22558             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22559         });
22560         this.menu.on(Roo.apply({}, this.menuListeners, {
22561             scope:this
22562         }));
22563         this.menu.picker.setValue(this.getValue() || new Date());
22564         this.menu.show(this.el, "tl-bl?");
22565     },
22566
22567     beforeBlur : function(){
22568         var v = this.parseDate(this.getRawValue());
22569         if(v){
22570             this.setValue(v);
22571         }
22572     }
22573
22574     /** @cfg {Boolean} grow @hide */
22575     /** @cfg {Number} growMin @hide */
22576     /** @cfg {Number} growMax @hide */
22577     /**
22578      * @hide
22579      * @method autoSize
22580      */
22581 });/*
22582  * Based on:
22583  * Ext JS Library 1.1.1
22584  * Copyright(c) 2006-2007, Ext JS, LLC.
22585  *
22586  * Originally Released Under LGPL - original licence link has changed is not relivant.
22587  *
22588  * Fork - LGPL
22589  * <script type="text/javascript">
22590  */
22591  
22592
22593 /**
22594  * @class Roo.form.ComboBox
22595  * @extends Roo.form.TriggerField
22596  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22597  * @constructor
22598  * Create a new ComboBox.
22599  * @param {Object} config Configuration options
22600  */
22601 Roo.form.ComboBox = function(config){
22602     Roo.form.ComboBox.superclass.constructor.call(this, config);
22603     this.addEvents({
22604         /**
22605          * @event expand
22606          * Fires when the dropdown list is expanded
22607              * @param {Roo.form.ComboBox} combo This combo box
22608              */
22609         'expand' : true,
22610         /**
22611          * @event collapse
22612          * Fires when the dropdown list is collapsed
22613              * @param {Roo.form.ComboBox} combo This combo box
22614              */
22615         'collapse' : true,
22616         /**
22617          * @event beforeselect
22618          * Fires before a list item is selected. Return false to cancel the selection.
22619              * @param {Roo.form.ComboBox} combo This combo box
22620              * @param {Roo.data.Record} record The data record returned from the underlying store
22621              * @param {Number} index The index of the selected item in the dropdown list
22622              */
22623         'beforeselect' : true,
22624         /**
22625          * @event select
22626          * Fires when a list item is selected
22627              * @param {Roo.form.ComboBox} combo This combo box
22628              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22629              * @param {Number} index The index of the selected item in the dropdown list
22630              */
22631         'select' : true,
22632         /**
22633          * @event beforequery
22634          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22635          * The event object passed has these properties:
22636              * @param {Roo.form.ComboBox} combo This combo box
22637              * @param {String} query The query
22638              * @param {Boolean} forceAll true to force "all" query
22639              * @param {Boolean} cancel true to cancel the query
22640              * @param {Object} e The query event object
22641              */
22642         'beforequery': true,
22643          /**
22644          * @event add
22645          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22646              * @param {Roo.form.ComboBox} combo This combo box
22647              */
22648         'add' : true,
22649         /**
22650          * @event edit
22651          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22652              * @param {Roo.form.ComboBox} combo This combo box
22653              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22654              */
22655         'edit' : true
22656         
22657         
22658     });
22659     if(this.transform){
22660         this.allowDomMove = false;
22661         var s = Roo.getDom(this.transform);
22662         if(!this.hiddenName){
22663             this.hiddenName = s.name;
22664         }
22665         if(!this.store){
22666             this.mode = 'local';
22667             var d = [], opts = s.options;
22668             for(var i = 0, len = opts.length;i < len; i++){
22669                 var o = opts[i];
22670                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22671                 if(o.selected) {
22672                     this.value = value;
22673                 }
22674                 d.push([value, o.text]);
22675             }
22676             this.store = new Roo.data.SimpleStore({
22677                 'id': 0,
22678                 fields: ['value', 'text'],
22679                 data : d
22680             });
22681             this.valueField = 'value';
22682             this.displayField = 'text';
22683         }
22684         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22685         if(!this.lazyRender){
22686             this.target = true;
22687             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22688             s.parentNode.removeChild(s); // remove it
22689             this.render(this.el.parentNode);
22690         }else{
22691             s.parentNode.removeChild(s); // remove it
22692         }
22693
22694     }
22695     if (this.store) {
22696         this.store = Roo.factory(this.store, Roo.data);
22697     }
22698     
22699     this.selectedIndex = -1;
22700     if(this.mode == 'local'){
22701         if(config.queryDelay === undefined){
22702             this.queryDelay = 10;
22703         }
22704         if(config.minChars === undefined){
22705             this.minChars = 0;
22706         }
22707     }
22708 };
22709
22710 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22711     /**
22712      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22713      */
22714     /**
22715      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22716      * rendering into an Roo.Editor, defaults to false)
22717      */
22718     /**
22719      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22720      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22721      */
22722     /**
22723      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22724      */
22725     /**
22726      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22727      * the dropdown list (defaults to undefined, with no header element)
22728      */
22729
22730      /**
22731      * @cfg {String/Roo.Template} tpl The template to use to render the output
22732      */
22733      
22734     // private
22735     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22736     /**
22737      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22738      */
22739     listWidth: undefined,
22740     /**
22741      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22742      * mode = 'remote' or 'text' if mode = 'local')
22743      */
22744     displayField: undefined,
22745     /**
22746      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22747      * mode = 'remote' or 'value' if mode = 'local'). 
22748      * Note: use of a valueField requires the user make a selection
22749      * in order for a value to be mapped.
22750      */
22751     valueField: undefined,
22752     /**
22753      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22754      * field's data value (defaults to the underlying DOM element's name)
22755      */
22756     hiddenName: undefined,
22757     /**
22758      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22759      */
22760     listClass: '',
22761     /**
22762      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22763      */
22764     selectedClass: 'x-combo-selected',
22765     /**
22766      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22767      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22768      * which displays a downward arrow icon).
22769      */
22770     triggerClass : 'x-form-arrow-trigger',
22771     /**
22772      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22773      */
22774     shadow:'sides',
22775     /**
22776      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22777      * anchor positions (defaults to 'tl-bl')
22778      */
22779     listAlign: 'tl-bl?',
22780     /**
22781      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22782      */
22783     maxHeight: 300,
22784     /**
22785      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22786      * query specified by the allQuery config option (defaults to 'query')
22787      */
22788     triggerAction: 'query',
22789     /**
22790      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22791      * (defaults to 4, does not apply if editable = false)
22792      */
22793     minChars : 4,
22794     /**
22795      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22796      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22797      */
22798     typeAhead: false,
22799     /**
22800      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22801      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22802      */
22803     queryDelay: 500,
22804     /**
22805      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22806      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22807      */
22808     pageSize: 0,
22809     /**
22810      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22811      * when editable = true (defaults to false)
22812      */
22813     selectOnFocus:false,
22814     /**
22815      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22816      */
22817     queryParam: 'query',
22818     /**
22819      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22820      * when mode = 'remote' (defaults to 'Loading...')
22821      */
22822     loadingText: 'Loading...',
22823     /**
22824      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22825      */
22826     resizable: false,
22827     /**
22828      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22829      */
22830     handleHeight : 8,
22831     /**
22832      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22833      * traditional select (defaults to true)
22834      */
22835     editable: true,
22836     /**
22837      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22838      */
22839     allQuery: '',
22840     /**
22841      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22842      */
22843     mode: 'remote',
22844     /**
22845      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22846      * listWidth has a higher value)
22847      */
22848     minListWidth : 70,
22849     /**
22850      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22851      * allow the user to set arbitrary text into the field (defaults to false)
22852      */
22853     forceSelection:false,
22854     /**
22855      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22856      * if typeAhead = true (defaults to 250)
22857      */
22858     typeAheadDelay : 250,
22859     /**
22860      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22861      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22862      */
22863     valueNotFoundText : undefined,
22864     /**
22865      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22866      */
22867     blockFocus : false,
22868     
22869     /**
22870      * @cfg {Boolean} disableClear Disable showing of clear button.
22871      */
22872     disableClear : false,
22873     /**
22874      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22875      */
22876     alwaysQuery : false,
22877     
22878     //private
22879     addicon : false,
22880     editicon: false,
22881     
22882     
22883     // private
22884     onRender : function(ct, position){
22885         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22886         if(this.hiddenName){
22887             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22888                     'before', true);
22889             this.hiddenField.value =
22890                 this.hiddenValue !== undefined ? this.hiddenValue :
22891                 this.value !== undefined ? this.value : '';
22892
22893             // prevent input submission
22894             this.el.dom.removeAttribute('name');
22895         }
22896         if(Roo.isGecko){
22897             this.el.dom.setAttribute('autocomplete', 'off');
22898         }
22899
22900         var cls = 'x-combo-list';
22901
22902         this.list = new Roo.Layer({
22903             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22904         });
22905
22906         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22907         this.list.setWidth(lw);
22908         this.list.swallowEvent('mousewheel');
22909         this.assetHeight = 0;
22910
22911         if(this.title){
22912             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22913             this.assetHeight += this.header.getHeight();
22914         }
22915
22916         this.innerList = this.list.createChild({cls:cls+'-inner'});
22917         this.innerList.on('mouseover', this.onViewOver, this);
22918         this.innerList.on('mousemove', this.onViewMove, this);
22919         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22920         
22921         if(this.allowBlank && !this.pageSize && !this.disableClear){
22922             this.footer = this.list.createChild({cls:cls+'-ft'});
22923             this.pageTb = new Roo.Toolbar(this.footer);
22924            
22925         }
22926         if(this.pageSize){
22927             this.footer = this.list.createChild({cls:cls+'-ft'});
22928             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22929                     {pageSize: this.pageSize});
22930             
22931         }
22932         
22933         if (this.pageTb && this.allowBlank && !this.disableClear) {
22934             var _this = this;
22935             this.pageTb.add(new Roo.Toolbar.Fill(), {
22936                 cls: 'x-btn-icon x-btn-clear',
22937                 text: '&#160;',
22938                 handler: function()
22939                 {
22940                     _this.collapse();
22941                     _this.clearValue();
22942                     _this.onSelect(false, -1);
22943                 }
22944             });
22945         }
22946         if (this.footer) {
22947             this.assetHeight += this.footer.getHeight();
22948         }
22949         
22950
22951         if(!this.tpl){
22952             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22953         }
22954
22955         this.view = new Roo.View(this.innerList, this.tpl, {
22956             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22957         });
22958
22959         this.view.on('click', this.onViewClick, this);
22960
22961         this.store.on('beforeload', this.onBeforeLoad, this);
22962         this.store.on('load', this.onLoad, this);
22963         this.store.on('loadexception', this.collapse, this);
22964
22965         if(this.resizable){
22966             this.resizer = new Roo.Resizable(this.list,  {
22967                pinned:true, handles:'se'
22968             });
22969             this.resizer.on('resize', function(r, w, h){
22970                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22971                 this.listWidth = w;
22972                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22973                 this.restrictHeight();
22974             }, this);
22975             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22976         }
22977         if(!this.editable){
22978             this.editable = true;
22979             this.setEditable(false);
22980         }  
22981         
22982         
22983         if (typeof(this.events.add.listeners) != 'undefined') {
22984             
22985             this.addicon = this.wrap.createChild(
22986                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22987        
22988             this.addicon.on('click', function(e) {
22989                 this.fireEvent('add', this);
22990             }, this);
22991         }
22992         if (typeof(this.events.edit.listeners) != 'undefined') {
22993             
22994             this.editicon = this.wrap.createChild(
22995                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22996             if (this.addicon) {
22997                 this.editicon.setStyle('margin-left', '40px');
22998             }
22999             this.editicon.on('click', function(e) {
23000                 
23001                 // we fire even  if inothing is selected..
23002                 this.fireEvent('edit', this, this.lastData );
23003                 
23004             }, this);
23005         }
23006         
23007         
23008         
23009     },
23010
23011     // private
23012     initEvents : function(){
23013         Roo.form.ComboBox.superclass.initEvents.call(this);
23014
23015         this.keyNav = new Roo.KeyNav(this.el, {
23016             "up" : function(e){
23017                 this.inKeyMode = true;
23018                 this.selectPrev();
23019             },
23020
23021             "down" : function(e){
23022                 if(!this.isExpanded()){
23023                     this.onTriggerClick();
23024                 }else{
23025                     this.inKeyMode = true;
23026                     this.selectNext();
23027                 }
23028             },
23029
23030             "enter" : function(e){
23031                 this.onViewClick();
23032                 //return true;
23033             },
23034
23035             "esc" : function(e){
23036                 this.collapse();
23037             },
23038
23039             "tab" : function(e){
23040                 this.onViewClick(false);
23041                 return true;
23042             },
23043
23044             scope : this,
23045
23046             doRelay : function(foo, bar, hname){
23047                 if(hname == 'down' || this.scope.isExpanded()){
23048                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23049                 }
23050                 return true;
23051             },
23052
23053             forceKeyDown: true
23054         });
23055         this.queryDelay = Math.max(this.queryDelay || 10,
23056                 this.mode == 'local' ? 10 : 250);
23057         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23058         if(this.typeAhead){
23059             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23060         }
23061         if(this.editable !== false){
23062             this.el.on("keyup", this.onKeyUp, this);
23063         }
23064         if(this.forceSelection){
23065             this.on('blur', this.doForce, this);
23066         }
23067     },
23068
23069     onDestroy : function(){
23070         if(this.view){
23071             this.view.setStore(null);
23072             this.view.el.removeAllListeners();
23073             this.view.el.remove();
23074             this.view.purgeListeners();
23075         }
23076         if(this.list){
23077             this.list.destroy();
23078         }
23079         if(this.store){
23080             this.store.un('beforeload', this.onBeforeLoad, this);
23081             this.store.un('load', this.onLoad, this);
23082             this.store.un('loadexception', this.collapse, this);
23083         }
23084         Roo.form.ComboBox.superclass.onDestroy.call(this);
23085     },
23086
23087     // private
23088     fireKey : function(e){
23089         if(e.isNavKeyPress() && !this.list.isVisible()){
23090             this.fireEvent("specialkey", this, e);
23091         }
23092     },
23093
23094     // private
23095     onResize: function(w, h){
23096         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23097         
23098         if(typeof w != 'number'){
23099             // we do not handle it!?!?
23100             return;
23101         }
23102         var tw = this.trigger.getWidth();
23103         tw += this.addicon ? this.addicon.getWidth() : 0;
23104         tw += this.editicon ? this.editicon.getWidth() : 0;
23105         var x = w - tw;
23106         this.el.setWidth( this.adjustWidth('input', x));
23107             
23108         this.trigger.setStyle('left', x+'px');
23109         
23110         if(this.list && this.listWidth === undefined){
23111             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23112             this.list.setWidth(lw);
23113             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23114         }
23115         
23116     
23117         
23118     },
23119
23120     /**
23121      * Allow or prevent the user from directly editing the field text.  If false is passed,
23122      * the user will only be able to select from the items defined in the dropdown list.  This method
23123      * is the runtime equivalent of setting the 'editable' config option at config time.
23124      * @param {Boolean} value True to allow the user to directly edit the field text
23125      */
23126     setEditable : function(value){
23127         if(value == this.editable){
23128             return;
23129         }
23130         this.editable = value;
23131         if(!value){
23132             this.el.dom.setAttribute('readOnly', true);
23133             this.el.on('mousedown', this.onTriggerClick,  this);
23134             this.el.addClass('x-combo-noedit');
23135         }else{
23136             this.el.dom.setAttribute('readOnly', false);
23137             this.el.un('mousedown', this.onTriggerClick,  this);
23138             this.el.removeClass('x-combo-noedit');
23139         }
23140     },
23141
23142     // private
23143     onBeforeLoad : function(){
23144         if(!this.hasFocus){
23145             return;
23146         }
23147         this.innerList.update(this.loadingText ?
23148                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23149         this.restrictHeight();
23150         this.selectedIndex = -1;
23151     },
23152
23153     // private
23154     onLoad : function(){
23155         if(!this.hasFocus){
23156             return;
23157         }
23158         if(this.store.getCount() > 0){
23159             this.expand();
23160             this.restrictHeight();
23161             if(this.lastQuery == this.allQuery){
23162                 if(this.editable){
23163                     this.el.dom.select();
23164                 }
23165                 if(!this.selectByValue(this.value, true)){
23166                     this.select(0, true);
23167                 }
23168             }else{
23169                 this.selectNext();
23170                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23171                     this.taTask.delay(this.typeAheadDelay);
23172                 }
23173             }
23174         }else{
23175             this.onEmptyResults();
23176         }
23177         //this.el.focus();
23178     },
23179
23180     // private
23181     onTypeAhead : function(){
23182         if(this.store.getCount() > 0){
23183             var r = this.store.getAt(0);
23184             var newValue = r.data[this.displayField];
23185             var len = newValue.length;
23186             var selStart = this.getRawValue().length;
23187             if(selStart != len){
23188                 this.setRawValue(newValue);
23189                 this.selectText(selStart, newValue.length);
23190             }
23191         }
23192     },
23193
23194     // private
23195     onSelect : function(record, index){
23196         if(this.fireEvent('beforeselect', this, record, index) !== false){
23197             this.setFromData(index > -1 ? record.data : false);
23198             this.collapse();
23199             this.fireEvent('select', this, record, index);
23200         }
23201     },
23202
23203     /**
23204      * Returns the currently selected field value or empty string if no value is set.
23205      * @return {String} value The selected value
23206      */
23207     getValue : function(){
23208         if(this.valueField){
23209             return typeof this.value != 'undefined' ? this.value : '';
23210         }else{
23211             return Roo.form.ComboBox.superclass.getValue.call(this);
23212         }
23213     },
23214
23215     /**
23216      * Clears any text/value currently set in the field
23217      */
23218     clearValue : function(){
23219         if(this.hiddenField){
23220             this.hiddenField.value = '';
23221         }
23222         this.value = '';
23223         this.setRawValue('');
23224         this.lastSelectionText = '';
23225         this.applyEmptyText();
23226     },
23227
23228     /**
23229      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23230      * will be displayed in the field.  If the value does not match the data value of an existing item,
23231      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23232      * Otherwise the field will be blank (although the value will still be set).
23233      * @param {String} value The value to match
23234      */
23235     setValue : function(v){
23236         var text = v;
23237         if(this.valueField){
23238             var r = this.findRecord(this.valueField, v);
23239             if(r){
23240                 text = r.data[this.displayField];
23241             }else if(this.valueNotFoundText !== undefined){
23242                 text = this.valueNotFoundText;
23243             }
23244         }
23245         this.lastSelectionText = text;
23246         if(this.hiddenField){
23247             this.hiddenField.value = v;
23248         }
23249         Roo.form.ComboBox.superclass.setValue.call(this, text);
23250         this.value = v;
23251     },
23252     /**
23253      * @property {Object} the last set data for the element
23254      */
23255     
23256     lastData : false,
23257     /**
23258      * Sets the value of the field based on a object which is related to the record format for the store.
23259      * @param {Object} value the value to set as. or false on reset?
23260      */
23261     setFromData : function(o){
23262         var dv = ''; // display value
23263         var vv = ''; // value value..
23264         this.lastData = o;
23265         if (this.displayField) {
23266             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23267         } else {
23268             // this is an error condition!!!
23269             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23270         }
23271         
23272         if(this.valueField){
23273             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23274         }
23275         if(this.hiddenField){
23276             this.hiddenField.value = vv;
23277             
23278             this.lastSelectionText = dv;
23279             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23280             this.value = vv;
23281             return;
23282         }
23283         // no hidden field.. - we store the value in 'value', but still display
23284         // display field!!!!
23285         this.lastSelectionText = dv;
23286         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23287         this.value = vv;
23288         
23289         
23290     },
23291     // private
23292     reset : function(){
23293         // overridden so that last data is reset..
23294         this.setValue(this.originalValue);
23295         this.clearInvalid();
23296         this.lastData = false;
23297     },
23298     // private
23299     findRecord : function(prop, value){
23300         var record;
23301         if(this.store.getCount() > 0){
23302             this.store.each(function(r){
23303                 if(r.data[prop] == value){
23304                     record = r;
23305                     return false;
23306                 }
23307             });
23308         }
23309         return record;
23310     },
23311
23312     // private
23313     onViewMove : function(e, t){
23314         this.inKeyMode = false;
23315     },
23316
23317     // private
23318     onViewOver : function(e, t){
23319         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23320             return;
23321         }
23322         var item = this.view.findItemFromChild(t);
23323         if(item){
23324             var index = this.view.indexOf(item);
23325             this.select(index, false);
23326         }
23327     },
23328
23329     // private
23330     onViewClick : function(doFocus){
23331         var index = this.view.getSelectedIndexes()[0];
23332         var r = this.store.getAt(index);
23333         if(r){
23334             this.onSelect(r, index);
23335         }
23336         if(doFocus !== false && !this.blockFocus){
23337             this.el.focus();
23338         }
23339     },
23340
23341     // private
23342     restrictHeight : function(){
23343         this.innerList.dom.style.height = '';
23344         var inner = this.innerList.dom;
23345         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23346         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23347         this.list.beginUpdate();
23348         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23349         this.list.alignTo(this.el, this.listAlign);
23350         this.list.endUpdate();
23351     },
23352
23353     // private
23354     onEmptyResults : function(){
23355         this.collapse();
23356     },
23357
23358     /**
23359      * Returns true if the dropdown list is expanded, else false.
23360      */
23361     isExpanded : function(){
23362         return this.list.isVisible();
23363     },
23364
23365     /**
23366      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23367      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23368      * @param {String} value The data value of the item to select
23369      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23370      * selected item if it is not currently in view (defaults to true)
23371      * @return {Boolean} True if the value matched an item in the list, else false
23372      */
23373     selectByValue : function(v, scrollIntoView){
23374         if(v !== undefined && v !== null){
23375             var r = this.findRecord(this.valueField || this.displayField, v);
23376             if(r){
23377                 this.select(this.store.indexOf(r), scrollIntoView);
23378                 return true;
23379             }
23380         }
23381         return false;
23382     },
23383
23384     /**
23385      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23386      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23387      * @param {Number} index The zero-based index of the list item to select
23388      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23389      * selected item if it is not currently in view (defaults to true)
23390      */
23391     select : function(index, scrollIntoView){
23392         this.selectedIndex = index;
23393         this.view.select(index);
23394         if(scrollIntoView !== false){
23395             var el = this.view.getNode(index);
23396             if(el){
23397                 this.innerList.scrollChildIntoView(el, false);
23398             }
23399         }
23400     },
23401
23402     // private
23403     selectNext : function(){
23404         var ct = this.store.getCount();
23405         if(ct > 0){
23406             if(this.selectedIndex == -1){
23407                 this.select(0);
23408             }else if(this.selectedIndex < ct-1){
23409                 this.select(this.selectedIndex+1);
23410             }
23411         }
23412     },
23413
23414     // private
23415     selectPrev : function(){
23416         var ct = this.store.getCount();
23417         if(ct > 0){
23418             if(this.selectedIndex == -1){
23419                 this.select(0);
23420             }else if(this.selectedIndex != 0){
23421                 this.select(this.selectedIndex-1);
23422             }
23423         }
23424     },
23425
23426     // private
23427     onKeyUp : function(e){
23428         if(this.editable !== false && !e.isSpecialKey()){
23429             this.lastKey = e.getKey();
23430             this.dqTask.delay(this.queryDelay);
23431         }
23432     },
23433
23434     // private
23435     validateBlur : function(){
23436         return !this.list || !this.list.isVisible();   
23437     },
23438
23439     // private
23440     initQuery : function(){
23441         this.doQuery(this.getRawValue());
23442     },
23443
23444     // private
23445     doForce : function(){
23446         if(this.el.dom.value.length > 0){
23447             this.el.dom.value =
23448                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23449             this.applyEmptyText();
23450         }
23451     },
23452
23453     /**
23454      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23455      * query allowing the query action to be canceled if needed.
23456      * @param {String} query The SQL query to execute
23457      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23458      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23459      * saved in the current store (defaults to false)
23460      */
23461     doQuery : function(q, forceAll){
23462         if(q === undefined || q === null){
23463             q = '';
23464         }
23465         var qe = {
23466             query: q,
23467             forceAll: forceAll,
23468             combo: this,
23469             cancel:false
23470         };
23471         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23472             return false;
23473         }
23474         q = qe.query;
23475         forceAll = qe.forceAll;
23476         if(forceAll === true || (q.length >= this.minChars)){
23477             if(this.lastQuery != q || this.alwaysQuery){
23478                 this.lastQuery = q;
23479                 if(this.mode == 'local'){
23480                     this.selectedIndex = -1;
23481                     if(forceAll){
23482                         this.store.clearFilter();
23483                     }else{
23484                         this.store.filter(this.displayField, q);
23485                     }
23486                     this.onLoad();
23487                 }else{
23488                     this.store.baseParams[this.queryParam] = q;
23489                     this.store.load({
23490                         params: this.getParams(q)
23491                     });
23492                     this.expand();
23493                 }
23494             }else{
23495                 this.selectedIndex = -1;
23496                 this.onLoad();   
23497             }
23498         }
23499     },
23500
23501     // private
23502     getParams : function(q){
23503         var p = {};
23504         //p[this.queryParam] = q;
23505         if(this.pageSize){
23506             p.start = 0;
23507             p.limit = this.pageSize;
23508         }
23509         return p;
23510     },
23511
23512     /**
23513      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23514      */
23515     collapse : function(){
23516         if(!this.isExpanded()){
23517             return;
23518         }
23519         this.list.hide();
23520         Roo.get(document).un('mousedown', this.collapseIf, this);
23521         Roo.get(document).un('mousewheel', this.collapseIf, this);
23522         if (!this.editable) {
23523             Roo.get(document).un('keydown', this.listKeyPress, this);
23524         }
23525         this.fireEvent('collapse', this);
23526     },
23527
23528     // private
23529     collapseIf : function(e){
23530         if(!e.within(this.wrap) && !e.within(this.list)){
23531             this.collapse();
23532         }
23533     },
23534
23535     /**
23536      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23537      */
23538     expand : function(){
23539         if(this.isExpanded() || !this.hasFocus){
23540             return;
23541         }
23542         this.list.alignTo(this.el, this.listAlign);
23543         this.list.show();
23544         Roo.get(document).on('mousedown', this.collapseIf, this);
23545         Roo.get(document).on('mousewheel', this.collapseIf, this);
23546         if (!this.editable) {
23547             Roo.get(document).on('keydown', this.listKeyPress, this);
23548         }
23549         
23550         this.fireEvent('expand', this);
23551     },
23552
23553     // private
23554     // Implements the default empty TriggerField.onTriggerClick function
23555     onTriggerClick : function(){
23556         if(this.disabled){
23557             return;
23558         }
23559         if(this.isExpanded()){
23560             this.collapse();
23561             if (!this.blockFocus) {
23562                 this.el.focus();
23563             }
23564             
23565         }else {
23566             this.hasFocus = true;
23567             if(this.triggerAction == 'all') {
23568                 this.doQuery(this.allQuery, true);
23569             } else {
23570                 this.doQuery(this.getRawValue());
23571             }
23572             if (!this.blockFocus) {
23573                 this.el.focus();
23574             }
23575         }
23576     },
23577     listKeyPress : function(e)
23578     {
23579         //Roo.log('listkeypress');
23580         // scroll to first matching element based on key pres..
23581         if (e.isSpecialKey()) {
23582             return false;
23583         }
23584         var k = String.fromCharCode(e.getKey()).toUpperCase();
23585         //Roo.log(k);
23586         var match  = false;
23587         var csel = this.view.getSelectedNodes();
23588         var cselitem = false;
23589         if (csel.length) {
23590             var ix = this.view.indexOf(csel[0]);
23591             cselitem  = this.store.getAt(ix);
23592             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23593                 cselitem = false;
23594             }
23595             
23596         }
23597         
23598         this.store.each(function(v) { 
23599             if (cselitem) {
23600                 // start at existing selection.
23601                 if (cselitem.id == v.id) {
23602                     cselitem = false;
23603                 }
23604                 return;
23605             }
23606                 
23607             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23608                 match = this.store.indexOf(v);
23609                 return false;
23610             }
23611         }, this);
23612         
23613         if (match === false) {
23614             return true; // no more action?
23615         }
23616         // scroll to?
23617         this.view.select(match);
23618         var sn = Roo.get(this.view.getSelectedNodes()[0])
23619         sn.scrollIntoView(sn.dom.parentNode, false);
23620     }
23621
23622     /** 
23623     * @cfg {Boolean} grow 
23624     * @hide 
23625     */
23626     /** 
23627     * @cfg {Number} growMin 
23628     * @hide 
23629     */
23630     /** 
23631     * @cfg {Number} growMax 
23632     * @hide 
23633     */
23634     /**
23635      * @hide
23636      * @method autoSize
23637      */
23638 });/*
23639  * Based on:
23640  * Ext JS Library 1.1.1
23641  * Copyright(c) 2006-2007, Ext JS, LLC.
23642  *
23643  * Originally Released Under LGPL - original licence link has changed is not relivant.
23644  *
23645  * Fork - LGPL
23646  * <script type="text/javascript">
23647  */
23648 /**
23649  * @class Roo.form.Checkbox
23650  * @extends Roo.form.Field
23651  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23652  * @constructor
23653  * Creates a new Checkbox
23654  * @param {Object} config Configuration options
23655  */
23656 Roo.form.Checkbox = function(config){
23657     Roo.form.Checkbox.superclass.constructor.call(this, config);
23658     this.addEvents({
23659         /**
23660          * @event check
23661          * Fires when the checkbox is checked or unchecked.
23662              * @param {Roo.form.Checkbox} this This checkbox
23663              * @param {Boolean} checked The new checked value
23664              */
23665         check : true
23666     });
23667 };
23668
23669 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23670     /**
23671      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23672      */
23673     focusClass : undefined,
23674     /**
23675      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23676      */
23677     fieldClass: "x-form-field",
23678     /**
23679      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23680      */
23681     checked: false,
23682     /**
23683      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23684      * {tag: "input", type: "checkbox", autocomplete: "off"})
23685      */
23686     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23687     /**
23688      * @cfg {String} boxLabel The text that appears beside the checkbox
23689      */
23690     boxLabel : "",
23691     /**
23692      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23693      */  
23694     inputValue : '1',
23695     /**
23696      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23697      */
23698      valueOff: '0', // value when not checked..
23699
23700     actionMode : 'viewEl', 
23701     //
23702     // private
23703     itemCls : 'x-menu-check-item x-form-item',
23704     groupClass : 'x-menu-group-item',
23705     inputType : 'hidden',
23706     
23707     
23708     inSetChecked: false, // check that we are not calling self...
23709     
23710     inputElement: false, // real input element?
23711     basedOn: false, // ????
23712     
23713     isFormField: true, // not sure where this is needed!!!!
23714
23715     onResize : function(){
23716         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23717         if(!this.boxLabel){
23718             this.el.alignTo(this.wrap, 'c-c');
23719         }
23720     },
23721
23722     initEvents : function(){
23723         Roo.form.Checkbox.superclass.initEvents.call(this);
23724         this.el.on("click", this.onClick,  this);
23725         this.el.on("change", this.onClick,  this);
23726     },
23727
23728
23729     getResizeEl : function(){
23730         return this.wrap;
23731     },
23732
23733     getPositionEl : function(){
23734         return this.wrap;
23735     },
23736
23737     // private
23738     onRender : function(ct, position){
23739         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23740         /*
23741         if(this.inputValue !== undefined){
23742             this.el.dom.value = this.inputValue;
23743         }
23744         */
23745         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23746         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23747         var viewEl = this.wrap.createChild({ 
23748             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23749         this.viewEl = viewEl;   
23750         this.wrap.on('click', this.onClick,  this); 
23751         
23752         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23753         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23754         
23755         
23756         
23757         if(this.boxLabel){
23758             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23759         //    viewEl.on('click', this.onClick,  this); 
23760         }
23761         //if(this.checked){
23762             this.setChecked(this.checked);
23763         //}else{
23764             //this.checked = this.el.dom;
23765         //}
23766
23767     },
23768
23769     // private
23770     initValue : Roo.emptyFn,
23771
23772     /**
23773      * Returns the checked state of the checkbox.
23774      * @return {Boolean} True if checked, else false
23775      */
23776     getValue : function(){
23777         if(this.el){
23778             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23779         }
23780         return this.valueOff;
23781         
23782     },
23783
23784         // private
23785     onClick : function(){ 
23786         this.setChecked(!this.checked);
23787
23788         //if(this.el.dom.checked != this.checked){
23789         //    this.setValue(this.el.dom.checked);
23790        // }
23791     },
23792
23793     /**
23794      * Sets the checked state of the checkbox.
23795      * On is always based on a string comparison between inputValue and the param.
23796      * @param {Boolean/String} value - the value to set 
23797      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23798      */
23799     setValue : function(v,suppressEvent){
23800         
23801         
23802         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23803         //if(this.el && this.el.dom){
23804         //    this.el.dom.checked = this.checked;
23805         //    this.el.dom.defaultChecked = this.checked;
23806         //}
23807         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23808         //this.fireEvent("check", this, this.checked);
23809     },
23810     // private..
23811     setChecked : function(state,suppressEvent)
23812     {
23813         if (this.inSetChecked) {
23814             this.checked = state;
23815             return;
23816         }
23817         
23818     
23819         if(this.wrap){
23820             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23821         }
23822         this.checked = state;
23823         if(suppressEvent !== true){
23824             this.fireEvent('check', this, state);
23825         }
23826         this.inSetChecked = true;
23827         this.el.dom.value = state ? this.inputValue : this.valueOff;
23828         this.inSetChecked = false;
23829         
23830     },
23831     // handle setting of hidden value by some other method!!?!?
23832     setFromHidden: function()
23833     {
23834         if(!this.el){
23835             return;
23836         }
23837         //console.log("SET FROM HIDDEN");
23838         //alert('setFrom hidden');
23839         this.setValue(this.el.dom.value);
23840     },
23841     
23842     onDestroy : function()
23843     {
23844         if(this.viewEl){
23845             Roo.get(this.viewEl).remove();
23846         }
23847          
23848         Roo.form.Checkbox.superclass.onDestroy.call(this);
23849     }
23850
23851 });/*
23852  * Based on:
23853  * Ext JS Library 1.1.1
23854  * Copyright(c) 2006-2007, Ext JS, LLC.
23855  *
23856  * Originally Released Under LGPL - original licence link has changed is not relivant.
23857  *
23858  * Fork - LGPL
23859  * <script type="text/javascript">
23860  */
23861  
23862 /**
23863  * @class Roo.form.Radio
23864  * @extends Roo.form.Checkbox
23865  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23866  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23867  * @constructor
23868  * Creates a new Radio
23869  * @param {Object} config Configuration options
23870  */
23871 Roo.form.Radio = function(){
23872     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23873 };
23874 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23875     inputType: 'radio',
23876
23877     /**
23878      * If this radio is part of a group, it will return the selected value
23879      * @return {String}
23880      */
23881     getGroupValue : function(){
23882         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23883     }
23884 });//<script type="text/javascript">
23885
23886 /*
23887  * Ext JS Library 1.1.1
23888  * Copyright(c) 2006-2007, Ext JS, LLC.
23889  * licensing@extjs.com
23890  * 
23891  * http://www.extjs.com/license
23892  */
23893  
23894  /*
23895   * 
23896   * Known bugs:
23897   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23898   * - IE ? - no idea how much works there.
23899   * 
23900   * 
23901   * 
23902   */
23903  
23904
23905 /**
23906  * @class Ext.form.HtmlEditor
23907  * @extends Ext.form.Field
23908  * Provides a lightweight HTML Editor component.
23909  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23910  * 
23911  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23912  * supported by this editor.</b><br/><br/>
23913  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23914  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23915  */
23916 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23917       /**
23918      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23919      */
23920     toolbars : false,
23921     /**
23922      * @cfg {String} createLinkText The default text for the create link prompt
23923      */
23924     createLinkText : 'Please enter the URL for the link:',
23925     /**
23926      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23927      */
23928     defaultLinkValue : 'http:/'+'/',
23929    
23930     
23931     // id of frame..
23932     frameId: false,
23933     
23934     // private properties
23935     validationEvent : false,
23936     deferHeight: true,
23937     initialized : false,
23938     activated : false,
23939     sourceEditMode : false,
23940     onFocus : Roo.emptyFn,
23941     iframePad:3,
23942     hideMode:'offsets',
23943     defaultAutoCreate : {
23944         tag: "textarea",
23945         style:"width:500px;height:300px;",
23946         autocomplete: "off"
23947     },
23948
23949     // private
23950     initComponent : function(){
23951         this.addEvents({
23952             /**
23953              * @event initialize
23954              * Fires when the editor is fully initialized (including the iframe)
23955              * @param {HtmlEditor} this
23956              */
23957             initialize: true,
23958             /**
23959              * @event activate
23960              * Fires when the editor is first receives the focus. Any insertion must wait
23961              * until after this event.
23962              * @param {HtmlEditor} this
23963              */
23964             activate: true,
23965              /**
23966              * @event beforesync
23967              * Fires before the textarea is updated with content from the editor iframe. Return false
23968              * to cancel the sync.
23969              * @param {HtmlEditor} this
23970              * @param {String} html
23971              */
23972             beforesync: true,
23973              /**
23974              * @event beforepush
23975              * Fires before the iframe editor is updated with content from the textarea. Return false
23976              * to cancel the push.
23977              * @param {HtmlEditor} this
23978              * @param {String} html
23979              */
23980             beforepush: true,
23981              /**
23982              * @event sync
23983              * Fires when the textarea is updated with content from the editor iframe.
23984              * @param {HtmlEditor} this
23985              * @param {String} html
23986              */
23987             sync: true,
23988              /**
23989              * @event push
23990              * Fires when the iframe editor is updated with content from the textarea.
23991              * @param {HtmlEditor} this
23992              * @param {String} html
23993              */
23994             push: true,
23995              /**
23996              * @event editmodechange
23997              * Fires when the editor switches edit modes
23998              * @param {HtmlEditor} this
23999              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24000              */
24001             editmodechange: true,
24002             /**
24003              * @event editorevent
24004              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24005              * @param {HtmlEditor} this
24006              */
24007             editorevent: true
24008         })
24009     },
24010
24011     /**
24012      * Protected method that will not generally be called directly. It
24013      * is called when the editor creates its toolbar. Override this method if you need to
24014      * add custom toolbar buttons.
24015      * @param {HtmlEditor} editor
24016      */
24017     createToolbar : function(editor){
24018         if (!editor.toolbars || !editor.toolbars.length) {
24019             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24020         }
24021         
24022         for (var i =0 ; i < editor.toolbars.length;i++) {
24023             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24024             editor.toolbars[i].init(editor);
24025         }
24026          
24027         
24028     },
24029
24030     /**
24031      * Protected method that will not generally be called directly. It
24032      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24033      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24034      */
24035     getDocMarkup : function(){
24036         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24037     },
24038
24039     // private
24040     onRender : function(ct, position){
24041         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24042         this.el.dom.style.border = '0 none';
24043         this.el.dom.setAttribute('tabIndex', -1);
24044         this.el.addClass('x-hidden');
24045         if(Roo.isIE){ // fix IE 1px bogus margin
24046             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24047         }
24048         this.wrap = this.el.wrap({
24049             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24050         });
24051
24052         this.frameId = Roo.id();
24053         this.createToolbar(this);
24054         
24055         
24056         
24057         
24058       
24059         
24060         var iframe = this.wrap.createChild({
24061             tag: 'iframe',
24062             id: this.frameId,
24063             name: this.frameId,
24064             frameBorder : 'no',
24065             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24066         });
24067         
24068        // console.log(iframe);
24069         //this.wrap.dom.appendChild(iframe);
24070
24071         this.iframe = iframe.dom;
24072
24073          this.assignDocWin();
24074         
24075         this.doc.designMode = 'on';
24076        
24077         this.doc.open();
24078         this.doc.write(this.getDocMarkup());
24079         this.doc.close();
24080
24081         
24082         var task = { // must defer to wait for browser to be ready
24083             run : function(){
24084                 //console.log("run task?" + this.doc.readyState);
24085                 this.assignDocWin();
24086                 if(this.doc.body || this.doc.readyState == 'complete'){
24087                     try {
24088                         this.doc.designMode="on";
24089                     } catch (e) {
24090                         return;
24091                     }
24092                     Roo.TaskMgr.stop(task);
24093                     this.initEditor.defer(10, this);
24094                 }
24095             },
24096             interval : 10,
24097             duration:10000,
24098             scope: this
24099         };
24100         Roo.TaskMgr.start(task);
24101
24102         if(!this.width){
24103             this.setSize(this.el.getSize());
24104         }
24105     },
24106
24107     // private
24108     onResize : function(w, h){
24109         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24110         if(this.el && this.iframe){
24111             if(typeof w == 'number'){
24112                 var aw = w - this.wrap.getFrameWidth('lr');
24113                 this.el.setWidth(this.adjustWidth('textarea', aw));
24114                 this.iframe.style.width = aw + 'px';
24115             }
24116             if(typeof h == 'number'){
24117                 var tbh = 0;
24118                 for (var i =0; i < this.toolbars.length;i++) {
24119                     // fixme - ask toolbars for heights?
24120                     tbh += this.toolbars[i].tb.el.getHeight();
24121                 }
24122                 
24123                 
24124                 
24125                 
24126                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24127                 this.el.setHeight(this.adjustWidth('textarea', ah));
24128                 this.iframe.style.height = ah + 'px';
24129                 if(this.doc){
24130                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24131                 }
24132             }
24133         }
24134     },
24135
24136     /**
24137      * Toggles the editor between standard and source edit mode.
24138      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24139      */
24140     toggleSourceEdit : function(sourceEditMode){
24141         
24142         this.sourceEditMode = sourceEditMode === true;
24143         
24144         if(this.sourceEditMode){
24145           
24146             this.syncValue();
24147             this.iframe.className = 'x-hidden';
24148             this.el.removeClass('x-hidden');
24149             this.el.dom.removeAttribute('tabIndex');
24150             this.el.focus();
24151         }else{
24152              
24153             this.pushValue();
24154             this.iframe.className = '';
24155             this.el.addClass('x-hidden');
24156             this.el.dom.setAttribute('tabIndex', -1);
24157             this.deferFocus();
24158         }
24159         this.setSize(this.wrap.getSize());
24160         this.fireEvent('editmodechange', this, this.sourceEditMode);
24161     },
24162
24163     // private used internally
24164     createLink : function(){
24165         var url = prompt(this.createLinkText, this.defaultLinkValue);
24166         if(url && url != 'http:/'+'/'){
24167             this.relayCmd('createlink', url);
24168         }
24169     },
24170
24171     // private (for BoxComponent)
24172     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24173
24174     // private (for BoxComponent)
24175     getResizeEl : function(){
24176         return this.wrap;
24177     },
24178
24179     // private (for BoxComponent)
24180     getPositionEl : function(){
24181         return this.wrap;
24182     },
24183
24184     // private
24185     initEvents : function(){
24186         this.originalValue = this.getValue();
24187     },
24188
24189     /**
24190      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24191      * @method
24192      */
24193     markInvalid : Roo.emptyFn,
24194     /**
24195      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24196      * @method
24197      */
24198     clearInvalid : Roo.emptyFn,
24199
24200     setValue : function(v){
24201         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24202         this.pushValue();
24203     },
24204
24205     /**
24206      * Protected method that will not generally be called directly. If you need/want
24207      * custom HTML cleanup, this is the method you should override.
24208      * @param {String} html The HTML to be cleaned
24209      * return {String} The cleaned HTML
24210      */
24211     cleanHtml : function(html){
24212         html = String(html);
24213         if(html.length > 5){
24214             if(Roo.isSafari){ // strip safari nonsense
24215                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24216             }
24217         }
24218         if(html == '&nbsp;'){
24219             html = '';
24220         }
24221         return html;
24222     },
24223
24224     /**
24225      * Protected method that will not generally be called directly. Syncs the contents
24226      * of the editor iframe with the textarea.
24227      */
24228     syncValue : function(){
24229         if(this.initialized){
24230             var bd = (this.doc.body || this.doc.documentElement);
24231             this.cleanUpPaste();
24232             var html = bd.innerHTML;
24233             if(Roo.isSafari){
24234                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24235                 var m = bs.match(/text-align:(.*?);/i);
24236                 if(m && m[1]){
24237                     html = '<div style="'+m[0]+'">' + html + '</div>';
24238                 }
24239             }
24240             html = this.cleanHtml(html);
24241             if(this.fireEvent('beforesync', this, html) !== false){
24242                 this.el.dom.value = html;
24243                 this.fireEvent('sync', this, html);
24244             }
24245         }
24246     },
24247
24248     /**
24249      * Protected method that will not generally be called directly. Pushes the value of the textarea
24250      * into the iframe editor.
24251      */
24252     pushValue : function(){
24253         if(this.initialized){
24254             var v = this.el.dom.value;
24255             if(v.length < 1){
24256                 v = '&#160;';
24257             }
24258             
24259             if(this.fireEvent('beforepush', this, v) !== false){
24260                 var d = (this.doc.body || this.doc.documentElement);
24261                 d.innerHTML = v;
24262                 this.cleanUpPaste();
24263                 this.el.dom.value = d.innerHTML;
24264                 this.fireEvent('push', this, v);
24265             }
24266         }
24267     },
24268
24269     // private
24270     deferFocus : function(){
24271         this.focus.defer(10, this);
24272     },
24273
24274     // doc'ed in Field
24275     focus : function(){
24276         if(this.win && !this.sourceEditMode){
24277             this.win.focus();
24278         }else{
24279             this.el.focus();
24280         }
24281     },
24282     
24283     assignDocWin: function()
24284     {
24285         var iframe = this.iframe;
24286         
24287          if(Roo.isIE){
24288             this.doc = iframe.contentWindow.document;
24289             this.win = iframe.contentWindow;
24290         } else {
24291             if (!Roo.get(this.frameId)) {
24292                 return;
24293             }
24294             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24295             this.win = Roo.get(this.frameId).dom.contentWindow;
24296         }
24297     },
24298     
24299     // private
24300     initEditor : function(){
24301         //console.log("INIT EDITOR");
24302         this.assignDocWin();
24303         
24304         
24305         
24306         this.doc.designMode="on";
24307         this.doc.open();
24308         this.doc.write(this.getDocMarkup());
24309         this.doc.close();
24310         
24311         var dbody = (this.doc.body || this.doc.documentElement);
24312         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24313         // this copies styles from the containing element into thsi one..
24314         // not sure why we need all of this..
24315         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24316         ss['background-attachment'] = 'fixed'; // w3c
24317         dbody.bgProperties = 'fixed'; // ie
24318         Roo.DomHelper.applyStyles(dbody, ss);
24319         Roo.EventManager.on(this.doc, {
24320             'mousedown': this.onEditorEvent,
24321             'dblclick': this.onEditorEvent,
24322             'click': this.onEditorEvent,
24323             'keyup': this.onEditorEvent,
24324             buffer:100,
24325             scope: this
24326         });
24327         if(Roo.isGecko){
24328             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24329         }
24330         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24331             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24332         }
24333         this.initialized = true;
24334
24335         this.fireEvent('initialize', this);
24336         this.pushValue();
24337     },
24338
24339     // private
24340     onDestroy : function(){
24341         
24342         
24343         
24344         if(this.rendered){
24345             
24346             for (var i =0; i < this.toolbars.length;i++) {
24347                 // fixme - ask toolbars for heights?
24348                 this.toolbars[i].onDestroy();
24349             }
24350             
24351             this.wrap.dom.innerHTML = '';
24352             this.wrap.remove();
24353         }
24354     },
24355
24356     // private
24357     onFirstFocus : function(){
24358         
24359         this.assignDocWin();
24360         
24361         
24362         this.activated = true;
24363         for (var i =0; i < this.toolbars.length;i++) {
24364             this.toolbars[i].onFirstFocus();
24365         }
24366        
24367         if(Roo.isGecko){ // prevent silly gecko errors
24368             this.win.focus();
24369             var s = this.win.getSelection();
24370             if(!s.focusNode || s.focusNode.nodeType != 3){
24371                 var r = s.getRangeAt(0);
24372                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24373                 r.collapse(true);
24374                 this.deferFocus();
24375             }
24376             try{
24377                 this.execCmd('useCSS', true);
24378                 this.execCmd('styleWithCSS', false);
24379             }catch(e){}
24380         }
24381         this.fireEvent('activate', this);
24382     },
24383
24384     // private
24385     adjustFont: function(btn){
24386         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24387         //if(Roo.isSafari){ // safari
24388         //    adjust *= 2;
24389        // }
24390         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24391         if(Roo.isSafari){ // safari
24392             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24393             v =  (v < 10) ? 10 : v;
24394             v =  (v > 48) ? 48 : v;
24395             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24396             
24397         }
24398         
24399         
24400         v = Math.max(1, v+adjust);
24401         
24402         this.execCmd('FontSize', v  );
24403     },
24404
24405     onEditorEvent : function(e){
24406         this.fireEvent('editorevent', this, e);
24407       //  this.updateToolbar();
24408         this.syncValue();
24409     },
24410
24411     insertTag : function(tg)
24412     {
24413         // could be a bit smarter... -> wrap the current selected tRoo..
24414         
24415         this.execCmd("formatblock",   tg);
24416         
24417     },
24418     
24419     insertText : function(txt)
24420     {
24421         
24422         
24423         range = this.createRange();
24424         range.deleteContents();
24425                //alert(Sender.getAttribute('label'));
24426                
24427         range.insertNode(this.doc.createTextNode(txt));
24428     } ,
24429     
24430     // private
24431     relayBtnCmd : function(btn){
24432         this.relayCmd(btn.cmd);
24433     },
24434
24435     /**
24436      * Executes a Midas editor command on the editor document and performs necessary focus and
24437      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24438      * @param {String} cmd The Midas command
24439      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24440      */
24441     relayCmd : function(cmd, value){
24442         this.win.focus();
24443         this.execCmd(cmd, value);
24444         this.fireEvent('editorevent', this);
24445         //this.updateToolbar();
24446         this.deferFocus();
24447     },
24448
24449     /**
24450      * Executes a Midas editor command directly on the editor document.
24451      * For visual commands, you should use {@link #relayCmd} instead.
24452      * <b>This should only be called after the editor is initialized.</b>
24453      * @param {String} cmd The Midas command
24454      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24455      */
24456     execCmd : function(cmd, value){
24457         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24458         this.syncValue();
24459     },
24460
24461    
24462     /**
24463      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24464      * to insert tRoo.
24465      * @param {String} text
24466      */
24467     insertAtCursor : function(text){
24468         if(!this.activated){
24469             return;
24470         }
24471         if(Roo.isIE){
24472             this.win.focus();
24473             var r = this.doc.selection.createRange();
24474             if(r){
24475                 r.collapse(true);
24476                 r.pasteHTML(text);
24477                 this.syncValue();
24478                 this.deferFocus();
24479             }
24480         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24481             this.win.focus();
24482             this.execCmd('InsertHTML', text);
24483             this.deferFocus();
24484         }
24485     },
24486  // private
24487     mozKeyPress : function(e){
24488         if(e.ctrlKey){
24489             var c = e.getCharCode(), cmd;
24490           
24491             if(c > 0){
24492                 c = String.fromCharCode(c).toLowerCase();
24493                 switch(c){
24494                     case 'b':
24495                         cmd = 'bold';
24496                     break;
24497                     case 'i':
24498                         cmd = 'italic';
24499                     break;
24500                     case 'u':
24501                         cmd = 'underline';
24502                     case 'v':
24503                         this.cleanUpPaste.defer(100, this);
24504                         return;
24505                     break;
24506                 }
24507                 if(cmd){
24508                     this.win.focus();
24509                     this.execCmd(cmd);
24510                     this.deferFocus();
24511                     e.preventDefault();
24512                 }
24513                 
24514             }
24515         }
24516     },
24517
24518     // private
24519     fixKeys : function(){ // load time branching for fastest keydown performance
24520         if(Roo.isIE){
24521             return function(e){
24522                 var k = e.getKey(), r;
24523                 if(k == e.TAB){
24524                     e.stopEvent();
24525                     r = this.doc.selection.createRange();
24526                     if(r){
24527                         r.collapse(true);
24528                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24529                         this.deferFocus();
24530                     }
24531                     return;
24532                 }
24533                 
24534                 if(k == e.ENTER){
24535                     r = this.doc.selection.createRange();
24536                     if(r){
24537                         var target = r.parentElement();
24538                         if(!target || target.tagName.toLowerCase() != 'li'){
24539                             e.stopEvent();
24540                             r.pasteHTML('<br />');
24541                             r.collapse(false);
24542                             r.select();
24543                         }
24544                     }
24545                 }
24546                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24547                     this.cleanUpPaste.defer(100, this);
24548                     return;
24549                 }
24550                 
24551                 
24552             };
24553         }else if(Roo.isOpera){
24554             return function(e){
24555                 var k = e.getKey();
24556                 if(k == e.TAB){
24557                     e.stopEvent();
24558                     this.win.focus();
24559                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24560                     this.deferFocus();
24561                 }
24562                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24563                     this.cleanUpPaste.defer(100, this);
24564                     return;
24565                 }
24566                 
24567             };
24568         }else if(Roo.isSafari){
24569             return function(e){
24570                 var k = e.getKey();
24571                 
24572                 if(k == e.TAB){
24573                     e.stopEvent();
24574                     this.execCmd('InsertText','\t');
24575                     this.deferFocus();
24576                     return;
24577                 }
24578                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24579                     this.cleanUpPaste.defer(100, this);
24580                     return;
24581                 }
24582                 
24583              };
24584         }
24585     }(),
24586     
24587     getAllAncestors: function()
24588     {
24589         var p = this.getSelectedNode();
24590         var a = [];
24591         if (!p) {
24592             a.push(p); // push blank onto stack..
24593             p = this.getParentElement();
24594         }
24595         
24596         
24597         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24598             a.push(p);
24599             p = p.parentNode;
24600         }
24601         a.push(this.doc.body);
24602         return a;
24603     },
24604     lastSel : false,
24605     lastSelNode : false,
24606     
24607     
24608     getSelection : function() 
24609     {
24610         this.assignDocWin();
24611         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24612     },
24613     
24614     getSelectedNode: function() 
24615     {
24616         // this may only work on Gecko!!!
24617         
24618         // should we cache this!!!!
24619         
24620         
24621         
24622          
24623         var range = this.createRange(this.getSelection());
24624         
24625         if (Roo.isIE) {
24626             var parent = range.parentElement();
24627             while (true) {
24628                 var testRange = range.duplicate();
24629                 testRange.moveToElementText(parent);
24630                 if (testRange.inRange(range)) {
24631                     break;
24632                 }
24633                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24634                     break;
24635                 }
24636                 parent = parent.parentElement;
24637             }
24638             return parent;
24639         }
24640         
24641         
24642         var ar = range.endContainer.childNodes;
24643         if (!ar.length) {
24644             ar = range.commonAncestorContainer.childNodes;
24645             //alert(ar.length);
24646         }
24647         var nodes = [];
24648         var other_nodes = [];
24649         var has_other_nodes = false;
24650         for (var i=0;i<ar.length;i++) {
24651             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24652                 continue;
24653             }
24654             // fullly contained node.
24655             
24656             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24657                 nodes.push(ar[i]);
24658                 continue;
24659             }
24660             
24661             // probably selected..
24662             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24663                 other_nodes.push(ar[i]);
24664                 continue;
24665             }
24666             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24667                 continue;
24668             }
24669             
24670             
24671             has_other_nodes = true;
24672         }
24673         if (!nodes.length && other_nodes.length) {
24674             nodes= other_nodes;
24675         }
24676         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24677             return false;
24678         }
24679         
24680         return nodes[0];
24681     },
24682     createRange: function(sel)
24683     {
24684         // this has strange effects when using with 
24685         // top toolbar - not sure if it's a great idea.
24686         //this.editor.contentWindow.focus();
24687         if (typeof sel != "undefined") {
24688             try {
24689                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24690             } catch(e) {
24691                 return this.doc.createRange();
24692             }
24693         } else {
24694             return this.doc.createRange();
24695         }
24696     },
24697     getParentElement: function()
24698     {
24699         
24700         this.assignDocWin();
24701         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24702         
24703         var range = this.createRange(sel);
24704          
24705         try {
24706             var p = range.commonAncestorContainer;
24707             while (p.nodeType == 3) { // text node
24708                 p = p.parentNode;
24709             }
24710             return p;
24711         } catch (e) {
24712             return null;
24713         }
24714     
24715     },
24716     
24717     
24718     
24719     // BC Hacks - cause I cant work out what i was trying to do..
24720     rangeIntersectsNode : function(range, node)
24721     {
24722         var nodeRange = node.ownerDocument.createRange();
24723         try {
24724             nodeRange.selectNode(node);
24725         }
24726         catch (e) {
24727             nodeRange.selectNodeContents(node);
24728         }
24729
24730         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24731                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24732     },
24733     rangeCompareNode : function(range, node) {
24734         var nodeRange = node.ownerDocument.createRange();
24735         try {
24736             nodeRange.selectNode(node);
24737         } catch (e) {
24738             nodeRange.selectNodeContents(node);
24739         }
24740         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24741         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24742
24743         if (nodeIsBefore && !nodeIsAfter)
24744             return 0;
24745         if (!nodeIsBefore && nodeIsAfter)
24746             return 1;
24747         if (nodeIsBefore && nodeIsAfter)
24748             return 2;
24749
24750         return 3;
24751     },
24752
24753     // private? - in a new class?
24754     cleanUpPaste :  function()
24755     {
24756         // cleans up the whole document..
24757       //  console.log('cleanuppaste');
24758         this.cleanUpChildren(this.doc.body);
24759         
24760         
24761     },
24762     cleanUpChildren : function (n)
24763     {
24764         if (!n.childNodes.length) {
24765             return;
24766         }
24767         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24768            this.cleanUpChild(n.childNodes[i]);
24769         }
24770     },
24771     
24772     
24773         
24774     
24775     cleanUpChild : function (node)
24776     {
24777         //console.log(node);
24778         if (node.nodeName == "#text") {
24779             // clean up silly Windows -- stuff?
24780             return; 
24781         }
24782         if (node.nodeName == "#comment") {
24783             node.parentNode.removeChild(node);
24784             // clean up silly Windows -- stuff?
24785             return; 
24786         }
24787         
24788         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24789             // remove node.
24790             node.parentNode.removeChild(node);
24791             return;
24792             
24793         }
24794         if (!node.attributes || !node.attributes.length) {
24795             this.cleanUpChildren(node);
24796             return;
24797         }
24798         
24799         function cleanAttr(n,v)
24800         {
24801             
24802             if (v.match(/^\./) || v.match(/^\//)) {
24803                 return;
24804             }
24805             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24806                 return;
24807             }
24808             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24809             node.removeAttribute(n);
24810             
24811         }
24812         
24813         function cleanStyle(n,v)
24814         {
24815             if (v.match(/expression/)) { //XSS?? should we even bother..
24816                 node.removeAttribute(n);
24817                 return;
24818             }
24819             
24820             
24821             var parts = v.split(/;/);
24822             Roo.each(parts, function(p) {
24823                 p = p.replace(/\s+/g,'');
24824                 if (!p.length) {
24825                     return;
24826                 }
24827                 var l = p.split(':').shift().replace(/\s+/g,'');
24828                 
24829                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24830                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24831                     node.removeAttribute(n);
24832                     return false;
24833                 }
24834             });
24835             
24836             
24837         }
24838         
24839         
24840         for (var i = node.attributes.length-1; i > -1 ; i--) {
24841             var a = node.attributes[i];
24842             //console.log(a);
24843             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24844                 node.removeAttribute(a.name);
24845                 return;
24846             }
24847             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24848                 cleanAttr(a.name,a.value); // fixme..
24849                 return;
24850             }
24851             if (a.name == 'style') {
24852                 cleanStyle(a.name,a.value);
24853             }
24854             /// clean up MS crap..
24855             if (a.name == 'class') {
24856                 if (a.value.match(/^Mso/)) {
24857                     node.className = '';
24858                 }
24859             }
24860             
24861             // style cleanup!?
24862             // class cleanup?
24863             
24864         }
24865         
24866         
24867         this.cleanUpChildren(node);
24868         
24869         
24870     }
24871     
24872     
24873     // hide stuff that is not compatible
24874     /**
24875      * @event blur
24876      * @hide
24877      */
24878     /**
24879      * @event change
24880      * @hide
24881      */
24882     /**
24883      * @event focus
24884      * @hide
24885      */
24886     /**
24887      * @event specialkey
24888      * @hide
24889      */
24890     /**
24891      * @cfg {String} fieldClass @hide
24892      */
24893     /**
24894      * @cfg {String} focusClass @hide
24895      */
24896     /**
24897      * @cfg {String} autoCreate @hide
24898      */
24899     /**
24900      * @cfg {String} inputType @hide
24901      */
24902     /**
24903      * @cfg {String} invalidClass @hide
24904      */
24905     /**
24906      * @cfg {String} invalidText @hide
24907      */
24908     /**
24909      * @cfg {String} msgFx @hide
24910      */
24911     /**
24912      * @cfg {String} validateOnBlur @hide
24913      */
24914 });
24915
24916 Roo.form.HtmlEditor.white = [
24917         'area', 'br', 'img', 'input', 'hr', 'wbr',
24918         
24919        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24920        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24921        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24922        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24923        'table',   'ul',         'xmp', 
24924        
24925        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24926       'thead',   'tr', 
24927      
24928       'dir', 'menu', 'ol', 'ul', 'dl',
24929        
24930       'embed',  'object'
24931 ];
24932
24933
24934 Roo.form.HtmlEditor.black = [
24935     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24936         'applet', // 
24937         'base',   'basefont', 'bgsound', 'blink',  'body', 
24938         'frame',  'frameset', 'head',    'html',   'ilayer', 
24939         'iframe', 'layer',  'link',     'meta',    'object',   
24940         'script', 'style' ,'title',  'xml' // clean later..
24941 ];
24942 Roo.form.HtmlEditor.clean = [
24943     'script', 'style', 'title', 'xml'
24944 ];
24945
24946 // attributes..
24947
24948 Roo.form.HtmlEditor.ablack = [
24949     'on'
24950 ];
24951     
24952 Roo.form.HtmlEditor.aclean = [ 
24953     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24954 ];
24955
24956 // protocols..
24957 Roo.form.HtmlEditor.pwhite= [
24958         'http',  'https',  'mailto'
24959 ];
24960
24961 Roo.form.HtmlEditor.cwhite= [
24962         'text-align',
24963         'font-size'
24964 ];
24965
24966 // <script type="text/javascript">
24967 /*
24968  * Based on
24969  * Ext JS Library 1.1.1
24970  * Copyright(c) 2006-2007, Ext JS, LLC.
24971  *  
24972  
24973  */
24974
24975 /**
24976  * @class Roo.form.HtmlEditorToolbar1
24977  * Basic Toolbar
24978  * 
24979  * Usage:
24980  *
24981  new Roo.form.HtmlEditor({
24982     ....
24983     toolbars : [
24984         new Roo.form.HtmlEditorToolbar1({
24985             disable : { fonts: 1 , format: 1, ..., ... , ...],
24986             btns : [ .... ]
24987         })
24988     }
24989      
24990  * 
24991  * @cfg {Object} disable List of elements to disable..
24992  * @cfg {Array} btns List of additional buttons.
24993  * 
24994  * 
24995  * NEEDS Extra CSS? 
24996  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24997  */
24998  
24999 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25000 {
25001     
25002     Roo.apply(this, config);
25003     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25004     // dont call parent... till later.
25005 }
25006
25007 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25008     
25009     tb: false,
25010     
25011     rendered: false,
25012     
25013     editor : false,
25014     /**
25015      * @cfg {Object} disable  List of toolbar elements to disable
25016          
25017      */
25018     disable : false,
25019       /**
25020      * @cfg {Array} fontFamilies An array of available font families
25021      */
25022     fontFamilies : [
25023         'Arial',
25024         'Courier New',
25025         'Tahoma',
25026         'Times New Roman',
25027         'Verdana'
25028     ],
25029     
25030     specialChars : [
25031            "&#169;",
25032           "&#174;",     
25033           "&#8482;",    
25034           "&#163;" ,    
25035          // "&#8212;",    
25036           "&#8230;",    
25037           "&#247;" ,    
25038         //  "&#225;" ,     ?? a acute?
25039            "&#8364;"    , //Euro
25040        //   "&#8220;"    ,
25041         //  "&#8221;"    ,
25042         //  "&#8226;"    ,
25043           "&#176;"  //   , // degrees
25044
25045          // "&#233;"     , // e ecute
25046          // "&#250;"     , // u ecute?
25047     ],
25048     inputElements : [ 
25049             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25050             "input:submit", "input:button", "select", "textarea", "label" ],
25051     formats : [
25052         ["p"] ,  
25053         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25054         ["pre"],[ "code"], 
25055         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25056     ],
25057      /**
25058      * @cfg {String} defaultFont default font to use.
25059      */
25060     defaultFont: 'tahoma',
25061    
25062     fontSelect : false,
25063     
25064     
25065     formatCombo : false,
25066     
25067     init : function(editor)
25068     {
25069         this.editor = editor;
25070         
25071         
25072         var fid = editor.frameId;
25073         var etb = this;
25074         function btn(id, toggle, handler){
25075             var xid = fid + '-'+ id ;
25076             return {
25077                 id : xid,
25078                 cmd : id,
25079                 cls : 'x-btn-icon x-edit-'+id,
25080                 enableToggle:toggle !== false,
25081                 scope: editor, // was editor...
25082                 handler:handler||editor.relayBtnCmd,
25083                 clickEvent:'mousedown',
25084                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25085                 tabIndex:-1
25086             };
25087         }
25088         
25089         
25090         
25091         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25092         this.tb = tb;
25093          // stop form submits
25094         tb.el.on('click', function(e){
25095             e.preventDefault(); // what does this do?
25096         });
25097
25098         if(!this.disable.font && !Roo.isSafari){
25099             /* why no safari for fonts
25100             editor.fontSelect = tb.el.createChild({
25101                 tag:'select',
25102                 tabIndex: -1,
25103                 cls:'x-font-select',
25104                 html: editor.createFontOptions()
25105             });
25106             editor.fontSelect.on('change', function(){
25107                 var font = editor.fontSelect.dom.value;
25108                 editor.relayCmd('fontname', font);
25109                 editor.deferFocus();
25110             }, editor);
25111             tb.add(
25112                 editor.fontSelect.dom,
25113                 '-'
25114             );
25115             */
25116         };
25117         if(!this.disable.formats){
25118             this.formatCombo = new Roo.form.ComboBox({
25119                 store: new Roo.data.SimpleStore({
25120                     id : 'tag',
25121                     fields: ['tag'],
25122                     data : this.formats // from states.js
25123                 }),
25124                 blockFocus : true,
25125                 //autoCreate : {tag: "div",  size: "20"},
25126                 displayField:'tag',
25127                 typeAhead: false,
25128                 mode: 'local',
25129                 editable : false,
25130                 triggerAction: 'all',
25131                 emptyText:'Add tag',
25132                 selectOnFocus:true,
25133                 width:135,
25134                 listeners : {
25135                     'select': function(c, r, i) {
25136                         editor.insertTag(r.get('tag'));
25137                         editor.focus();
25138                     }
25139                 }
25140
25141             });
25142             tb.addField(this.formatCombo);
25143             
25144         }
25145         
25146         if(!this.disable.format){
25147             tb.add(
25148                 btn('bold'),
25149                 btn('italic'),
25150                 btn('underline')
25151             );
25152         };
25153         if(!this.disable.fontSize){
25154             tb.add(
25155                 '-',
25156                 
25157                 
25158                 btn('increasefontsize', false, editor.adjustFont),
25159                 btn('decreasefontsize', false, editor.adjustFont)
25160             );
25161         };
25162         
25163         
25164         if(this.disable.colors){
25165             tb.add(
25166                 '-', {
25167                     id:editor.frameId +'-forecolor',
25168                     cls:'x-btn-icon x-edit-forecolor',
25169                     clickEvent:'mousedown',
25170                     tooltip: this.buttonTips['forecolor'] || undefined,
25171                     tabIndex:-1,
25172                     menu : new Roo.menu.ColorMenu({
25173                         allowReselect: true,
25174                         focus: Roo.emptyFn,
25175                         value:'000000',
25176                         plain:true,
25177                         selectHandler: function(cp, color){
25178                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25179                             editor.deferFocus();
25180                         },
25181                         scope: editor,
25182                         clickEvent:'mousedown'
25183                     })
25184                 }, {
25185                     id:editor.frameId +'backcolor',
25186                     cls:'x-btn-icon x-edit-backcolor',
25187                     clickEvent:'mousedown',
25188                     tooltip: this.buttonTips['backcolor'] || undefined,
25189                     tabIndex:-1,
25190                     menu : new Roo.menu.ColorMenu({
25191                         focus: Roo.emptyFn,
25192                         value:'FFFFFF',
25193                         plain:true,
25194                         allowReselect: true,
25195                         selectHandler: function(cp, color){
25196                             if(Roo.isGecko){
25197                                 editor.execCmd('useCSS', false);
25198                                 editor.execCmd('hilitecolor', color);
25199                                 editor.execCmd('useCSS', true);
25200                                 editor.deferFocus();
25201                             }else{
25202                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25203                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25204                                 editor.deferFocus();
25205                             }
25206                         },
25207                         scope:editor,
25208                         clickEvent:'mousedown'
25209                     })
25210                 }
25211             );
25212         };
25213         // now add all the items...
25214         
25215
25216         if(!this.disable.alignments){
25217             tb.add(
25218                 '-',
25219                 btn('justifyleft'),
25220                 btn('justifycenter'),
25221                 btn('justifyright')
25222             );
25223         };
25224
25225         //if(!Roo.isSafari){
25226             if(!this.disable.links){
25227                 tb.add(
25228                     '-',
25229                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25230                 );
25231             };
25232
25233             if(!this.disable.lists){
25234                 tb.add(
25235                     '-',
25236                     btn('insertorderedlist'),
25237                     btn('insertunorderedlist')
25238                 );
25239             }
25240             if(!this.disable.sourceEdit){
25241                 tb.add(
25242                     '-',
25243                     btn('sourceedit', true, function(btn){
25244                         this.toggleSourceEdit(btn.pressed);
25245                     })
25246                 );
25247             }
25248         //}
25249         
25250         var smenu = { };
25251         // special menu.. - needs to be tidied up..
25252         if (!this.disable.special) {
25253             smenu = {
25254                 text: "&#169;",
25255                 cls: 'x-edit-none',
25256                 menu : {
25257                     items : []
25258                    }
25259             };
25260             for (var i =0; i < this.specialChars.length; i++) {
25261                 smenu.menu.items.push({
25262                     
25263                     html: this.specialChars[i],
25264                     handler: function(a,b) {
25265                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25266                         
25267                     },
25268                     tabIndex:-1
25269                 });
25270             }
25271             
25272             
25273             tb.add(smenu);
25274             
25275             
25276         }
25277         if (this.btns) {
25278             for(var i =0; i< this.btns.length;i++) {
25279                 var b = this.btns[i];
25280                 b.cls =  'x-edit-none';
25281                 b.scope = editor;
25282                 tb.add(b);
25283             }
25284         
25285         }
25286         
25287         
25288         
25289         // disable everything...
25290         
25291         this.tb.items.each(function(item){
25292            if(item.id != editor.frameId+ '-sourceedit'){
25293                 item.disable();
25294             }
25295         });
25296         this.rendered = true;
25297         
25298         // the all the btns;
25299         editor.on('editorevent', this.updateToolbar, this);
25300         // other toolbars need to implement this..
25301         //editor.on('editmodechange', this.updateToolbar, this);
25302     },
25303     
25304     
25305     
25306     /**
25307      * Protected method that will not generally be called directly. It triggers
25308      * a toolbar update by reading the markup state of the current selection in the editor.
25309      */
25310     updateToolbar: function(){
25311
25312         if(!this.editor.activated){
25313             this.editor.onFirstFocus();
25314             return;
25315         }
25316
25317         var btns = this.tb.items.map, 
25318             doc = this.editor.doc,
25319             frameId = this.editor.frameId;
25320
25321         if(!this.disable.font && !Roo.isSafari){
25322             /*
25323             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25324             if(name != this.fontSelect.dom.value){
25325                 this.fontSelect.dom.value = name;
25326             }
25327             */
25328         }
25329         if(!this.disable.format){
25330             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25331             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25332             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25333         }
25334         if(!this.disable.alignments){
25335             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25336             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25337             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25338         }
25339         if(!Roo.isSafari && !this.disable.lists){
25340             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25341             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25342         }
25343         
25344         var ans = this.editor.getAllAncestors();
25345         if (this.formatCombo) {
25346             
25347             
25348             var store = this.formatCombo.store;
25349             this.formatCombo.setValue("");
25350             for (var i =0; i < ans.length;i++) {
25351                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25352                     // select it..
25353                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25354                     break;
25355                 }
25356             }
25357         }
25358         
25359         
25360         
25361         // hides menus... - so this cant be on a menu...
25362         Roo.menu.MenuMgr.hideAll();
25363
25364         //this.editorsyncValue();
25365     },
25366    
25367     
25368     createFontOptions : function(){
25369         var buf = [], fs = this.fontFamilies, ff, lc;
25370         for(var i = 0, len = fs.length; i< len; i++){
25371             ff = fs[i];
25372             lc = ff.toLowerCase();
25373             buf.push(
25374                 '<option value="',lc,'" style="font-family:',ff,';"',
25375                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25376                     ff,
25377                 '</option>'
25378             );
25379         }
25380         return buf.join('');
25381     },
25382     
25383     toggleSourceEdit : function(sourceEditMode){
25384         if(sourceEditMode === undefined){
25385             sourceEditMode = !this.sourceEditMode;
25386         }
25387         this.sourceEditMode = sourceEditMode === true;
25388         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25389         // just toggle the button?
25390         if(btn.pressed !== this.editor.sourceEditMode){
25391             btn.toggle(this.editor.sourceEditMode);
25392             return;
25393         }
25394         
25395         if(this.sourceEditMode){
25396             this.tb.items.each(function(item){
25397                 if(item.cmd != 'sourceedit'){
25398                     item.disable();
25399                 }
25400             });
25401           
25402         }else{
25403             if(this.initialized){
25404                 this.tb.items.each(function(item){
25405                     item.enable();
25406                 });
25407             }
25408             
25409         }
25410         // tell the editor that it's been pressed..
25411         this.editor.toggleSourceEdit(sourceEditMode);
25412        
25413     },
25414      /**
25415      * Object collection of toolbar tooltips for the buttons in the editor. The key
25416      * is the command id associated with that button and the value is a valid QuickTips object.
25417      * For example:
25418 <pre><code>
25419 {
25420     bold : {
25421         title: 'Bold (Ctrl+B)',
25422         text: 'Make the selected text bold.',
25423         cls: 'x-html-editor-tip'
25424     },
25425     italic : {
25426         title: 'Italic (Ctrl+I)',
25427         text: 'Make the selected text italic.',
25428         cls: 'x-html-editor-tip'
25429     },
25430     ...
25431 </code></pre>
25432     * @type Object
25433      */
25434     buttonTips : {
25435         bold : {
25436             title: 'Bold (Ctrl+B)',
25437             text: 'Make the selected text bold.',
25438             cls: 'x-html-editor-tip'
25439         },
25440         italic : {
25441             title: 'Italic (Ctrl+I)',
25442             text: 'Make the selected text italic.',
25443             cls: 'x-html-editor-tip'
25444         },
25445         underline : {
25446             title: 'Underline (Ctrl+U)',
25447             text: 'Underline the selected text.',
25448             cls: 'x-html-editor-tip'
25449         },
25450         increasefontsize : {
25451             title: 'Grow Text',
25452             text: 'Increase the font size.',
25453             cls: 'x-html-editor-tip'
25454         },
25455         decreasefontsize : {
25456             title: 'Shrink Text',
25457             text: 'Decrease the font size.',
25458             cls: 'x-html-editor-tip'
25459         },
25460         backcolor : {
25461             title: 'Text Highlight Color',
25462             text: 'Change the background color of the selected text.',
25463             cls: 'x-html-editor-tip'
25464         },
25465         forecolor : {
25466             title: 'Font Color',
25467             text: 'Change the color of the selected text.',
25468             cls: 'x-html-editor-tip'
25469         },
25470         justifyleft : {
25471             title: 'Align Text Left',
25472             text: 'Align text to the left.',
25473             cls: 'x-html-editor-tip'
25474         },
25475         justifycenter : {
25476             title: 'Center Text',
25477             text: 'Center text in the editor.',
25478             cls: 'x-html-editor-tip'
25479         },
25480         justifyright : {
25481             title: 'Align Text Right',
25482             text: 'Align text to the right.',
25483             cls: 'x-html-editor-tip'
25484         },
25485         insertunorderedlist : {
25486             title: 'Bullet List',
25487             text: 'Start a bulleted list.',
25488             cls: 'x-html-editor-tip'
25489         },
25490         insertorderedlist : {
25491             title: 'Numbered List',
25492             text: 'Start a numbered list.',
25493             cls: 'x-html-editor-tip'
25494         },
25495         createlink : {
25496             title: 'Hyperlink',
25497             text: 'Make the selected text a hyperlink.',
25498             cls: 'x-html-editor-tip'
25499         },
25500         sourceedit : {
25501             title: 'Source Edit',
25502             text: 'Switch to source editing mode.',
25503             cls: 'x-html-editor-tip'
25504         }
25505     },
25506     // private
25507     onDestroy : function(){
25508         if(this.rendered){
25509             
25510             this.tb.items.each(function(item){
25511                 if(item.menu){
25512                     item.menu.removeAll();
25513                     if(item.menu.el){
25514                         item.menu.el.destroy();
25515                     }
25516                 }
25517                 item.destroy();
25518             });
25519              
25520         }
25521     },
25522     onFirstFocus: function() {
25523         this.tb.items.each(function(item){
25524            item.enable();
25525         });
25526     }
25527 });
25528
25529
25530
25531
25532 // <script type="text/javascript">
25533 /*
25534  * Based on
25535  * Ext JS Library 1.1.1
25536  * Copyright(c) 2006-2007, Ext JS, LLC.
25537  *  
25538  
25539  */
25540
25541  
25542 /**
25543  * @class Roo.form.HtmlEditor.ToolbarContext
25544  * Context Toolbar
25545  * 
25546  * Usage:
25547  *
25548  new Roo.form.HtmlEditor({
25549     ....
25550     toolbars : [
25551         new Roo.form.HtmlEditor.ToolbarStandard(),
25552         new Roo.form.HtmlEditor.ToolbarContext()
25553         })
25554     }
25555      
25556  * 
25557  * @config : {Object} disable List of elements to disable.. (not done yet.)
25558  * 
25559  * 
25560  */
25561
25562 Roo.form.HtmlEditor.ToolbarContext = function(config)
25563 {
25564     
25565     Roo.apply(this, config);
25566     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25567     // dont call parent... till later.
25568 }
25569 Roo.form.HtmlEditor.ToolbarContext.types = {
25570     'IMG' : {
25571         width : {
25572             title: "Width",
25573             width: 40
25574         },
25575         height:  {
25576             title: "Height",
25577             width: 40
25578         },
25579         align: {
25580             title: "Align",
25581             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25582             width : 80
25583             
25584         },
25585         border: {
25586             title: "Border",
25587             width: 40
25588         },
25589         alt: {
25590             title: "Alt",
25591             width: 120
25592         },
25593         src : {
25594             title: "Src",
25595             width: 220
25596         }
25597         
25598     },
25599     'A' : {
25600         name : {
25601             title: "Name",
25602             width: 50
25603         },
25604         href:  {
25605             title: "Href",
25606             width: 220
25607         } // border?
25608         
25609     },
25610     'TABLE' : {
25611         rows : {
25612             title: "Rows",
25613             width: 20
25614         },
25615         cols : {
25616             title: "Cols",
25617             width: 20
25618         },
25619         width : {
25620             title: "Width",
25621             width: 40
25622         },
25623         height : {
25624             title: "Height",
25625             width: 40
25626         },
25627         border : {
25628             title: "Border",
25629             width: 20
25630         }
25631     },
25632     'TD' : {
25633         width : {
25634             title: "Width",
25635             width: 40
25636         },
25637         height : {
25638             title: "Height",
25639             width: 40
25640         },   
25641         align: {
25642             title: "Align",
25643             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25644             width: 40
25645         },
25646         valign: {
25647             title: "Valign",
25648             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25649             width: 40
25650         },
25651         colspan: {
25652             title: "Colspan",
25653             width: 20
25654             
25655         }
25656     },
25657     'INPUT' : {
25658         name : {
25659             title: "name",
25660             width: 120
25661         },
25662         value : {
25663             title: "Value",
25664             width: 120
25665         },
25666         width : {
25667             title: "Width",
25668             width: 40
25669         }
25670     },
25671     'LABEL' : {
25672         'for' : {
25673             title: "For",
25674             width: 120
25675         }
25676     },
25677     'TEXTAREA' : {
25678           name : {
25679             title: "name",
25680             width: 120
25681         },
25682         rows : {
25683             title: "Rows",
25684             width: 20
25685         },
25686         cols : {
25687             title: "Cols",
25688             width: 20
25689         }
25690     },
25691     'SELECT' : {
25692         name : {
25693             title: "name",
25694             width: 120
25695         },
25696         selectoptions : {
25697             title: "Options",
25698             width: 200
25699         }
25700     },
25701     'BODY' : {
25702         title : {
25703             title: "title",
25704             width: 120,
25705             disabled : true
25706         }
25707     }
25708 };
25709
25710
25711
25712 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25713     
25714     tb: false,
25715     
25716     rendered: false,
25717     
25718     editor : false,
25719     /**
25720      * @cfg {Object} disable  List of toolbar elements to disable
25721          
25722      */
25723     disable : false,
25724     
25725     
25726     
25727     toolbars : false,
25728     
25729     init : function(editor)
25730     {
25731         this.editor = editor;
25732         
25733         
25734         var fid = editor.frameId;
25735         var etb = this;
25736         function btn(id, toggle, handler){
25737             var xid = fid + '-'+ id ;
25738             return {
25739                 id : xid,
25740                 cmd : id,
25741                 cls : 'x-btn-icon x-edit-'+id,
25742                 enableToggle:toggle !== false,
25743                 scope: editor, // was editor...
25744                 handler:handler||editor.relayBtnCmd,
25745                 clickEvent:'mousedown',
25746                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25747                 tabIndex:-1
25748             };
25749         }
25750         // create a new element.
25751         var wdiv = editor.wrap.createChild({
25752                 tag: 'div'
25753             }, editor.wrap.dom.firstChild.nextSibling, true);
25754         
25755         // can we do this more than once??
25756         
25757          // stop form submits
25758       
25759  
25760         // disable everything...
25761         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25762         this.toolbars = {};
25763            
25764         for (var i in  ty) {
25765           
25766             this.toolbars[i] = this.buildToolbar(ty[i],i);
25767         }
25768         this.tb = this.toolbars.BODY;
25769         this.tb.el.show();
25770         
25771          
25772         this.rendered = true;
25773         
25774         // the all the btns;
25775         editor.on('editorevent', this.updateToolbar, this);
25776         // other toolbars need to implement this..
25777         //editor.on('editmodechange', this.updateToolbar, this);
25778     },
25779     
25780     
25781     
25782     /**
25783      * Protected method that will not generally be called directly. It triggers
25784      * a toolbar update by reading the markup state of the current selection in the editor.
25785      */
25786     updateToolbar: function(){
25787
25788         if(!this.editor.activated){
25789             this.editor.onFirstFocus();
25790             return;
25791         }
25792
25793         
25794         var ans = this.editor.getAllAncestors();
25795         
25796         // pick
25797         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25798         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25799         sel = sel ? sel : this.editor.doc.body;
25800         sel = sel.tagName.length ? sel : this.editor.doc.body;
25801         var tn = sel.tagName.toUpperCase();
25802         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25803         tn = sel.tagName.toUpperCase();
25804         if (this.tb.name  == tn) {
25805             return; // no change
25806         }
25807         this.tb.el.hide();
25808         ///console.log("show: " + tn);
25809         this.tb =  this.toolbars[tn];
25810         this.tb.el.show();
25811         this.tb.fields.each(function(e) {
25812             e.setValue(sel.getAttribute(e.name));
25813         });
25814         this.tb.selectedNode = sel;
25815         
25816         
25817         Roo.menu.MenuMgr.hideAll();
25818
25819         //this.editorsyncValue();
25820     },
25821    
25822        
25823     // private
25824     onDestroy : function(){
25825         if(this.rendered){
25826             
25827             this.tb.items.each(function(item){
25828                 if(item.menu){
25829                     item.menu.removeAll();
25830                     if(item.menu.el){
25831                         item.menu.el.destroy();
25832                     }
25833                 }
25834                 item.destroy();
25835             });
25836              
25837         }
25838     },
25839     onFirstFocus: function() {
25840         // need to do this for all the toolbars..
25841         this.tb.items.each(function(item){
25842            item.enable();
25843         });
25844     },
25845     buildToolbar: function(tlist, nm)
25846     {
25847         var editor = this.editor;
25848          // create a new element.
25849         var wdiv = editor.wrap.createChild({
25850                 tag: 'div'
25851             }, editor.wrap.dom.firstChild.nextSibling, true);
25852         
25853        
25854         var tb = new Roo.Toolbar(wdiv);
25855         tb.add(nm+ ":&nbsp;");
25856         for (var i in tlist) {
25857             var item = tlist[i];
25858             tb.add(item.title + ":&nbsp;");
25859             if (item.opts) {
25860                 // fixme
25861                 
25862               
25863                 tb.addField( new Roo.form.ComboBox({
25864                     store: new Roo.data.SimpleStore({
25865                         id : 'val',
25866                         fields: ['val'],
25867                         data : item.opts // from states.js
25868                     }),
25869                     name : i,
25870                     displayField:'val',
25871                     typeAhead: false,
25872                     mode: 'local',
25873                     editable : false,
25874                     triggerAction: 'all',
25875                     emptyText:'Select',
25876                     selectOnFocus:true,
25877                     width: item.width ? item.width  : 130,
25878                     listeners : {
25879                         'select': function(c, r, i) {
25880                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25881                         }
25882                     }
25883
25884                 }));
25885                 continue;
25886                     
25887                 
25888                 
25889                 
25890                 
25891                 tb.addField( new Roo.form.TextField({
25892                     name: i,
25893                     width: 100,
25894                     //allowBlank:false,
25895                     value: ''
25896                 }));
25897                 continue;
25898             }
25899             tb.addField( new Roo.form.TextField({
25900                 name: i,
25901                 width: item.width,
25902                 //allowBlank:true,
25903                 value: '',
25904                 listeners: {
25905                     'change' : function(f, nv, ov) {
25906                         tb.selectedNode.setAttribute(f.name, nv);
25907                     }
25908                 }
25909             }));
25910              
25911         }
25912         tb.el.on('click', function(e){
25913             e.preventDefault(); // what does this do?
25914         });
25915         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25916         tb.el.hide();
25917         tb.name = nm;
25918         // dont need to disable them... as they will get hidden
25919         return tb;
25920          
25921         
25922     }
25923     
25924     
25925     
25926     
25927 });
25928
25929
25930
25931
25932
25933 /*
25934  * Based on:
25935  * Ext JS Library 1.1.1
25936  * Copyright(c) 2006-2007, Ext JS, LLC.
25937  *
25938  * Originally Released Under LGPL - original licence link has changed is not relivant.
25939  *
25940  * Fork - LGPL
25941  * <script type="text/javascript">
25942  */
25943  
25944 /**
25945  * @class Roo.form.BasicForm
25946  * @extends Roo.util.Observable
25947  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25948  * @constructor
25949  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25950  * @param {Object} config Configuration options
25951  */
25952 Roo.form.BasicForm = function(el, config){
25953     this.allItems = [];
25954     this.childForms = [];
25955     Roo.apply(this, config);
25956     /*
25957      * The Roo.form.Field items in this form.
25958      * @type MixedCollection
25959      */
25960      
25961      
25962     this.items = new Roo.util.MixedCollection(false, function(o){
25963         return o.id || (o.id = Roo.id());
25964     });
25965     this.addEvents({
25966         /**
25967          * @event beforeaction
25968          * Fires before any action is performed. Return false to cancel the action.
25969          * @param {Form} this
25970          * @param {Action} action The action to be performed
25971          */
25972         beforeaction: true,
25973         /**
25974          * @event actionfailed
25975          * Fires when an action fails.
25976          * @param {Form} this
25977          * @param {Action} action The action that failed
25978          */
25979         actionfailed : true,
25980         /**
25981          * @event actioncomplete
25982          * Fires when an action is completed.
25983          * @param {Form} this
25984          * @param {Action} action The action that completed
25985          */
25986         actioncomplete : true
25987     });
25988     if(el){
25989         this.initEl(el);
25990     }
25991     Roo.form.BasicForm.superclass.constructor.call(this);
25992 };
25993
25994 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25995     /**
25996      * @cfg {String} method
25997      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25998      */
25999     /**
26000      * @cfg {DataReader} reader
26001      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
26002      * This is optional as there is built-in support for processing JSON.
26003      */
26004     /**
26005      * @cfg {DataReader} errorReader
26006      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
26007      * This is completely optional as there is built-in support for processing JSON.
26008      */
26009     /**
26010      * @cfg {String} url
26011      * The URL to use for form actions if one isn't supplied in the action options.
26012      */
26013     /**
26014      * @cfg {Boolean} fileUpload
26015      * Set to true if this form is a file upload.
26016      */
26017      
26018     /**
26019      * @cfg {Object} baseParams
26020      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
26021      */
26022      /**
26023      
26024     /**
26025      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
26026      */
26027     timeout: 30,
26028
26029     // private
26030     activeAction : null,
26031
26032     /**
26033      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
26034      * or setValues() data instead of when the form was first created.
26035      */
26036     trackResetOnLoad : false,
26037     
26038     
26039     /**
26040      * childForms - used for multi-tab forms
26041      * @type {Array}
26042      */
26043     childForms : false,
26044     
26045     /**
26046      * allItems - full list of fields.
26047      * @type {Array}
26048      */
26049     allItems : false,
26050     
26051     /**
26052      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26053      * element by passing it or its id or mask the form itself by passing in true.
26054      * @type Mixed
26055      */
26056     waitMsgTarget : false,
26057
26058     // private
26059     initEl : function(el){
26060         this.el = Roo.get(el);
26061         this.id = this.el.id || Roo.id();
26062         this.el.on('submit', this.onSubmit, this);
26063         this.el.addClass('x-form');
26064     },
26065
26066     // private
26067     onSubmit : function(e){
26068         e.stopEvent();
26069     },
26070
26071     /**
26072      * Returns true if client-side validation on the form is successful.
26073      * @return Boolean
26074      */
26075     isValid : function(){
26076         var valid = true;
26077         this.items.each(function(f){
26078            if(!f.validate()){
26079                valid = false;
26080            }
26081         });
26082         return valid;
26083     },
26084
26085     /**
26086      * Returns true if any fields in this form have changed since their original load.
26087      * @return Boolean
26088      */
26089     isDirty : function(){
26090         var dirty = false;
26091         this.items.each(function(f){
26092            if(f.isDirty()){
26093                dirty = true;
26094                return false;
26095            }
26096         });
26097         return dirty;
26098     },
26099
26100     /**
26101      * Performs a predefined action (submit or load) or custom actions you define on this form.
26102      * @param {String} actionName The name of the action type
26103      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26104      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26105      * accept other config options):
26106      * <pre>
26107 Property          Type             Description
26108 ----------------  ---------------  ----------------------------------------------------------------------------------
26109 url               String           The url for the action (defaults to the form's url)
26110 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26111 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26112 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26113                                    validate the form on the client (defaults to false)
26114      * </pre>
26115      * @return {BasicForm} this
26116      */
26117     doAction : function(action, options){
26118         if(typeof action == 'string'){
26119             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26120         }
26121         if(this.fireEvent('beforeaction', this, action) !== false){
26122             this.beforeAction(action);
26123             action.run.defer(100, action);
26124         }
26125         return this;
26126     },
26127
26128     /**
26129      * Shortcut to do a submit action.
26130      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26131      * @return {BasicForm} this
26132      */
26133     submit : function(options){
26134         this.doAction('submit', options);
26135         return this;
26136     },
26137
26138     /**
26139      * Shortcut to do a load action.
26140      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26141      * @return {BasicForm} this
26142      */
26143     load : function(options){
26144         this.doAction('load', options);
26145         return this;
26146     },
26147
26148     /**
26149      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26150      * @param {Record} record The record to edit
26151      * @return {BasicForm} this
26152      */
26153     updateRecord : function(record){
26154         record.beginEdit();
26155         var fs = record.fields;
26156         fs.each(function(f){
26157             var field = this.findField(f.name);
26158             if(field){
26159                 record.set(f.name, field.getValue());
26160             }
26161         }, this);
26162         record.endEdit();
26163         return this;
26164     },
26165
26166     /**
26167      * Loads an Roo.data.Record into this form.
26168      * @param {Record} record The record to load
26169      * @return {BasicForm} this
26170      */
26171     loadRecord : function(record){
26172         this.setValues(record.data);
26173         return this;
26174     },
26175
26176     // private
26177     beforeAction : function(action){
26178         var o = action.options;
26179         
26180        
26181         if(this.waitMsgTarget === true){
26182             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
26183         }else if(this.waitMsgTarget){
26184             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26185             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
26186         }else {
26187             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
26188         }
26189          
26190     },
26191
26192     // private
26193     afterAction : function(action, success){
26194         this.activeAction = null;
26195         var o = action.options;
26196         
26197         if(this.waitMsgTarget === true){
26198             this.el.unmask();
26199         }else if(this.waitMsgTarget){
26200             this.waitMsgTarget.unmask();
26201         }else{
26202             Roo.MessageBox.updateProgress(1);
26203             Roo.MessageBox.hide();
26204         }
26205          
26206         if(success){
26207             if(o.reset){
26208                 this.reset();
26209             }
26210             Roo.callback(o.success, o.scope, [this, action]);
26211             this.fireEvent('actioncomplete', this, action);
26212             
26213         }else{
26214             Roo.callback(o.failure, o.scope, [this, action]);
26215             // show an error message if no failed handler is set..
26216             if (!this.hasListener('actionfailed')) {
26217                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
26218             }
26219             
26220             this.fireEvent('actionfailed', this, action);
26221         }
26222         
26223     },
26224
26225     /**
26226      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26227      * @param {String} id The value to search for
26228      * @return Field
26229      */
26230     findField : function(id){
26231         var field = this.items.get(id);
26232         if(!field){
26233             this.items.each(function(f){
26234                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26235                     field = f;
26236                     return false;
26237                 }
26238             });
26239         }
26240         return field || null;
26241     },
26242
26243     /**
26244      * Add a secondary form to this one, 
26245      * Used to provide tabbed forms. One form is primary, with hidden values 
26246      * which mirror the elements from the other forms.
26247      * 
26248      * @param {Roo.form.Form} form to add.
26249      * 
26250      */
26251     addForm : function(form)
26252     {
26253        
26254         if (this.childForms.indexOf(form) > -1) {
26255             // already added..
26256             return;
26257         }
26258         this.childForms.push(form);
26259         var n = '';
26260         Roo.each(form.allItems, function (fe) {
26261             
26262             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26263             if (this.findField(n)) { // already added..
26264                 return;
26265             }
26266             var add = new Roo.form.Hidden({
26267                 name : n
26268             });
26269             add.render(this.el);
26270             
26271             this.add( add );
26272         }, this);
26273         
26274     },
26275     /**
26276      * Mark fields in this form invalid in bulk.
26277      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26278      * @return {BasicForm} this
26279      */
26280     markInvalid : function(errors){
26281         if(errors instanceof Array){
26282             for(var i = 0, len = errors.length; i < len; i++){
26283                 var fieldError = errors[i];
26284                 var f = this.findField(fieldError.id);
26285                 if(f){
26286                     f.markInvalid(fieldError.msg);
26287                 }
26288             }
26289         }else{
26290             var field, id;
26291             for(id in errors){
26292                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26293                     field.markInvalid(errors[id]);
26294                 }
26295             }
26296         }
26297         Roo.each(this.childForms || [], function (f) {
26298             f.markInvalid(errors);
26299         });
26300         
26301         return this;
26302     },
26303
26304     /**
26305      * Set values for fields in this form in bulk.
26306      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26307      * @return {BasicForm} this
26308      */
26309     setValues : function(values){
26310         if(values instanceof Array){ // array of objects
26311             for(var i = 0, len = values.length; i < len; i++){
26312                 var v = values[i];
26313                 var f = this.findField(v.id);
26314                 if(f){
26315                     f.setValue(v.value);
26316                     if(this.trackResetOnLoad){
26317                         f.originalValue = f.getValue();
26318                     }
26319                 }
26320             }
26321         }else{ // object hash
26322             var field, id;
26323             for(id in values){
26324                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26325                     
26326                     if (field.setFromData && 
26327                         field.valueField && 
26328                         field.displayField &&
26329                         // combos' with local stores can 
26330                         // be queried via setValue()
26331                         // to set their value..
26332                         (field.store && !field.store.isLocal)
26333                         ) {
26334                         // it's a combo
26335                         var sd = { };
26336                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26337                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26338                         field.setFromData(sd);
26339                         
26340                     } else {
26341                         field.setValue(values[id]);
26342                     }
26343                     
26344                     
26345                     if(this.trackResetOnLoad){
26346                         field.originalValue = field.getValue();
26347                     }
26348                 }
26349             }
26350         }
26351          
26352         Roo.each(this.childForms || [], function (f) {
26353             f.setValues(values);
26354         });
26355                 
26356         return this;
26357     },
26358
26359     /**
26360      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26361      * they are returned as an array.
26362      * @param {Boolean} asString
26363      * @return {Object}
26364      */
26365     getValues : function(asString){
26366         if (this.childForms) {
26367             // copy values from the child forms
26368             Roo.each(this.childForms, function (f) {
26369                 this.setValues(f.getValues());
26370             }, this);
26371         }
26372         
26373         
26374         
26375         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26376         if(asString === true){
26377             return fs;
26378         }
26379         return Roo.urlDecode(fs);
26380     },
26381     
26382     /**
26383      * Returns the fields in this form as an object with key/value pairs. 
26384      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26385      * @return {Object}
26386      */
26387     getFieldValues : function()
26388     {
26389         if (this.childForms) {
26390             // copy values from the child forms
26391             Roo.each(this.childForms, function (f) {
26392                 this.setValues(f.getValues());
26393             }, this);
26394         }
26395         
26396         var ret = {};
26397         this.items.each(function(f){
26398             if (!f.getName()) {
26399                 return;
26400             }
26401             var v = f.getValue();
26402             if ((typeof(v) == 'object') && f.getRawValue) {
26403                 v = f.getRawValue() ; // dates..
26404             }
26405             ret[f.getName()] = v;
26406         });
26407         
26408         return ret;
26409     },
26410
26411     /**
26412      * Clears all invalid messages in this form.
26413      * @return {BasicForm} this
26414      */
26415     clearInvalid : function(){
26416         this.items.each(function(f){
26417            f.clearInvalid();
26418         });
26419         
26420         Roo.each(this.childForms || [], function (f) {
26421             f.clearInvalid();
26422         });
26423         
26424         
26425         return this;
26426     },
26427
26428     /**
26429      * Resets this form.
26430      * @return {BasicForm} this
26431      */
26432     reset : function(){
26433         this.items.each(function(f){
26434             f.reset();
26435         });
26436         
26437         Roo.each(this.childForms || [], function (f) {
26438             f.reset();
26439         });
26440        
26441         
26442         return this;
26443     },
26444
26445     /**
26446      * Add Roo.form components to this form.
26447      * @param {Field} field1
26448      * @param {Field} field2 (optional)
26449      * @param {Field} etc (optional)
26450      * @return {BasicForm} this
26451      */
26452     add : function(){
26453         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26454         return this;
26455     },
26456
26457
26458     /**
26459      * Removes a field from the items collection (does NOT remove its markup).
26460      * @param {Field} field
26461      * @return {BasicForm} this
26462      */
26463     remove : function(field){
26464         this.items.remove(field);
26465         return this;
26466     },
26467
26468     /**
26469      * Looks at the fields in this form, checks them for an id attribute,
26470      * and calls applyTo on the existing dom element with that id.
26471      * @return {BasicForm} this
26472      */
26473     render : function(){
26474         this.items.each(function(f){
26475             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26476                 f.applyTo(f.id);
26477             }
26478         });
26479         return this;
26480     },
26481
26482     /**
26483      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26484      * @param {Object} values
26485      * @return {BasicForm} this
26486      */
26487     applyToFields : function(o){
26488         this.items.each(function(f){
26489            Roo.apply(f, o);
26490         });
26491         return this;
26492     },
26493
26494     /**
26495      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26496      * @param {Object} values
26497      * @return {BasicForm} this
26498      */
26499     applyIfToFields : function(o){
26500         this.items.each(function(f){
26501            Roo.applyIf(f, o);
26502         });
26503         return this;
26504     }
26505 });
26506
26507 // back compat
26508 Roo.BasicForm = Roo.form.BasicForm;/*
26509  * Based on:
26510  * Ext JS Library 1.1.1
26511  * Copyright(c) 2006-2007, Ext JS, LLC.
26512  *
26513  * Originally Released Under LGPL - original licence link has changed is not relivant.
26514  *
26515  * Fork - LGPL
26516  * <script type="text/javascript">
26517  */
26518
26519 /**
26520  * @class Roo.form.Form
26521  * @extends Roo.form.BasicForm
26522  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26523  * @constructor
26524  * @param {Object} config Configuration options
26525  */
26526 Roo.form.Form = function(config){
26527     var xitems =  [];
26528     if (config.items) {
26529         xitems = config.items;
26530         delete config.items;
26531     }
26532    
26533     
26534     Roo.form.Form.superclass.constructor.call(this, null, config);
26535     this.url = this.url || this.action;
26536     if(!this.root){
26537         this.root = new Roo.form.Layout(Roo.applyIf({
26538             id: Roo.id()
26539         }, config));
26540     }
26541     this.active = this.root;
26542     /**
26543      * Array of all the buttons that have been added to this form via {@link addButton}
26544      * @type Array
26545      */
26546     this.buttons = [];
26547     this.allItems = [];
26548     this.addEvents({
26549         /**
26550          * @event clientvalidation
26551          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26552          * @param {Form} this
26553          * @param {Boolean} valid true if the form has passed client-side validation
26554          */
26555         clientvalidation: true,
26556         /**
26557          * @event rendered
26558          * Fires when the form is rendered
26559          * @param {Roo.form.Form} form
26560          */
26561         rendered : true
26562     });
26563     
26564     if (this.progressUrl) {
26565             // push a hidden field onto the list of fields..
26566             this.addxtype( {
26567                     xns: Roo.form, 
26568                     xtype : 'Hidden', 
26569                     name : 'UPLOAD_IDENTIFIER' 
26570             });
26571         }
26572         
26573     
26574     Roo.each(xitems, this.addxtype, this);
26575     
26576     
26577     
26578 };
26579
26580 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26581     /**
26582      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26583      */
26584     /**
26585      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26586      */
26587     /**
26588      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26589      */
26590     buttonAlign:'center',
26591
26592     /**
26593      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26594      */
26595     minButtonWidth:75,
26596
26597     /**
26598      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26599      * This property cascades to child containers if not set.
26600      */
26601     labelAlign:'left',
26602
26603     /**
26604      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26605      * fires a looping event with that state. This is required to bind buttons to the valid
26606      * state using the config value formBind:true on the button.
26607      */
26608     monitorValid : false,
26609
26610     /**
26611      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26612      */
26613     monitorPoll : 200,
26614     
26615     /**
26616      * @cfg {String} progressUrl - Url to return progress data 
26617      */
26618     
26619     progressUrl : false,
26620   
26621     /**
26622      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26623      * fields are added and the column is closed. If no fields are passed the column remains open
26624      * until end() is called.
26625      * @param {Object} config The config to pass to the column
26626      * @param {Field} field1 (optional)
26627      * @param {Field} field2 (optional)
26628      * @param {Field} etc (optional)
26629      * @return Column The column container object
26630      */
26631     column : function(c){
26632         var col = new Roo.form.Column(c);
26633         this.start(col);
26634         if(arguments.length > 1){ // duplicate code required because of Opera
26635             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26636             this.end();
26637         }
26638         return col;
26639     },
26640
26641     /**
26642      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26643      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26644      * until end() is called.
26645      * @param {Object} config The config to pass to the fieldset
26646      * @param {Field} field1 (optional)
26647      * @param {Field} field2 (optional)
26648      * @param {Field} etc (optional)
26649      * @return FieldSet The fieldset container object
26650      */
26651     fieldset : function(c){
26652         var fs = new Roo.form.FieldSet(c);
26653         this.start(fs);
26654         if(arguments.length > 1){ // duplicate code required because of Opera
26655             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26656             this.end();
26657         }
26658         return fs;
26659     },
26660
26661     /**
26662      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26663      * fields are added and the container is closed. If no fields are passed the container remains open
26664      * until end() is called.
26665      * @param {Object} config The config to pass to the Layout
26666      * @param {Field} field1 (optional)
26667      * @param {Field} field2 (optional)
26668      * @param {Field} etc (optional)
26669      * @return Layout The container object
26670      */
26671     container : function(c){
26672         var l = new Roo.form.Layout(c);
26673         this.start(l);
26674         if(arguments.length > 1){ // duplicate code required because of Opera
26675             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26676             this.end();
26677         }
26678         return l;
26679     },
26680
26681     /**
26682      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26683      * @param {Object} container A Roo.form.Layout or subclass of Layout
26684      * @return {Form} this
26685      */
26686     start : function(c){
26687         // cascade label info
26688         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26689         this.active.stack.push(c);
26690         c.ownerCt = this.active;
26691         this.active = c;
26692         return this;
26693     },
26694
26695     /**
26696      * Closes the current open container
26697      * @return {Form} this
26698      */
26699     end : function(){
26700         if(this.active == this.root){
26701             return this;
26702         }
26703         this.active = this.active.ownerCt;
26704         return this;
26705     },
26706
26707     /**
26708      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26709      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26710      * as the label of the field.
26711      * @param {Field} field1
26712      * @param {Field} field2 (optional)
26713      * @param {Field} etc. (optional)
26714      * @return {Form} this
26715      */
26716     add : function(){
26717         this.active.stack.push.apply(this.active.stack, arguments);
26718         this.allItems.push.apply(this.allItems,arguments);
26719         var r = [];
26720         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26721             if(a[i].isFormField){
26722                 r.push(a[i]);
26723             }
26724         }
26725         if(r.length > 0){
26726             Roo.form.Form.superclass.add.apply(this, r);
26727         }
26728         return this;
26729     },
26730     
26731
26732     
26733     
26734     
26735      /**
26736      * Find any element that has been added to a form, using it's ID or name
26737      * This can include framesets, columns etc. along with regular fields..
26738      * @param {String} id - id or name to find.
26739      
26740      * @return {Element} e - or false if nothing found.
26741      */
26742     findbyId : function(id)
26743     {
26744         var ret = false;
26745         if (!id) {
26746             return ret;
26747         }
26748         Roo.each(this.allItems, function(f){
26749             if (f.id == id || f.name == id ){
26750                 ret = f;
26751                 return false;
26752             }
26753         });
26754         return ret;
26755     },
26756
26757     
26758     
26759     /**
26760      * Render this form into the passed container. This should only be called once!
26761      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26762      * @return {Form} this
26763      */
26764     render : function(ct)
26765     {
26766         
26767         
26768         
26769         ct = Roo.get(ct);
26770         var o = this.autoCreate || {
26771             tag: 'form',
26772             method : this.method || 'POST',
26773             id : this.id || Roo.id()
26774         };
26775         this.initEl(ct.createChild(o));
26776
26777         this.root.render(this.el);
26778         
26779        
26780              
26781         this.items.each(function(f){
26782             f.render('x-form-el-'+f.id);
26783         });
26784
26785         if(this.buttons.length > 0){
26786             // tables are required to maintain order and for correct IE layout
26787             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26788                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26789                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26790             }}, null, true);
26791             var tr = tb.getElementsByTagName('tr')[0];
26792             for(var i = 0, len = this.buttons.length; i < len; i++) {
26793                 var b = this.buttons[i];
26794                 var td = document.createElement('td');
26795                 td.className = 'x-form-btn-td';
26796                 b.render(tr.appendChild(td));
26797             }
26798         }
26799         if(this.monitorValid){ // initialize after render
26800             this.startMonitoring();
26801         }
26802         this.fireEvent('rendered', this);
26803         return this;
26804     },
26805
26806     /**
26807      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26808      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26809      * object or a valid Roo.DomHelper element config
26810      * @param {Function} handler The function called when the button is clicked
26811      * @param {Object} scope (optional) The scope of the handler function
26812      * @return {Roo.Button}
26813      */
26814     addButton : function(config, handler, scope){
26815         var bc = {
26816             handler: handler,
26817             scope: scope,
26818             minWidth: this.minButtonWidth,
26819             hideParent:true
26820         };
26821         if(typeof config == "string"){
26822             bc.text = config;
26823         }else{
26824             Roo.apply(bc, config);
26825         }
26826         var btn = new Roo.Button(null, bc);
26827         this.buttons.push(btn);
26828         return btn;
26829     },
26830
26831      /**
26832      * Adds a series of form elements (using the xtype property as the factory method.
26833      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26834      * @param {Object} config 
26835      */
26836     
26837     addxtype : function()
26838     {
26839         var ar = Array.prototype.slice.call(arguments, 0);
26840         var ret = false;
26841         for(var i = 0; i < ar.length; i++) {
26842             if (!ar[i]) {
26843                 continue; // skip -- if this happends something invalid got sent, we 
26844                 // should ignore it, as basically that interface element will not show up
26845                 // and that should be pretty obvious!!
26846             }
26847             
26848             if (Roo.form[ar[i].xtype]) {
26849                 ar[i].form = this;
26850                 var fe = Roo.factory(ar[i], Roo.form);
26851                 if (!ret) {
26852                     ret = fe;
26853                 }
26854                 fe.form = this;
26855                 if (fe.store) {
26856                     fe.store.form = this;
26857                 }
26858                 if (fe.isLayout) {  
26859                          
26860                     this.start(fe);
26861                     this.allItems.push(fe);
26862                     if (fe.items && fe.addxtype) {
26863                         fe.addxtype.apply(fe, fe.items);
26864                         delete fe.items;
26865                     }
26866                      this.end();
26867                     continue;
26868                 }
26869                 
26870                 
26871                  
26872                 this.add(fe);
26873               //  console.log('adding ' + ar[i].xtype);
26874             }
26875             if (ar[i].xtype == 'Button') {  
26876                 //console.log('adding button');
26877                 //console.log(ar[i]);
26878                 this.addButton(ar[i]);
26879                 this.allItems.push(fe);
26880                 continue;
26881             }
26882             
26883             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26884                 alert('end is not supported on xtype any more, use items');
26885             //    this.end();
26886             //    //console.log('adding end');
26887             }
26888             
26889         }
26890         return ret;
26891     },
26892     
26893     /**
26894      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26895      * option "monitorValid"
26896      */
26897     startMonitoring : function(){
26898         if(!this.bound){
26899             this.bound = true;
26900             Roo.TaskMgr.start({
26901                 run : this.bindHandler,
26902                 interval : this.monitorPoll || 200,
26903                 scope: this
26904             });
26905         }
26906     },
26907
26908     /**
26909      * Stops monitoring of the valid state of this form
26910      */
26911     stopMonitoring : function(){
26912         this.bound = false;
26913     },
26914
26915     // private
26916     bindHandler : function(){
26917         if(!this.bound){
26918             return false; // stops binding
26919         }
26920         var valid = true;
26921         this.items.each(function(f){
26922             if(!f.isValid(true)){
26923                 valid = false;
26924                 return false;
26925             }
26926         });
26927         for(var i = 0, len = this.buttons.length; i < len; i++){
26928             var btn = this.buttons[i];
26929             if(btn.formBind === true && btn.disabled === valid){
26930                 btn.setDisabled(!valid);
26931             }
26932         }
26933         this.fireEvent('clientvalidation', this, valid);
26934     }
26935     
26936     
26937     
26938     
26939     
26940     
26941     
26942     
26943 });
26944
26945
26946 // back compat
26947 Roo.Form = Roo.form.Form;
26948 /*
26949  * Based on:
26950  * Ext JS Library 1.1.1
26951  * Copyright(c) 2006-2007, Ext JS, LLC.
26952  *
26953  * Originally Released Under LGPL - original licence link has changed is not relivant.
26954  *
26955  * Fork - LGPL
26956  * <script type="text/javascript">
26957  */
26958  
26959  /**
26960  * @class Roo.form.Action
26961  * Internal Class used to handle form actions
26962  * @constructor
26963  * @param {Roo.form.BasicForm} el The form element or its id
26964  * @param {Object} config Configuration options
26965  */
26966  
26967  
26968 // define the action interface
26969 Roo.form.Action = function(form, options){
26970     this.form = form;
26971     this.options = options || {};
26972 };
26973 /**
26974  * Client Validation Failed
26975  * @const 
26976  */
26977 Roo.form.Action.CLIENT_INVALID = 'client';
26978 /**
26979  * Server Validation Failed
26980  * @const 
26981  */
26982  Roo.form.Action.SERVER_INVALID = 'server';
26983  /**
26984  * Connect to Server Failed
26985  * @const 
26986  */
26987 Roo.form.Action.CONNECT_FAILURE = 'connect';
26988 /**
26989  * Reading Data from Server Failed
26990  * @const 
26991  */
26992 Roo.form.Action.LOAD_FAILURE = 'load';
26993
26994 Roo.form.Action.prototype = {
26995     type : 'default',
26996     failureType : undefined,
26997     response : undefined,
26998     result : undefined,
26999
27000     // interface method
27001     run : function(options){
27002
27003     },
27004
27005     // interface method
27006     success : function(response){
27007
27008     },
27009
27010     // interface method
27011     handleResponse : function(response){
27012
27013     },
27014
27015     // default connection failure
27016     failure : function(response){
27017         
27018         this.response = response;
27019         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27020         this.form.afterAction(this, false);
27021     },
27022
27023     processResponse : function(response){
27024         this.response = response;
27025         if(!response.responseText){
27026             return true;
27027         }
27028         this.result = this.handleResponse(response);
27029         return this.result;
27030     },
27031
27032     // utility functions used internally
27033     getUrl : function(appendParams){
27034         var url = this.options.url || this.form.url || this.form.el.dom.action;
27035         if(appendParams){
27036             var p = this.getParams();
27037             if(p){
27038                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
27039             }
27040         }
27041         return url;
27042     },
27043
27044     getMethod : function(){
27045         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
27046     },
27047
27048     getParams : function(){
27049         var bp = this.form.baseParams;
27050         var p = this.options.params;
27051         if(p){
27052             if(typeof p == "object"){
27053                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27054             }else if(typeof p == 'string' && bp){
27055                 p += '&' + Roo.urlEncode(bp);
27056             }
27057         }else if(bp){
27058             p = Roo.urlEncode(bp);
27059         }
27060         return p;
27061     },
27062
27063     createCallback : function(){
27064         return {
27065             success: this.success,
27066             failure: this.failure,
27067             scope: this,
27068             timeout: (this.form.timeout*1000),
27069             upload: this.form.fileUpload ? this.success : undefined
27070         };
27071     }
27072 };
27073
27074 Roo.form.Action.Submit = function(form, options){
27075     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27076 };
27077
27078 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27079     type : 'submit',
27080
27081     haveProgress : false,
27082     uploadComplete : false,
27083     
27084     // uploadProgress indicator.
27085     uploadProgress : function()
27086     {
27087         if (!this.form.progressUrl) {
27088             return;
27089         }
27090         
27091         if (!this.haveProgress) {
27092             Roo.MessageBox.progress("Uploading", "Uploading");
27093         }
27094         if (this.uploadComplete) {
27095            Roo.MessageBox.hide();
27096            return;
27097         }
27098         
27099         this.haveProgress = true;
27100    
27101         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27102         
27103         var c = new Roo.data.Connection();
27104         c.request({
27105             url : this.form.progressUrl,
27106             params: {
27107                 id : uid
27108             },
27109             method: 'GET',
27110             success : function(req){
27111                //console.log(data);
27112                 var rdata = false;
27113                 var edata;
27114                 try  {
27115                    rdata = Roo.decode(req.responseText)
27116                 } catch (e) {
27117                     Roo.log("Invalid data from server..");
27118                     Roo.log(edata);
27119                     return;
27120                 }
27121                 if (!rdata || !rdata.success) {
27122                     Roo.log(rdata);
27123                     return;
27124                 }
27125                 var data = rdata.data;
27126                 
27127                 if (this.uploadComplete) {
27128                    Roo.MessageBox.hide();
27129                    return;
27130                 }
27131                    
27132                 if (data){
27133                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27134                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27135                     );
27136                 }
27137                 this.uploadProgress.defer(2000,this);
27138             },
27139        
27140             failure: function(data) {
27141                 Roo.log('progress url failed ');
27142                 Roo.log(data);
27143             },
27144             scope : this
27145         });
27146            
27147     },
27148     
27149     
27150     run : function()
27151     {
27152         // run get Values on the form, so it syncs any secondary forms.
27153         this.form.getValues();
27154         
27155         var o = this.options;
27156         var method = this.getMethod();
27157         var isPost = method == 'POST';
27158         if(o.clientValidation === false || this.form.isValid()){
27159             
27160             if (this.form.progressUrl) {
27161                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27162                     (new Date() * 1) + '' + Math.random());
27163                     
27164             } 
27165             
27166             
27167             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27168                 form:this.form.el.dom,
27169                 url:this.getUrl(!isPost),
27170                 method: method,
27171                 params:isPost ? this.getParams() : null,
27172                 isUpload: this.form.fileUpload
27173             }));
27174             
27175             this.uploadProgress();
27176
27177         }else if (o.clientValidation !== false){ // client validation failed
27178             this.failureType = Roo.form.Action.CLIENT_INVALID;
27179             this.form.afterAction(this, false);
27180         }
27181     },
27182
27183     success : function(response)
27184     {
27185         this.uploadComplete= true;
27186         if (this.haveProgress) {
27187             Roo.MessageBox.hide();
27188         }
27189         
27190         
27191         var result = this.processResponse(response);
27192         if(result === true || result.success){
27193             this.form.afterAction(this, true);
27194             return;
27195         }
27196         if(result.errors){
27197             this.form.markInvalid(result.errors);
27198             this.failureType = Roo.form.Action.SERVER_INVALID;
27199         }
27200         this.form.afterAction(this, false);
27201     },
27202     failure : function(response)
27203     {
27204         this.uploadComplete= true;
27205         if (this.haveProgress) {
27206             Roo.MessageBox.hide();
27207         }
27208         
27209         
27210         this.response = response;
27211         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27212         this.form.afterAction(this, false);
27213     },
27214     
27215     handleResponse : function(response){
27216         if(this.form.errorReader){
27217             var rs = this.form.errorReader.read(response);
27218             var errors = [];
27219             if(rs.records){
27220                 for(var i = 0, len = rs.records.length; i < len; i++) {
27221                     var r = rs.records[i];
27222                     errors[i] = r.data;
27223                 }
27224             }
27225             if(errors.length < 1){
27226                 errors = null;
27227             }
27228             return {
27229                 success : rs.success,
27230                 errors : errors
27231             };
27232         }
27233         var ret = false;
27234         try {
27235             ret = Roo.decode(response.responseText);
27236         } catch (e) {
27237             ret = {
27238                 success: false,
27239                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27240                 errors : []
27241             };
27242         }
27243         return ret;
27244         
27245     }
27246 });
27247
27248
27249 Roo.form.Action.Load = function(form, options){
27250     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27251     this.reader = this.form.reader;
27252 };
27253
27254 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27255     type : 'load',
27256
27257     run : function(){
27258         
27259         Roo.Ajax.request(Roo.apply(
27260                 this.createCallback(), {
27261                     method:this.getMethod(),
27262                     url:this.getUrl(false),
27263                     params:this.getParams()
27264         }));
27265     },
27266
27267     success : function(response){
27268         
27269         var result = this.processResponse(response);
27270         if(result === true || !result.success || !result.data){
27271             this.failureType = Roo.form.Action.LOAD_FAILURE;
27272             this.form.afterAction(this, false);
27273             return;
27274         }
27275         this.form.clearInvalid();
27276         this.form.setValues(result.data);
27277         this.form.afterAction(this, true);
27278     },
27279
27280     handleResponse : function(response){
27281         if(this.form.reader){
27282             var rs = this.form.reader.read(response);
27283             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27284             return {
27285                 success : rs.success,
27286                 data : data
27287             };
27288         }
27289         return Roo.decode(response.responseText);
27290     }
27291 });
27292
27293 Roo.form.Action.ACTION_TYPES = {
27294     'load' : Roo.form.Action.Load,
27295     'submit' : Roo.form.Action.Submit
27296 };/*
27297  * Based on:
27298  * Ext JS Library 1.1.1
27299  * Copyright(c) 2006-2007, Ext JS, LLC.
27300  *
27301  * Originally Released Under LGPL - original licence link has changed is not relivant.
27302  *
27303  * Fork - LGPL
27304  * <script type="text/javascript">
27305  */
27306  
27307 /**
27308  * @class Roo.form.Layout
27309  * @extends Roo.Component
27310  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27311  * @constructor
27312  * @param {Object} config Configuration options
27313  */
27314 Roo.form.Layout = function(config){
27315     var xitems = [];
27316     if (config.items) {
27317         xitems = config.items;
27318         delete config.items;
27319     }
27320     Roo.form.Layout.superclass.constructor.call(this, config);
27321     this.stack = [];
27322     Roo.each(xitems, this.addxtype, this);
27323      
27324 };
27325
27326 Roo.extend(Roo.form.Layout, Roo.Component, {
27327     /**
27328      * @cfg {String/Object} autoCreate
27329      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27330      */
27331     /**
27332      * @cfg {String/Object/Function} style
27333      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27334      * a function which returns such a specification.
27335      */
27336     /**
27337      * @cfg {String} labelAlign
27338      * Valid values are "left," "top" and "right" (defaults to "left")
27339      */
27340     /**
27341      * @cfg {Number} labelWidth
27342      * Fixed width in pixels of all field labels (defaults to undefined)
27343      */
27344     /**
27345      * @cfg {Boolean} clear
27346      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27347      */
27348     clear : true,
27349     /**
27350      * @cfg {String} labelSeparator
27351      * The separator to use after field labels (defaults to ':')
27352      */
27353     labelSeparator : ':',
27354     /**
27355      * @cfg {Boolean} hideLabels
27356      * True to suppress the display of field labels in this layout (defaults to false)
27357      */
27358     hideLabels : false,
27359
27360     // private
27361     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27362     
27363     isLayout : true,
27364     
27365     // private
27366     onRender : function(ct, position){
27367         if(this.el){ // from markup
27368             this.el = Roo.get(this.el);
27369         }else {  // generate
27370             var cfg = this.getAutoCreate();
27371             this.el = ct.createChild(cfg, position);
27372         }
27373         if(this.style){
27374             this.el.applyStyles(this.style);
27375         }
27376         if(this.labelAlign){
27377             this.el.addClass('x-form-label-'+this.labelAlign);
27378         }
27379         if(this.hideLabels){
27380             this.labelStyle = "display:none";
27381             this.elementStyle = "padding-left:0;";
27382         }else{
27383             if(typeof this.labelWidth == 'number'){
27384                 this.labelStyle = "width:"+this.labelWidth+"px;";
27385                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27386             }
27387             if(this.labelAlign == 'top'){
27388                 this.labelStyle = "width:auto;";
27389                 this.elementStyle = "padding-left:0;";
27390             }
27391         }
27392         var stack = this.stack;
27393         var slen = stack.length;
27394         if(slen > 0){
27395             if(!this.fieldTpl){
27396                 var t = new Roo.Template(
27397                     '<div class="x-form-item {5}">',
27398                         '<label for="{0}" style="{2}">{1}{4}</label>',
27399                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27400                         '</div>',
27401                     '</div><div class="x-form-clear-left"></div>'
27402                 );
27403                 t.disableFormats = true;
27404                 t.compile();
27405                 Roo.form.Layout.prototype.fieldTpl = t;
27406             }
27407             for(var i = 0; i < slen; i++) {
27408                 if(stack[i].isFormField){
27409                     this.renderField(stack[i]);
27410                 }else{
27411                     this.renderComponent(stack[i]);
27412                 }
27413             }
27414         }
27415         if(this.clear){
27416             this.el.createChild({cls:'x-form-clear'});
27417         }
27418     },
27419
27420     // private
27421     renderField : function(f){
27422         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27423                f.id, //0
27424                f.fieldLabel, //1
27425                f.labelStyle||this.labelStyle||'', //2
27426                this.elementStyle||'', //3
27427                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27428                f.itemCls||this.itemCls||''  //5
27429        ], true).getPrevSibling());
27430     },
27431
27432     // private
27433     renderComponent : function(c){
27434         c.render(c.isLayout ? this.el : this.el.createChild());    
27435     },
27436     /**
27437      * Adds a object form elements (using the xtype property as the factory method.)
27438      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27439      * @param {Object} config 
27440      */
27441     addxtype : function(o)
27442     {
27443         // create the lement.
27444         o.form = this.form;
27445         var fe = Roo.factory(o, Roo.form);
27446         this.form.allItems.push(fe);
27447         this.stack.push(fe);
27448         
27449         if (fe.isFormField) {
27450             this.form.items.add(fe);
27451         }
27452          
27453         return fe;
27454     }
27455 });
27456
27457 /**
27458  * @class Roo.form.Column
27459  * @extends Roo.form.Layout
27460  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27461  * @constructor
27462  * @param {Object} config Configuration options
27463  */
27464 Roo.form.Column = function(config){
27465     Roo.form.Column.superclass.constructor.call(this, config);
27466 };
27467
27468 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27469     /**
27470      * @cfg {Number/String} width
27471      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27472      */
27473     /**
27474      * @cfg {String/Object} autoCreate
27475      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27476      */
27477
27478     // private
27479     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27480
27481     // private
27482     onRender : function(ct, position){
27483         Roo.form.Column.superclass.onRender.call(this, ct, position);
27484         if(this.width){
27485             this.el.setWidth(this.width);
27486         }
27487     }
27488 });
27489
27490
27491 /**
27492  * @class Roo.form.Row
27493  * @extends Roo.form.Layout
27494  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27495  * @constructor
27496  * @param {Object} config Configuration options
27497  */
27498
27499  
27500 Roo.form.Row = function(config){
27501     Roo.form.Row.superclass.constructor.call(this, config);
27502 };
27503  
27504 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27505       /**
27506      * @cfg {Number/String} width
27507      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27508      */
27509     /**
27510      * @cfg {Number/String} height
27511      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27512      */
27513     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27514     
27515     padWidth : 20,
27516     // private
27517     onRender : function(ct, position){
27518         //console.log('row render');
27519         if(!this.rowTpl){
27520             var t = new Roo.Template(
27521                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27522                     '<label for="{0}" style="{2}">{1}{4}</label>',
27523                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27524                     '</div>',
27525                 '</div>'
27526             );
27527             t.disableFormats = true;
27528             t.compile();
27529             Roo.form.Layout.prototype.rowTpl = t;
27530         }
27531         this.fieldTpl = this.rowTpl;
27532         
27533         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27534         var labelWidth = 100;
27535         
27536         if ((this.labelAlign != 'top')) {
27537             if (typeof this.labelWidth == 'number') {
27538                 labelWidth = this.labelWidth
27539             }
27540             this.padWidth =  20 + labelWidth;
27541             
27542         }
27543         
27544         Roo.form.Column.superclass.onRender.call(this, ct, position);
27545         if(this.width){
27546             this.el.setWidth(this.width);
27547         }
27548         if(this.height){
27549             this.el.setHeight(this.height);
27550         }
27551     },
27552     
27553     // private
27554     renderField : function(f){
27555         f.fieldEl = this.fieldTpl.append(this.el, [
27556                f.id, f.fieldLabel,
27557                f.labelStyle||this.labelStyle||'',
27558                this.elementStyle||'',
27559                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27560                f.itemCls||this.itemCls||'',
27561                f.width ? f.width + this.padWidth : 160 + this.padWidth
27562        ],true);
27563     }
27564 });
27565  
27566
27567 /**
27568  * @class Roo.form.FieldSet
27569  * @extends Roo.form.Layout
27570  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27571  * @constructor
27572  * @param {Object} config Configuration options
27573  */
27574 Roo.form.FieldSet = function(config){
27575     Roo.form.FieldSet.superclass.constructor.call(this, config);
27576 };
27577
27578 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27579     /**
27580      * @cfg {String} legend
27581      * The text to display as the legend for the FieldSet (defaults to '')
27582      */
27583     /**
27584      * @cfg {String/Object} autoCreate
27585      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27586      */
27587
27588     // private
27589     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27590
27591     // private
27592     onRender : function(ct, position){
27593         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27594         if(this.legend){
27595             this.setLegend(this.legend);
27596         }
27597     },
27598
27599     // private
27600     setLegend : function(text){
27601         if(this.rendered){
27602             this.el.child('legend').update(text);
27603         }
27604     }
27605 });/*
27606  * Based on:
27607  * Ext JS Library 1.1.1
27608  * Copyright(c) 2006-2007, Ext JS, LLC.
27609  *
27610  * Originally Released Under LGPL - original licence link has changed is not relivant.
27611  *
27612  * Fork - LGPL
27613  * <script type="text/javascript">
27614  */
27615 /**
27616  * @class Roo.form.VTypes
27617  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27618  * @singleton
27619  */
27620 Roo.form.VTypes = function(){
27621     // closure these in so they are only created once.
27622     var alpha = /^[a-zA-Z_]+$/;
27623     var alphanum = /^[a-zA-Z0-9_]+$/;
27624     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27625     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27626
27627     // All these messages and functions are configurable
27628     return {
27629         /**
27630          * The function used to validate email addresses
27631          * @param {String} value The email address
27632          */
27633         'email' : function(v){
27634             return email.test(v);
27635         },
27636         /**
27637          * The error text to display when the email validation function returns false
27638          * @type String
27639          */
27640         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27641         /**
27642          * The keystroke filter mask to be applied on email input
27643          * @type RegExp
27644          */
27645         'emailMask' : /[a-z0-9_\.\-@]/i,
27646
27647         /**
27648          * The function used to validate URLs
27649          * @param {String} value The URL
27650          */
27651         'url' : function(v){
27652             return url.test(v);
27653         },
27654         /**
27655          * The error text to display when the url validation function returns false
27656          * @type String
27657          */
27658         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27659         
27660         /**
27661          * The function used to validate alpha values
27662          * @param {String} value The value
27663          */
27664         'alpha' : function(v){
27665             return alpha.test(v);
27666         },
27667         /**
27668          * The error text to display when the alpha validation function returns false
27669          * @type String
27670          */
27671         'alphaText' : 'This field should only contain letters and _',
27672         /**
27673          * The keystroke filter mask to be applied on alpha input
27674          * @type RegExp
27675          */
27676         'alphaMask' : /[a-z_]/i,
27677
27678         /**
27679          * The function used to validate alphanumeric values
27680          * @param {String} value The value
27681          */
27682         'alphanum' : function(v){
27683             return alphanum.test(v);
27684         },
27685         /**
27686          * The error text to display when the alphanumeric validation function returns false
27687          * @type String
27688          */
27689         'alphanumText' : 'This field should only contain letters, numbers and _',
27690         /**
27691          * The keystroke filter mask to be applied on alphanumeric input
27692          * @type RegExp
27693          */
27694         'alphanumMask' : /[a-z0-9_]/i
27695     };
27696 }();//<script type="text/javascript">
27697
27698 /**
27699  * @class Roo.form.FCKeditor
27700  * @extends Roo.form.TextArea
27701  * Wrapper around the FCKEditor http://www.fckeditor.net
27702  * @constructor
27703  * Creates a new FCKeditor
27704  * @param {Object} config Configuration options
27705  */
27706 Roo.form.FCKeditor = function(config){
27707     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27708     this.addEvents({
27709          /**
27710          * @event editorinit
27711          * Fired when the editor is initialized - you can add extra handlers here..
27712          * @param {FCKeditor} this
27713          * @param {Object} the FCK object.
27714          */
27715         editorinit : true
27716     });
27717     
27718     
27719 };
27720 Roo.form.FCKeditor.editors = { };
27721 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27722 {
27723     //defaultAutoCreate : {
27724     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27725     //},
27726     // private
27727     /**
27728      * @cfg {Object} fck options - see fck manual for details.
27729      */
27730     fckconfig : false,
27731     
27732     /**
27733      * @cfg {Object} fck toolbar set (Basic or Default)
27734      */
27735     toolbarSet : 'Basic',
27736     /**
27737      * @cfg {Object} fck BasePath
27738      */ 
27739     basePath : '/fckeditor/',
27740     
27741     
27742     frame : false,
27743     
27744     value : '',
27745     
27746    
27747     onRender : function(ct, position)
27748     {
27749         if(!this.el){
27750             this.defaultAutoCreate = {
27751                 tag: "textarea",
27752                 style:"width:300px;height:60px;",
27753                 autocomplete: "off"
27754             };
27755         }
27756         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27757         /*
27758         if(this.grow){
27759             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27760             if(this.preventScrollbars){
27761                 this.el.setStyle("overflow", "hidden");
27762             }
27763             this.el.setHeight(this.growMin);
27764         }
27765         */
27766         //console.log('onrender' + this.getId() );
27767         Roo.form.FCKeditor.editors[this.getId()] = this;
27768          
27769
27770         this.replaceTextarea() ;
27771         
27772     },
27773     
27774     getEditor : function() {
27775         return this.fckEditor;
27776     },
27777     /**
27778      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27779      * @param {Mixed} value The value to set
27780      */
27781     
27782     
27783     setValue : function(value)
27784     {
27785         //console.log('setValue: ' + value);
27786         
27787         if(typeof(value) == 'undefined') { // not sure why this is happending...
27788             return;
27789         }
27790         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27791         
27792         //if(!this.el || !this.getEditor()) {
27793         //    this.value = value;
27794             //this.setValue.defer(100,this,[value]);    
27795         //    return;
27796         //} 
27797         
27798         if(!this.getEditor()) {
27799             return;
27800         }
27801         
27802         this.getEditor().SetData(value);
27803         
27804         //
27805
27806     },
27807
27808     /**
27809      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27810      * @return {Mixed} value The field value
27811      */
27812     getValue : function()
27813     {
27814         
27815         if (this.frame && this.frame.dom.style.display == 'none') {
27816             return Roo.form.FCKeditor.superclass.getValue.call(this);
27817         }
27818         
27819         if(!this.el || !this.getEditor()) {
27820            
27821            // this.getValue.defer(100,this); 
27822             return this.value;
27823         }
27824        
27825         
27826         var value=this.getEditor().GetData();
27827         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27828         return Roo.form.FCKeditor.superclass.getValue.call(this);
27829         
27830
27831     },
27832
27833     /**
27834      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27835      * @return {Mixed} value The field value
27836      */
27837     getRawValue : function()
27838     {
27839         if (this.frame && this.frame.dom.style.display == 'none') {
27840             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27841         }
27842         
27843         if(!this.el || !this.getEditor()) {
27844             //this.getRawValue.defer(100,this); 
27845             return this.value;
27846             return;
27847         }
27848         
27849         
27850         
27851         var value=this.getEditor().GetData();
27852         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27853         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27854          
27855     },
27856     
27857     setSize : function(w,h) {
27858         
27859         
27860         
27861         //if (this.frame && this.frame.dom.style.display == 'none') {
27862         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27863         //    return;
27864         //}
27865         //if(!this.el || !this.getEditor()) {
27866         //    this.setSize.defer(100,this, [w,h]); 
27867         //    return;
27868         //}
27869         
27870         
27871         
27872         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27873         
27874         this.frame.dom.setAttribute('width', w);
27875         this.frame.dom.setAttribute('height', h);
27876         this.frame.setSize(w,h);
27877         
27878     },
27879     
27880     toggleSourceEdit : function(value) {
27881         
27882       
27883          
27884         this.el.dom.style.display = value ? '' : 'none';
27885         this.frame.dom.style.display = value ?  'none' : '';
27886         
27887     },
27888     
27889     
27890     focus: function(tag)
27891     {
27892         if (this.frame.dom.style.display == 'none') {
27893             return Roo.form.FCKeditor.superclass.focus.call(this);
27894         }
27895         if(!this.el || !this.getEditor()) {
27896             this.focus.defer(100,this, [tag]); 
27897             return;
27898         }
27899         
27900         
27901         
27902         
27903         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27904         this.getEditor().Focus();
27905         if (tgs.length) {
27906             if (!this.getEditor().Selection.GetSelection()) {
27907                 this.focus.defer(100,this, [tag]); 
27908                 return;
27909             }
27910             
27911             
27912             var r = this.getEditor().EditorDocument.createRange();
27913             r.setStart(tgs[0],0);
27914             r.setEnd(tgs[0],0);
27915             this.getEditor().Selection.GetSelection().removeAllRanges();
27916             this.getEditor().Selection.GetSelection().addRange(r);
27917             this.getEditor().Focus();
27918         }
27919         
27920     },
27921     
27922     
27923     
27924     replaceTextarea : function()
27925     {
27926         if ( document.getElementById( this.getId() + '___Frame' ) )
27927             return ;
27928         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27929         //{
27930             // We must check the elements firstly using the Id and then the name.
27931         var oTextarea = document.getElementById( this.getId() );
27932         
27933         var colElementsByName = document.getElementsByName( this.getId() ) ;
27934          
27935         oTextarea.style.display = 'none' ;
27936
27937         if ( oTextarea.tabIndex ) {            
27938             this.TabIndex = oTextarea.tabIndex ;
27939         }
27940         
27941         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27942         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27943         this.frame = Roo.get(this.getId() + '___Frame')
27944     },
27945     
27946     _getConfigHtml : function()
27947     {
27948         var sConfig = '' ;
27949
27950         for ( var o in this.fckconfig ) {
27951             sConfig += sConfig.length > 0  ? '&amp;' : '';
27952             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27953         }
27954
27955         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27956     },
27957     
27958     
27959     _getIFrameHtml : function()
27960     {
27961         var sFile = 'fckeditor.html' ;
27962         /* no idea what this is about..
27963         try
27964         {
27965             if ( (/fcksource=true/i).test( window.top.location.search ) )
27966                 sFile = 'fckeditor.original.html' ;
27967         }
27968         catch (e) { 
27969         */
27970
27971         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27972         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27973         
27974         
27975         var html = '<iframe id="' + this.getId() +
27976             '___Frame" src="' + sLink +
27977             '" width="' + this.width +
27978             '" height="' + this.height + '"' +
27979             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27980             ' frameborder="0" scrolling="no"></iframe>' ;
27981
27982         return html ;
27983     },
27984     
27985     _insertHtmlBefore : function( html, element )
27986     {
27987         if ( element.insertAdjacentHTML )       {
27988             // IE
27989             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27990         } else { // Gecko
27991             var oRange = document.createRange() ;
27992             oRange.setStartBefore( element ) ;
27993             var oFragment = oRange.createContextualFragment( html );
27994             element.parentNode.insertBefore( oFragment, element ) ;
27995         }
27996     }
27997     
27998     
27999   
28000     
28001     
28002     
28003     
28004
28005 });
28006
28007 //Roo.reg('fckeditor', Roo.form.FCKeditor);
28008
28009 function FCKeditor_OnComplete(editorInstance){
28010     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
28011     f.fckEditor = editorInstance;
28012     //console.log("loaded");
28013     f.fireEvent('editorinit', f, editorInstance);
28014
28015   
28016
28017  
28018
28019
28020
28021
28022
28023
28024
28025
28026
28027
28028
28029
28030
28031
28032
28033 //<script type="text/javascript">
28034 /**
28035  * @class Roo.form.GridField
28036  * @extends Roo.form.Field
28037  * Embed a grid (or editable grid into a form)
28038  * STATUS ALPHA
28039  * 
28040  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
28041  * it needs 
28042  * xgrid.store = Roo.data.Store
28043  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
28044  * xgrid.store.reader = Roo.data.JsonReader 
28045  * 
28046  * 
28047  * @constructor
28048  * Creates a new GridField
28049  * @param {Object} config Configuration options
28050  */
28051 Roo.form.GridField = function(config){
28052     Roo.form.GridField.superclass.constructor.call(this, config);
28053      
28054 };
28055
28056 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28057     /**
28058      * @cfg {Number} width  - used to restrict width of grid..
28059      */
28060     width : 100,
28061     /**
28062      * @cfg {Number} height - used to restrict height of grid..
28063      */
28064     height : 50,
28065      /**
28066      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28067          * 
28068          *}
28069      */
28070     xgrid : false, 
28071     /**
28072      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28073      * {tag: "input", type: "checkbox", autocomplete: "off"})
28074      */
28075    // defaultAutoCreate : { tag: 'div' },
28076     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28077     /**
28078      * @cfg {String} addTitle Text to include for adding a title.
28079      */
28080     addTitle : false,
28081     //
28082     onResize : function(){
28083         Roo.form.Field.superclass.onResize.apply(this, arguments);
28084     },
28085
28086     initEvents : function(){
28087         // Roo.form.Checkbox.superclass.initEvents.call(this);
28088         // has no events...
28089        
28090     },
28091
28092
28093     getResizeEl : function(){
28094         return this.wrap;
28095     },
28096
28097     getPositionEl : function(){
28098         return this.wrap;
28099     },
28100
28101     // private
28102     onRender : function(ct, position){
28103         
28104         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28105         var style = this.style;
28106         delete this.style;
28107         
28108         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28109         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28110         this.viewEl = this.wrap.createChild({ tag: 'div' });
28111         if (style) {
28112             this.viewEl.applyStyles(style);
28113         }
28114         if (this.width) {
28115             this.viewEl.setWidth(this.width);
28116         }
28117         if (this.height) {
28118             this.viewEl.setHeight(this.height);
28119         }
28120         //if(this.inputValue !== undefined){
28121         //this.setValue(this.value);
28122         
28123         
28124         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28125         
28126         
28127         this.grid.render();
28128         this.grid.getDataSource().on('remove', this.refreshValue, this);
28129         this.grid.getDataSource().on('update', this.refreshValue, this);
28130         this.grid.on('afteredit', this.refreshValue, this);
28131  
28132     },
28133      
28134     
28135     /**
28136      * Sets the value of the item. 
28137      * @param {String} either an object  or a string..
28138      */
28139     setValue : function(v){
28140         //this.value = v;
28141         v = v || []; // empty set..
28142         // this does not seem smart - it really only affects memoryproxy grids..
28143         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28144             var ds = this.grid.getDataSource();
28145             // assumes a json reader..
28146             var data = {}
28147             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28148             ds.loadData( data);
28149         }
28150         Roo.form.GridField.superclass.setValue.call(this, v);
28151         this.refreshValue();
28152         // should load data in the grid really....
28153     },
28154     
28155     // private
28156     refreshValue: function() {
28157          var val = [];
28158         this.grid.getDataSource().each(function(r) {
28159             val.push(r.data);
28160         });
28161         this.el.dom.value = Roo.encode(val);
28162     }
28163     
28164      
28165     
28166     
28167 });/*
28168  * Based on:
28169  * Ext JS Library 1.1.1
28170  * Copyright(c) 2006-2007, Ext JS, LLC.
28171  *
28172  * Originally Released Under LGPL - original licence link has changed is not relivant.
28173  *
28174  * Fork - LGPL
28175  * <script type="text/javascript">
28176  */
28177 /**
28178  * @class Roo.form.DisplayField
28179  * @extends Roo.form.Field
28180  * A generic Field to display non-editable data.
28181  * @constructor
28182  * Creates a new Display Field item.
28183  * @param {Object} config Configuration options
28184  */
28185 Roo.form.DisplayField = function(config){
28186     Roo.form.DisplayField.superclass.constructor.call(this, config);
28187     
28188 };
28189
28190 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28191     inputType:      'hidden',
28192     allowBlank:     true,
28193     readOnly:         true,
28194     
28195  
28196     /**
28197      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28198      */
28199     focusClass : undefined,
28200     /**
28201      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28202      */
28203     fieldClass: 'x-form-field',
28204     
28205      /**
28206      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28207      */
28208     valueRenderer: undefined,
28209     
28210     width: 100,
28211     /**
28212      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28213      * {tag: "input", type: "checkbox", autocomplete: "off"})
28214      */
28215      
28216  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28217
28218     onResize : function(){
28219         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28220         
28221     },
28222
28223     initEvents : function(){
28224         // Roo.form.Checkbox.superclass.initEvents.call(this);
28225         // has no events...
28226        
28227     },
28228
28229
28230     getResizeEl : function(){
28231         return this.wrap;
28232     },
28233
28234     getPositionEl : function(){
28235         return this.wrap;
28236     },
28237
28238     // private
28239     onRender : function(ct, position){
28240         
28241         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28242         //if(this.inputValue !== undefined){
28243         this.wrap = this.el.wrap();
28244         
28245         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
28246         
28247         if (this.bodyStyle) {
28248             this.viewEl.applyStyles(this.bodyStyle);
28249         }
28250         //this.viewEl.setStyle('padding', '2px');
28251         
28252         this.setValue(this.value);
28253         
28254     },
28255 /*
28256     // private
28257     initValue : Roo.emptyFn,
28258
28259   */
28260
28261         // private
28262     onClick : function(){
28263         
28264     },
28265
28266     /**
28267      * Sets the checked state of the checkbox.
28268      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28269      */
28270     setValue : function(v){
28271         this.value = v;
28272         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28273         // this might be called before we have a dom element..
28274         if (!this.viewEl) {
28275             return;
28276         }
28277         this.viewEl.dom.innerHTML = html;
28278         Roo.form.DisplayField.superclass.setValue.call(this, v);
28279
28280     }
28281 });/*
28282  * 
28283  * Licence- LGPL
28284  * 
28285  */
28286
28287 /**
28288  * @class Roo.form.DayPicker
28289  * @extends Roo.form.Field
28290  * A Day picker show [M] [T] [W] ....
28291  * @constructor
28292  * Creates a new Day Picker
28293  * @param {Object} config Configuration options
28294  */
28295 Roo.form.DayPicker= function(config){
28296     Roo.form.DayPicker.superclass.constructor.call(this, config);
28297      
28298 };
28299
28300 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
28301     /**
28302      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28303      */
28304     focusClass : undefined,
28305     /**
28306      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28307      */
28308     fieldClass: "x-form-field",
28309    
28310     /**
28311      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28312      * {tag: "input", type: "checkbox", autocomplete: "off"})
28313      */
28314     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
28315     
28316    
28317     actionMode : 'viewEl', 
28318     //
28319     // private
28320  
28321     inputType : 'hidden',
28322     
28323      
28324     inputElement: false, // real input element?
28325     basedOn: false, // ????
28326     
28327     isFormField: true, // not sure where this is needed!!!!
28328
28329     onResize : function(){
28330         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
28331         if(!this.boxLabel){
28332             this.el.alignTo(this.wrap, 'c-c');
28333         }
28334     },
28335
28336     initEvents : function(){
28337         Roo.form.Checkbox.superclass.initEvents.call(this);
28338         this.el.on("click", this.onClick,  this);
28339         this.el.on("change", this.onClick,  this);
28340     },
28341
28342
28343     getResizeEl : function(){
28344         return this.wrap;
28345     },
28346
28347     getPositionEl : function(){
28348         return this.wrap;
28349     },
28350
28351     
28352     // private
28353     onRender : function(ct, position){
28354         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
28355        
28356         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
28357         
28358         var r1 = '<table><tr>';
28359         var r2 = '<tr class="x-form-daypick-icons">';
28360         for (var i=0; i < 7; i++) {
28361             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
28362             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
28363         }
28364         
28365         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
28366         viewEl.select('img').on('click', this.onClick, this);
28367         this.viewEl = viewEl;   
28368         
28369         
28370         // this will not work on Chrome!!!
28371         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
28372         this.el.on('propertychange', this.setFromHidden,  this);  //ie
28373         
28374         
28375           
28376
28377     },
28378
28379     // private
28380     initValue : Roo.emptyFn,
28381
28382     /**
28383      * Returns the checked state of the checkbox.
28384      * @return {Boolean} True if checked, else false
28385      */
28386     getValue : function(){
28387         return this.el.dom.value;
28388         
28389     },
28390
28391         // private
28392     onClick : function(e){ 
28393         //this.setChecked(!this.checked);
28394         Roo.get(e.target).toggleClass('x-menu-item-checked');
28395         this.refreshValue();
28396         //if(this.el.dom.checked != this.checked){
28397         //    this.setValue(this.el.dom.checked);
28398        // }
28399     },
28400     
28401     // private
28402     refreshValue : function()
28403     {
28404         var val = '';
28405         this.viewEl.select('img',true).each(function(e,i,n)  {
28406             val += e.is(".x-menu-item-checked") ? String(n) : '';
28407         });
28408         this.setValue(val, true);
28409     },
28410
28411     /**
28412      * Sets the checked state of the checkbox.
28413      * On is always based on a string comparison between inputValue and the param.
28414      * @param {Boolean/String} value - the value to set 
28415      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
28416      */
28417     setValue : function(v,suppressEvent){
28418         if (!this.el.dom) {
28419             return;
28420         }
28421         var old = this.el.dom.value ;
28422         this.el.dom.value = v;
28423         if (suppressEvent) {
28424             return ;
28425         }
28426          
28427         // update display..
28428         this.viewEl.select('img',true).each(function(e,i,n)  {
28429             
28430             var on = e.is(".x-menu-item-checked");
28431             var newv = v.indexOf(String(n)) > -1;
28432             if (on != newv) {
28433                 e.toggleClass('x-menu-item-checked');
28434             }
28435             
28436         });
28437         
28438         
28439         this.fireEvent('change', this, v, old);
28440         
28441         
28442     },
28443    
28444     // handle setting of hidden value by some other method!!?!?
28445     setFromHidden: function()
28446     {
28447         if(!this.el){
28448             return;
28449         }
28450         //console.log("SET FROM HIDDEN");
28451         //alert('setFrom hidden');
28452         this.setValue(this.el.dom.value);
28453     },
28454     
28455     onDestroy : function()
28456     {
28457         if(this.viewEl){
28458             Roo.get(this.viewEl).remove();
28459         }
28460          
28461         Roo.form.DayPicker.superclass.onDestroy.call(this);
28462     }
28463
28464 });//<script type="text/javasscript">
28465  
28466
28467 /**
28468  * @class Roo.DDView
28469  * A DnD enabled version of Roo.View.
28470  * @param {Element/String} container The Element in which to create the View.
28471  * @param {String} tpl The template string used to create the markup for each element of the View
28472  * @param {Object} config The configuration properties. These include all the config options of
28473  * {@link Roo.View} plus some specific to this class.<br>
28474  * <p>
28475  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28476  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28477  * <p>
28478  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28479 .x-view-drag-insert-above {
28480         border-top:1px dotted #3366cc;
28481 }
28482 .x-view-drag-insert-below {
28483         border-bottom:1px dotted #3366cc;
28484 }
28485 </code></pre>
28486  * 
28487  */
28488  
28489 Roo.DDView = function(container, tpl, config) {
28490     Roo.DDView.superclass.constructor.apply(this, arguments);
28491     this.getEl().setStyle("outline", "0px none");
28492     this.getEl().unselectable();
28493     if (this.dragGroup) {
28494                 this.setDraggable(this.dragGroup.split(","));
28495     }
28496     if (this.dropGroup) {
28497                 this.setDroppable(this.dropGroup.split(","));
28498     }
28499     if (this.deletable) {
28500         this.setDeletable();
28501     }
28502     this.isDirtyFlag = false;
28503         this.addEvents({
28504                 "drop" : true
28505         });
28506 };
28507
28508 Roo.extend(Roo.DDView, Roo.View, {
28509 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28510 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28511 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28512 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28513
28514         isFormField: true,
28515
28516         reset: Roo.emptyFn,
28517         
28518         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28519
28520         validate: function() {
28521                 return true;
28522         },
28523         
28524         destroy: function() {
28525                 this.purgeListeners();
28526                 this.getEl.removeAllListeners();
28527                 this.getEl().remove();
28528                 if (this.dragZone) {
28529                         if (this.dragZone.destroy) {
28530                                 this.dragZone.destroy();
28531                         }
28532                 }
28533                 if (this.dropZone) {
28534                         if (this.dropZone.destroy) {
28535                                 this.dropZone.destroy();
28536                         }
28537                 }
28538         },
28539
28540 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28541         getName: function() {
28542                 return this.name;
28543         },
28544
28545 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28546         setValue: function(v) {
28547                 if (!this.store) {
28548                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28549                 }
28550                 var data = {};
28551                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28552                 this.store.proxy = new Roo.data.MemoryProxy(data);
28553                 this.store.load();
28554         },
28555
28556 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28557         getValue: function() {
28558                 var result = '(';
28559                 this.store.each(function(rec) {
28560                         result += rec.id + ',';
28561                 });
28562                 return result.substr(0, result.length - 1) + ')';
28563         },
28564         
28565         getIds: function() {
28566                 var i = 0, result = new Array(this.store.getCount());
28567                 this.store.each(function(rec) {
28568                         result[i++] = rec.id;
28569                 });
28570                 return result;
28571         },
28572         
28573         isDirty: function() {
28574                 return this.isDirtyFlag;
28575         },
28576
28577 /**
28578  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28579  *      whole Element becomes the target, and this causes the drop gesture to append.
28580  */
28581     getTargetFromEvent : function(e) {
28582                 var target = e.getTarget();
28583                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28584                 target = target.parentNode;
28585                 }
28586                 if (!target) {
28587                         target = this.el.dom.lastChild || this.el.dom;
28588                 }
28589                 return target;
28590     },
28591
28592 /**
28593  *      Create the drag data which consists of an object which has the property "ddel" as
28594  *      the drag proxy element. 
28595  */
28596     getDragData : function(e) {
28597         var target = this.findItemFromChild(e.getTarget());
28598                 if(target) {
28599                         this.handleSelection(e);
28600                         var selNodes = this.getSelectedNodes();
28601             var dragData = {
28602                 source: this,
28603                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28604                 nodes: selNodes,
28605                 records: []
28606                         };
28607                         var selectedIndices = this.getSelectedIndexes();
28608                         for (var i = 0; i < selectedIndices.length; i++) {
28609                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28610                         }
28611                         if (selNodes.length == 1) {
28612                                 dragData.ddel = target.cloneNode(true); // the div element
28613                         } else {
28614                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28615                                 div.className = 'multi-proxy';
28616                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28617                                         div.appendChild(selNodes[i].cloneNode(true));
28618                                 }
28619                                 dragData.ddel = div;
28620                         }
28621             //console.log(dragData)
28622             //console.log(dragData.ddel.innerHTML)
28623                         return dragData;
28624                 }
28625         //console.log('nodragData')
28626                 return false;
28627     },
28628     
28629 /**     Specify to which ddGroup items in this DDView may be dragged. */
28630     setDraggable: function(ddGroup) {
28631         if (ddGroup instanceof Array) {
28632                 Roo.each(ddGroup, this.setDraggable, this);
28633                 return;
28634         }
28635         if (this.dragZone) {
28636                 this.dragZone.addToGroup(ddGroup);
28637         } else {
28638                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28639                                 containerScroll: true,
28640                                 ddGroup: ddGroup 
28641
28642                         });
28643 //                      Draggability implies selection. DragZone's mousedown selects the element.
28644                         if (!this.multiSelect) { this.singleSelect = true; }
28645
28646 //                      Wire the DragZone's handlers up to methods in *this*
28647                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28648                 }
28649     },
28650
28651 /**     Specify from which ddGroup this DDView accepts drops. */
28652     setDroppable: function(ddGroup) {
28653         if (ddGroup instanceof Array) {
28654                 Roo.each(ddGroup, this.setDroppable, this);
28655                 return;
28656         }
28657         if (this.dropZone) {
28658                 this.dropZone.addToGroup(ddGroup);
28659         } else {
28660                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28661                                 containerScroll: true,
28662                                 ddGroup: ddGroup
28663                         });
28664
28665 //                      Wire the DropZone's handlers up to methods in *this*
28666                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28667                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28668                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28669                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28670                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28671                 }
28672     },
28673
28674 /**     Decide whether to drop above or below a View node. */
28675     getDropPoint : function(e, n, dd){
28676         if (n == this.el.dom) { return "above"; }
28677                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28678                 var c = t + (b - t) / 2;
28679                 var y = Roo.lib.Event.getPageY(e);
28680                 if(y <= c) {
28681                         return "above";
28682                 }else{
28683                         return "below";
28684                 }
28685     },
28686
28687     onNodeEnter : function(n, dd, e, data){
28688                 return false;
28689     },
28690     
28691     onNodeOver : function(n, dd, e, data){
28692                 var pt = this.getDropPoint(e, n, dd);
28693                 // set the insert point style on the target node
28694                 var dragElClass = this.dropNotAllowed;
28695                 if (pt) {
28696                         var targetElClass;
28697                         if (pt == "above"){
28698                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28699                                 targetElClass = "x-view-drag-insert-above";
28700                         } else {
28701                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28702                                 targetElClass = "x-view-drag-insert-below";
28703                         }
28704                         if (this.lastInsertClass != targetElClass){
28705                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28706                                 this.lastInsertClass = targetElClass;
28707                         }
28708                 }
28709                 return dragElClass;
28710         },
28711
28712     onNodeOut : function(n, dd, e, data){
28713                 this.removeDropIndicators(n);
28714     },
28715
28716     onNodeDrop : function(n, dd, e, data){
28717         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28718                 return false;
28719         }
28720         var pt = this.getDropPoint(e, n, dd);
28721                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28722                 if (pt == "below") { insertAt++; }
28723                 for (var i = 0; i < data.records.length; i++) {
28724                         var r = data.records[i];
28725                         var dup = this.store.getById(r.id);
28726                         if (dup && (dd != this.dragZone)) {
28727                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28728                         } else {
28729                                 if (data.copy) {
28730                                         this.store.insert(insertAt++, r.copy());
28731                                 } else {
28732                                         data.source.isDirtyFlag = true;
28733                                         r.store.remove(r);
28734                                         this.store.insert(insertAt++, r);
28735                                 }
28736                                 this.isDirtyFlag = true;
28737                         }
28738                 }
28739                 this.dragZone.cachedTarget = null;
28740                 return true;
28741     },
28742
28743     removeDropIndicators : function(n){
28744                 if(n){
28745                         Roo.fly(n).removeClass([
28746                                 "x-view-drag-insert-above",
28747                                 "x-view-drag-insert-below"]);
28748                         this.lastInsertClass = "_noclass";
28749                 }
28750     },
28751
28752 /**
28753  *      Utility method. Add a delete option to the DDView's context menu.
28754  *      @param {String} imageUrl The URL of the "delete" icon image.
28755  */
28756         setDeletable: function(imageUrl) {
28757                 if (!this.singleSelect && !this.multiSelect) {
28758                         this.singleSelect = true;
28759                 }
28760                 var c = this.getContextMenu();
28761                 this.contextMenu.on("itemclick", function(item) {
28762                         switch (item.id) {
28763                                 case "delete":
28764                                         this.remove(this.getSelectedIndexes());
28765                                         break;
28766                         }
28767                 }, this);
28768                 this.contextMenu.add({
28769                         icon: imageUrl,
28770                         id: "delete",
28771                         text: 'Delete'
28772                 });
28773         },
28774         
28775 /**     Return the context menu for this DDView. */
28776         getContextMenu: function() {
28777                 if (!this.contextMenu) {
28778 //                      Create the View's context menu
28779                         this.contextMenu = new Roo.menu.Menu({
28780                                 id: this.id + "-contextmenu"
28781                         });
28782                         this.el.on("contextmenu", this.showContextMenu, this);
28783                 }
28784                 return this.contextMenu;
28785         },
28786         
28787         disableContextMenu: function() {
28788                 if (this.contextMenu) {
28789                         this.el.un("contextmenu", this.showContextMenu, this);
28790                 }
28791         },
28792
28793         showContextMenu: function(e, item) {
28794         item = this.findItemFromChild(e.getTarget());
28795                 if (item) {
28796                         e.stopEvent();
28797                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28798                         this.contextMenu.showAt(e.getXY());
28799             }
28800     },
28801
28802 /**
28803  *      Remove {@link Roo.data.Record}s at the specified indices.
28804  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28805  */
28806     remove: function(selectedIndices) {
28807                 selectedIndices = [].concat(selectedIndices);
28808                 for (var i = 0; i < selectedIndices.length; i++) {
28809                         var rec = this.store.getAt(selectedIndices[i]);
28810                         this.store.remove(rec);
28811                 }
28812     },
28813
28814 /**
28815  *      Double click fires the event, but also, if this is draggable, and there is only one other
28816  *      related DropZone, it transfers the selected node.
28817  */
28818     onDblClick : function(e){
28819         var item = this.findItemFromChild(e.getTarget());
28820         if(item){
28821             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28822                 return false;
28823             }
28824             if (this.dragGroup) {
28825                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28826                     while (targets.indexOf(this.dropZone) > -1) {
28827                             targets.remove(this.dropZone);
28828                                 }
28829                     if (targets.length == 1) {
28830                                         this.dragZone.cachedTarget = null;
28831                         var el = Roo.get(targets[0].getEl());
28832                         var box = el.getBox(true);
28833                         targets[0].onNodeDrop(el.dom, {
28834                                 target: el.dom,
28835                                 xy: [box.x, box.y + box.height - 1]
28836                         }, null, this.getDragData(e));
28837                     }
28838                 }
28839         }
28840     },
28841     
28842     handleSelection: function(e) {
28843                 this.dragZone.cachedTarget = null;
28844         var item = this.findItemFromChild(e.getTarget());
28845         if (!item) {
28846                 this.clearSelections(true);
28847                 return;
28848         }
28849                 if (item && (this.multiSelect || this.singleSelect)){
28850                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28851                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28852                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28853                                 this.unselect(item);
28854                         } else {
28855                                 this.select(item, this.multiSelect && e.ctrlKey);
28856                                 this.lastSelection = item;
28857                         }
28858                 }
28859     },
28860
28861     onItemClick : function(item, index, e){
28862                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28863                         return false;
28864                 }
28865                 return true;
28866     },
28867
28868     unselect : function(nodeInfo, suppressEvent){
28869                 var node = this.getNode(nodeInfo);
28870                 if(node && this.isSelected(node)){
28871                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28872                                 Roo.fly(node).removeClass(this.selectedClass);
28873                                 this.selections.remove(node);
28874                                 if(!suppressEvent){
28875                                         this.fireEvent("selectionchange", this, this.selections);
28876                                 }
28877                         }
28878                 }
28879     }
28880 });
28881 /*
28882  * Based on:
28883  * Ext JS Library 1.1.1
28884  * Copyright(c) 2006-2007, Ext JS, LLC.
28885  *
28886  * Originally Released Under LGPL - original licence link has changed is not relivant.
28887  *
28888  * Fork - LGPL
28889  * <script type="text/javascript">
28890  */
28891  
28892 /**
28893  * @class Roo.LayoutManager
28894  * @extends Roo.util.Observable
28895  * Base class for layout managers.
28896  */
28897 Roo.LayoutManager = function(container, config){
28898     Roo.LayoutManager.superclass.constructor.call(this);
28899     this.el = Roo.get(container);
28900     // ie scrollbar fix
28901     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28902         document.body.scroll = "no";
28903     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28904         this.el.position('relative');
28905     }
28906     this.id = this.el.id;
28907     this.el.addClass("x-layout-container");
28908     /** false to disable window resize monitoring @type Boolean */
28909     this.monitorWindowResize = true;
28910     this.regions = {};
28911     this.addEvents({
28912         /**
28913          * @event layout
28914          * Fires when a layout is performed. 
28915          * @param {Roo.LayoutManager} this
28916          */
28917         "layout" : true,
28918         /**
28919          * @event regionresized
28920          * Fires when the user resizes a region. 
28921          * @param {Roo.LayoutRegion} region The resized region
28922          * @param {Number} newSize The new size (width for east/west, height for north/south)
28923          */
28924         "regionresized" : true,
28925         /**
28926          * @event regioncollapsed
28927          * Fires when a region is collapsed. 
28928          * @param {Roo.LayoutRegion} region The collapsed region
28929          */
28930         "regioncollapsed" : true,
28931         /**
28932          * @event regionexpanded
28933          * Fires when a region is expanded.  
28934          * @param {Roo.LayoutRegion} region The expanded region
28935          */
28936         "regionexpanded" : true
28937     });
28938     this.updating = false;
28939     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28940 };
28941
28942 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28943     /**
28944      * Returns true if this layout is currently being updated
28945      * @return {Boolean}
28946      */
28947     isUpdating : function(){
28948         return this.updating; 
28949     },
28950     
28951     /**
28952      * Suspend the LayoutManager from doing auto-layouts while
28953      * making multiple add or remove calls
28954      */
28955     beginUpdate : function(){
28956         this.updating = true;    
28957     },
28958     
28959     /**
28960      * Restore auto-layouts and optionally disable the manager from performing a layout
28961      * @param {Boolean} noLayout true to disable a layout update 
28962      */
28963     endUpdate : function(noLayout){
28964         this.updating = false;
28965         if(!noLayout){
28966             this.layout();
28967         }    
28968     },
28969     
28970     layout: function(){
28971         
28972     },
28973     
28974     onRegionResized : function(region, newSize){
28975         this.fireEvent("regionresized", region, newSize);
28976         this.layout();
28977     },
28978     
28979     onRegionCollapsed : function(region){
28980         this.fireEvent("regioncollapsed", region);
28981     },
28982     
28983     onRegionExpanded : function(region){
28984         this.fireEvent("regionexpanded", region);
28985     },
28986         
28987     /**
28988      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28989      * performs box-model adjustments.
28990      * @return {Object} The size as an object {width: (the width), height: (the height)}
28991      */
28992     getViewSize : function(){
28993         var size;
28994         if(this.el.dom != document.body){
28995             size = this.el.getSize();
28996         }else{
28997             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28998         }
28999         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29000         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29001         return size;
29002     },
29003     
29004     /**
29005      * Returns the Element this layout is bound to.
29006      * @return {Roo.Element}
29007      */
29008     getEl : function(){
29009         return this.el;
29010     },
29011     
29012     /**
29013      * Returns the specified region.
29014      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29015      * @return {Roo.LayoutRegion}
29016      */
29017     getRegion : function(target){
29018         return this.regions[target.toLowerCase()];
29019     },
29020     
29021     onWindowResize : function(){
29022         if(this.monitorWindowResize){
29023             this.layout();
29024         }
29025     }
29026 });/*
29027  * Based on:
29028  * Ext JS Library 1.1.1
29029  * Copyright(c) 2006-2007, Ext JS, LLC.
29030  *
29031  * Originally Released Under LGPL - original licence link has changed is not relivant.
29032  *
29033  * Fork - LGPL
29034  * <script type="text/javascript">
29035  */
29036 /**
29037  * @class Roo.BorderLayout
29038  * @extends Roo.LayoutManager
29039  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29040  * please see: <br><br>
29041  * <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>
29042  * <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>
29043  * Example:
29044  <pre><code>
29045  var layout = new Roo.BorderLayout(document.body, {
29046     north: {
29047         initialSize: 25,
29048         titlebar: false
29049     },
29050     west: {
29051         split:true,
29052         initialSize: 200,
29053         minSize: 175,
29054         maxSize: 400,
29055         titlebar: true,
29056         collapsible: true
29057     },
29058     east: {
29059         split:true,
29060         initialSize: 202,
29061         minSize: 175,
29062         maxSize: 400,
29063         titlebar: true,
29064         collapsible: true
29065     },
29066     south: {
29067         split:true,
29068         initialSize: 100,
29069         minSize: 100,
29070         maxSize: 200,
29071         titlebar: true,
29072         collapsible: true
29073     },
29074     center: {
29075         titlebar: true,
29076         autoScroll:true,
29077         resizeTabs: true,
29078         minTabWidth: 50,
29079         preferredTabWidth: 150
29080     }
29081 });
29082
29083 // shorthand
29084 var CP = Roo.ContentPanel;
29085
29086 layout.beginUpdate();
29087 layout.add("north", new CP("north", "North"));
29088 layout.add("south", new CP("south", {title: "South", closable: true}));
29089 layout.add("west", new CP("west", {title: "West"}));
29090 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29091 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29092 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29093 layout.getRegion("center").showPanel("center1");
29094 layout.endUpdate();
29095 </code></pre>
29096
29097 <b>The container the layout is rendered into can be either the body element or any other element.
29098 If it is not the body element, the container needs to either be an absolute positioned element,
29099 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29100 the container size if it is not the body element.</b>
29101
29102 * @constructor
29103 * Create a new BorderLayout
29104 * @param {String/HTMLElement/Element} container The container this layout is bound to
29105 * @param {Object} config Configuration options
29106  */
29107 Roo.BorderLayout = function(container, config){
29108     config = config || {};
29109     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29110     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29111     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29112         var target = this.factory.validRegions[i];
29113         if(config[target]){
29114             this.addRegion(target, config[target]);
29115         }
29116     }
29117 };
29118
29119 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29120     /**
29121      * Creates and adds a new region if it doesn't already exist.
29122      * @param {String} target The target region key (north, south, east, west or center).
29123      * @param {Object} config The regions config object
29124      * @return {BorderLayoutRegion} The new region
29125      */
29126     addRegion : function(target, config){
29127         if(!this.regions[target]){
29128             var r = this.factory.create(target, this, config);
29129             this.bindRegion(target, r);
29130         }
29131         return this.regions[target];
29132     },
29133
29134     // private (kinda)
29135     bindRegion : function(name, r){
29136         this.regions[name] = r;
29137         r.on("visibilitychange", this.layout, this);
29138         r.on("paneladded", this.layout, this);
29139         r.on("panelremoved", this.layout, this);
29140         r.on("invalidated", this.layout, this);
29141         r.on("resized", this.onRegionResized, this);
29142         r.on("collapsed", this.onRegionCollapsed, this);
29143         r.on("expanded", this.onRegionExpanded, this);
29144     },
29145
29146     /**
29147      * Performs a layout update.
29148      */
29149     layout : function(){
29150         if(this.updating) return;
29151         var size = this.getViewSize();
29152         var w = size.width;
29153         var h = size.height;
29154         var centerW = w;
29155         var centerH = h;
29156         var centerY = 0;
29157         var centerX = 0;
29158         //var x = 0, y = 0;
29159
29160         var rs = this.regions;
29161         var north = rs["north"];
29162         var south = rs["south"]; 
29163         var west = rs["west"];
29164         var east = rs["east"];
29165         var center = rs["center"];
29166         //if(this.hideOnLayout){ // not supported anymore
29167             //c.el.setStyle("display", "none");
29168         //}
29169         if(north && north.isVisible()){
29170             var b = north.getBox();
29171             var m = north.getMargins();
29172             b.width = w - (m.left+m.right);
29173             b.x = m.left;
29174             b.y = m.top;
29175             centerY = b.height + b.y + m.bottom;
29176             centerH -= centerY;
29177             north.updateBox(this.safeBox(b));
29178         }
29179         if(south && south.isVisible()){
29180             var b = south.getBox();
29181             var m = south.getMargins();
29182             b.width = w - (m.left+m.right);
29183             b.x = m.left;
29184             var totalHeight = (b.height + m.top + m.bottom);
29185             b.y = h - totalHeight + m.top;
29186             centerH -= totalHeight;
29187             south.updateBox(this.safeBox(b));
29188         }
29189         if(west && west.isVisible()){
29190             var b = west.getBox();
29191             var m = west.getMargins();
29192             b.height = centerH - (m.top+m.bottom);
29193             b.x = m.left;
29194             b.y = centerY + m.top;
29195             var totalWidth = (b.width + m.left + m.right);
29196             centerX += totalWidth;
29197             centerW -= totalWidth;
29198             west.updateBox(this.safeBox(b));
29199         }
29200         if(east && east.isVisible()){
29201             var b = east.getBox();
29202             var m = east.getMargins();
29203             b.height = centerH - (m.top+m.bottom);
29204             var totalWidth = (b.width + m.left + m.right);
29205             b.x = w - totalWidth + m.left;
29206             b.y = centerY + m.top;
29207             centerW -= totalWidth;
29208             east.updateBox(this.safeBox(b));
29209         }
29210         if(center){
29211             var m = center.getMargins();
29212             var centerBox = {
29213                 x: centerX + m.left,
29214                 y: centerY + m.top,
29215                 width: centerW - (m.left+m.right),
29216                 height: centerH - (m.top+m.bottom)
29217             };
29218             //if(this.hideOnLayout){
29219                 //center.el.setStyle("display", "block");
29220             //}
29221             center.updateBox(this.safeBox(centerBox));
29222         }
29223         this.el.repaint();
29224         this.fireEvent("layout", this);
29225     },
29226
29227     // private
29228     safeBox : function(box){
29229         box.width = Math.max(0, box.width);
29230         box.height = Math.max(0, box.height);
29231         return box;
29232     },
29233
29234     /**
29235      * Adds a ContentPanel (or subclass) to this layout.
29236      * @param {String} target The target region key (north, south, east, west or center).
29237      * @param {Roo.ContentPanel} panel The panel to add
29238      * @return {Roo.ContentPanel} The added panel
29239      */
29240     add : function(target, panel){
29241          
29242         target = target.toLowerCase();
29243         return this.regions[target].add(panel);
29244     },
29245
29246     /**
29247      * Remove a ContentPanel (or subclass) to this layout.
29248      * @param {String} target The target region key (north, south, east, west or center).
29249      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29250      * @return {Roo.ContentPanel} The removed panel
29251      */
29252     remove : function(target, panel){
29253         target = target.toLowerCase();
29254         return this.regions[target].remove(panel);
29255     },
29256
29257     /**
29258      * Searches all regions for a panel with the specified id
29259      * @param {String} panelId
29260      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29261      */
29262     findPanel : function(panelId){
29263         var rs = this.regions;
29264         for(var target in rs){
29265             if(typeof rs[target] != "function"){
29266                 var p = rs[target].getPanel(panelId);
29267                 if(p){
29268                     return p;
29269                 }
29270             }
29271         }
29272         return null;
29273     },
29274
29275     /**
29276      * Searches all regions for a panel with the specified id and activates (shows) it.
29277      * @param {String/ContentPanel} panelId The panels id or the panel itself
29278      * @return {Roo.ContentPanel} The shown panel or null
29279      */
29280     showPanel : function(panelId) {
29281       var rs = this.regions;
29282       for(var target in rs){
29283          var r = rs[target];
29284          if(typeof r != "function"){
29285             if(r.hasPanel(panelId)){
29286                return r.showPanel(panelId);
29287             }
29288          }
29289       }
29290       return null;
29291    },
29292
29293    /**
29294      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29295      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29296      */
29297     restoreState : function(provider){
29298         if(!provider){
29299             provider = Roo.state.Manager;
29300         }
29301         var sm = new Roo.LayoutStateManager();
29302         sm.init(this, provider);
29303     },
29304
29305     /**
29306      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29307      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29308      * a valid ContentPanel config object.  Example:
29309      * <pre><code>
29310 // Create the main layout
29311 var layout = new Roo.BorderLayout('main-ct', {
29312     west: {
29313         split:true,
29314         minSize: 175,
29315         titlebar: true
29316     },
29317     center: {
29318         title:'Components'
29319     }
29320 }, 'main-ct');
29321
29322 // Create and add multiple ContentPanels at once via configs
29323 layout.batchAdd({
29324    west: {
29325        id: 'source-files',
29326        autoCreate:true,
29327        title:'Ext Source Files',
29328        autoScroll:true,
29329        fitToFrame:true
29330    },
29331    center : {
29332        el: cview,
29333        autoScroll:true,
29334        fitToFrame:true,
29335        toolbar: tb,
29336        resizeEl:'cbody'
29337    }
29338 });
29339 </code></pre>
29340      * @param {Object} regions An object containing ContentPanel configs by region name
29341      */
29342     batchAdd : function(regions){
29343         this.beginUpdate();
29344         for(var rname in regions){
29345             var lr = this.regions[rname];
29346             if(lr){
29347                 this.addTypedPanels(lr, regions[rname]);
29348             }
29349         }
29350         this.endUpdate();
29351     },
29352
29353     // private
29354     addTypedPanels : function(lr, ps){
29355         if(typeof ps == 'string'){
29356             lr.add(new Roo.ContentPanel(ps));
29357         }
29358         else if(ps instanceof Array){
29359             for(var i =0, len = ps.length; i < len; i++){
29360                 this.addTypedPanels(lr, ps[i]);
29361             }
29362         }
29363         else if(!ps.events){ // raw config?
29364             var el = ps.el;
29365             delete ps.el; // prevent conflict
29366             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29367         }
29368         else {  // panel object assumed!
29369             lr.add(ps);
29370         }
29371     },
29372     /**
29373      * Adds a xtype elements to the layout.
29374      * <pre><code>
29375
29376 layout.addxtype({
29377        xtype : 'ContentPanel',
29378        region: 'west',
29379        items: [ .... ]
29380    }
29381 );
29382
29383 layout.addxtype({
29384         xtype : 'NestedLayoutPanel',
29385         region: 'west',
29386         layout: {
29387            center: { },
29388            west: { }   
29389         },
29390         items : [ ... list of content panels or nested layout panels.. ]
29391    }
29392 );
29393 </code></pre>
29394      * @param {Object} cfg Xtype definition of item to add.
29395      */
29396     addxtype : function(cfg)
29397     {
29398         // basically accepts a pannel...
29399         // can accept a layout region..!?!?
29400        // console.log('BorderLayout add ' + cfg.xtype)
29401         
29402         if (!cfg.xtype.match(/Panel$/)) {
29403             return false;
29404         }
29405         var ret = false;
29406         var region = cfg.region;
29407         delete cfg.region;
29408         
29409           
29410         var xitems = [];
29411         if (cfg.items) {
29412             xitems = cfg.items;
29413             delete cfg.items;
29414         }
29415         
29416         
29417         switch(cfg.xtype) 
29418         {
29419             case 'ContentPanel':  // ContentPanel (el, cfg)
29420             case 'ScrollPanel':  // ContentPanel (el, cfg)
29421                 if(cfg.autoCreate) {
29422                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29423                 } else {
29424                     var el = this.el.createChild();
29425                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29426                 }
29427                 
29428                 this.add(region, ret);
29429                 break;
29430             
29431             
29432             case 'TreePanel': // our new panel!
29433                 cfg.el = this.el.createChild();
29434                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29435                 this.add(region, ret);
29436                 break;
29437             
29438             case 'NestedLayoutPanel': 
29439                 // create a new Layout (which is  a Border Layout...
29440                 var el = this.el.createChild();
29441                 var clayout = cfg.layout;
29442                 delete cfg.layout;
29443                 clayout.items   = clayout.items  || [];
29444                 // replace this exitems with the clayout ones..
29445                 xitems = clayout.items;
29446                  
29447                 
29448                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29449                     cfg.background = false;
29450                 }
29451                 var layout = new Roo.BorderLayout(el, clayout);
29452                 
29453                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29454                 //console.log('adding nested layout panel '  + cfg.toSource());
29455                 this.add(region, ret);
29456                 
29457                 break;
29458                 
29459             case 'GridPanel': 
29460             
29461                 // needs grid and region
29462                 
29463                 //var el = this.getRegion(region).el.createChild();
29464                 var el = this.el.createChild();
29465                 // create the grid first...
29466                 
29467                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29468                 delete cfg.grid;
29469                 if (region == 'center' && this.active ) {
29470                     cfg.background = false;
29471                 }
29472                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29473                 
29474                 this.add(region, ret);
29475                 if (cfg.background) {
29476                     ret.on('activate', function(gp) {
29477                         if (!gp.grid.rendered) {
29478                             gp.grid.render();
29479                         }
29480                     });
29481                 } else {
29482                     grid.render();
29483                 }
29484                 break;
29485            
29486                
29487                 
29488                 
29489             default: 
29490                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29491                 return null;
29492              // GridPanel (grid, cfg)
29493             
29494         }
29495         this.beginUpdate();
29496         // add children..
29497         Roo.each(xitems, function(i)  {
29498             ret.addxtype(i);
29499         });
29500         this.endUpdate();
29501         return ret;
29502         
29503     }
29504 });
29505
29506 /**
29507  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29508  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29509  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29510  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29511  * <pre><code>
29512 // shorthand
29513 var CP = Roo.ContentPanel;
29514
29515 var layout = Roo.BorderLayout.create({
29516     north: {
29517         initialSize: 25,
29518         titlebar: false,
29519         panels: [new CP("north", "North")]
29520     },
29521     west: {
29522         split:true,
29523         initialSize: 200,
29524         minSize: 175,
29525         maxSize: 400,
29526         titlebar: true,
29527         collapsible: true,
29528         panels: [new CP("west", {title: "West"})]
29529     },
29530     east: {
29531         split:true,
29532         initialSize: 202,
29533         minSize: 175,
29534         maxSize: 400,
29535         titlebar: true,
29536         collapsible: true,
29537         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29538     },
29539     south: {
29540         split:true,
29541         initialSize: 100,
29542         minSize: 100,
29543         maxSize: 200,
29544         titlebar: true,
29545         collapsible: true,
29546         panels: [new CP("south", {title: "South", closable: true})]
29547     },
29548     center: {
29549         titlebar: true,
29550         autoScroll:true,
29551         resizeTabs: true,
29552         minTabWidth: 50,
29553         preferredTabWidth: 150,
29554         panels: [
29555             new CP("center1", {title: "Close Me", closable: true}),
29556             new CP("center2", {title: "Center Panel", closable: false})
29557         ]
29558     }
29559 }, document.body);
29560
29561 layout.getRegion("center").showPanel("center1");
29562 </code></pre>
29563  * @param config
29564  * @param targetEl
29565  */
29566 Roo.BorderLayout.create = function(config, targetEl){
29567     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29568     layout.beginUpdate();
29569     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29570     for(var j = 0, jlen = regions.length; j < jlen; j++){
29571         var lr = regions[j];
29572         if(layout.regions[lr] && config[lr].panels){
29573             var r = layout.regions[lr];
29574             var ps = config[lr].panels;
29575             layout.addTypedPanels(r, ps);
29576         }
29577     }
29578     layout.endUpdate();
29579     return layout;
29580 };
29581
29582 // private
29583 Roo.BorderLayout.RegionFactory = {
29584     // private
29585     validRegions : ["north","south","east","west","center"],
29586
29587     // private
29588     create : function(target, mgr, config){
29589         target = target.toLowerCase();
29590         if(config.lightweight || config.basic){
29591             return new Roo.BasicLayoutRegion(mgr, config, target);
29592         }
29593         switch(target){
29594             case "north":
29595                 return new Roo.NorthLayoutRegion(mgr, config);
29596             case "south":
29597                 return new Roo.SouthLayoutRegion(mgr, config);
29598             case "east":
29599                 return new Roo.EastLayoutRegion(mgr, config);
29600             case "west":
29601                 return new Roo.WestLayoutRegion(mgr, config);
29602             case "center":
29603                 return new Roo.CenterLayoutRegion(mgr, config);
29604         }
29605         throw 'Layout region "'+target+'" not supported.';
29606     }
29607 };/*
29608  * Based on:
29609  * Ext JS Library 1.1.1
29610  * Copyright(c) 2006-2007, Ext JS, LLC.
29611  *
29612  * Originally Released Under LGPL - original licence link has changed is not relivant.
29613  *
29614  * Fork - LGPL
29615  * <script type="text/javascript">
29616  */
29617  
29618 /**
29619  * @class Roo.BasicLayoutRegion
29620  * @extends Roo.util.Observable
29621  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29622  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29623  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29624  */
29625 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29626     this.mgr = mgr;
29627     this.position  = pos;
29628     this.events = {
29629         /**
29630          * @scope Roo.BasicLayoutRegion
29631          */
29632         
29633         /**
29634          * @event beforeremove
29635          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29636          * @param {Roo.LayoutRegion} this
29637          * @param {Roo.ContentPanel} panel The panel
29638          * @param {Object} e The cancel event object
29639          */
29640         "beforeremove" : true,
29641         /**
29642          * @event invalidated
29643          * Fires when the layout for this region is changed.
29644          * @param {Roo.LayoutRegion} this
29645          */
29646         "invalidated" : true,
29647         /**
29648          * @event visibilitychange
29649          * Fires when this region is shown or hidden 
29650          * @param {Roo.LayoutRegion} this
29651          * @param {Boolean} visibility true or false
29652          */
29653         "visibilitychange" : true,
29654         /**
29655          * @event paneladded
29656          * Fires when a panel is added. 
29657          * @param {Roo.LayoutRegion} this
29658          * @param {Roo.ContentPanel} panel The panel
29659          */
29660         "paneladded" : true,
29661         /**
29662          * @event panelremoved
29663          * Fires when a panel is removed. 
29664          * @param {Roo.LayoutRegion} this
29665          * @param {Roo.ContentPanel} panel The panel
29666          */
29667         "panelremoved" : true,
29668         /**
29669          * @event collapsed
29670          * Fires when this region is collapsed.
29671          * @param {Roo.LayoutRegion} this
29672          */
29673         "collapsed" : true,
29674         /**
29675          * @event expanded
29676          * Fires when this region is expanded.
29677          * @param {Roo.LayoutRegion} this
29678          */
29679         "expanded" : true,
29680         /**
29681          * @event slideshow
29682          * Fires when this region is slid into view.
29683          * @param {Roo.LayoutRegion} this
29684          */
29685         "slideshow" : true,
29686         /**
29687          * @event slidehide
29688          * Fires when this region slides out of view. 
29689          * @param {Roo.LayoutRegion} this
29690          */
29691         "slidehide" : true,
29692         /**
29693          * @event panelactivated
29694          * Fires when a panel is activated. 
29695          * @param {Roo.LayoutRegion} this
29696          * @param {Roo.ContentPanel} panel The activated panel
29697          */
29698         "panelactivated" : true,
29699         /**
29700          * @event resized
29701          * Fires when the user resizes this region. 
29702          * @param {Roo.LayoutRegion} this
29703          * @param {Number} newSize The new size (width for east/west, height for north/south)
29704          */
29705         "resized" : true
29706     };
29707     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29708     this.panels = new Roo.util.MixedCollection();
29709     this.panels.getKey = this.getPanelId.createDelegate(this);
29710     this.box = null;
29711     this.activePanel = null;
29712     // ensure listeners are added...
29713     
29714     if (config.listeners || config.events) {
29715         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29716             listeners : config.listeners || {},
29717             events : config.events || {}
29718         });
29719     }
29720     
29721     if(skipConfig !== true){
29722         this.applyConfig(config);
29723     }
29724 };
29725
29726 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29727     getPanelId : function(p){
29728         return p.getId();
29729     },
29730     
29731     applyConfig : function(config){
29732         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29733         this.config = config;
29734         
29735     },
29736     
29737     /**
29738      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29739      * the width, for horizontal (north, south) the height.
29740      * @param {Number} newSize The new width or height
29741      */
29742     resizeTo : function(newSize){
29743         var el = this.el ? this.el :
29744                  (this.activePanel ? this.activePanel.getEl() : null);
29745         if(el){
29746             switch(this.position){
29747                 case "east":
29748                 case "west":
29749                     el.setWidth(newSize);
29750                     this.fireEvent("resized", this, newSize);
29751                 break;
29752                 case "north":
29753                 case "south":
29754                     el.setHeight(newSize);
29755                     this.fireEvent("resized", this, newSize);
29756                 break;                
29757             }
29758         }
29759     },
29760     
29761     getBox : function(){
29762         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29763     },
29764     
29765     getMargins : function(){
29766         return this.margins;
29767     },
29768     
29769     updateBox : function(box){
29770         this.box = box;
29771         var el = this.activePanel.getEl();
29772         el.dom.style.left = box.x + "px";
29773         el.dom.style.top = box.y + "px";
29774         this.activePanel.setSize(box.width, box.height);
29775     },
29776     
29777     /**
29778      * Returns the container element for this region.
29779      * @return {Roo.Element}
29780      */
29781     getEl : function(){
29782         return this.activePanel;
29783     },
29784     
29785     /**
29786      * Returns true if this region is currently visible.
29787      * @return {Boolean}
29788      */
29789     isVisible : function(){
29790         return this.activePanel ? true : false;
29791     },
29792     
29793     setActivePanel : function(panel){
29794         panel = this.getPanel(panel);
29795         if(this.activePanel && this.activePanel != panel){
29796             this.activePanel.setActiveState(false);
29797             this.activePanel.getEl().setLeftTop(-10000,-10000);
29798         }
29799         this.activePanel = panel;
29800         panel.setActiveState(true);
29801         if(this.box){
29802             panel.setSize(this.box.width, this.box.height);
29803         }
29804         this.fireEvent("panelactivated", this, panel);
29805         this.fireEvent("invalidated");
29806     },
29807     
29808     /**
29809      * Show the specified panel.
29810      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29811      * @return {Roo.ContentPanel} The shown panel or null
29812      */
29813     showPanel : function(panel){
29814         if(panel = this.getPanel(panel)){
29815             this.setActivePanel(panel);
29816         }
29817         return panel;
29818     },
29819     
29820     /**
29821      * Get the active panel for this region.
29822      * @return {Roo.ContentPanel} The active panel or null
29823      */
29824     getActivePanel : function(){
29825         return this.activePanel;
29826     },
29827     
29828     /**
29829      * Add the passed ContentPanel(s)
29830      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29831      * @return {Roo.ContentPanel} The panel added (if only one was added)
29832      */
29833     add : function(panel){
29834         if(arguments.length > 1){
29835             for(var i = 0, len = arguments.length; i < len; i++) {
29836                 this.add(arguments[i]);
29837             }
29838             return null;
29839         }
29840         if(this.hasPanel(panel)){
29841             this.showPanel(panel);
29842             return panel;
29843         }
29844         var el = panel.getEl();
29845         if(el.dom.parentNode != this.mgr.el.dom){
29846             this.mgr.el.dom.appendChild(el.dom);
29847         }
29848         if(panel.setRegion){
29849             panel.setRegion(this);
29850         }
29851         this.panels.add(panel);
29852         el.setStyle("position", "absolute");
29853         if(!panel.background){
29854             this.setActivePanel(panel);
29855             if(this.config.initialSize && this.panels.getCount()==1){
29856                 this.resizeTo(this.config.initialSize);
29857             }
29858         }
29859         this.fireEvent("paneladded", this, panel);
29860         return panel;
29861     },
29862     
29863     /**
29864      * Returns true if the panel is in this region.
29865      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29866      * @return {Boolean}
29867      */
29868     hasPanel : function(panel){
29869         if(typeof panel == "object"){ // must be panel obj
29870             panel = panel.getId();
29871         }
29872         return this.getPanel(panel) ? true : false;
29873     },
29874     
29875     /**
29876      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29877      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29878      * @param {Boolean} preservePanel Overrides the config preservePanel option
29879      * @return {Roo.ContentPanel} The panel that was removed
29880      */
29881     remove : function(panel, preservePanel){
29882         panel = this.getPanel(panel);
29883         if(!panel){
29884             return null;
29885         }
29886         var e = {};
29887         this.fireEvent("beforeremove", this, panel, e);
29888         if(e.cancel === true){
29889             return null;
29890         }
29891         var panelId = panel.getId();
29892         this.panels.removeKey(panelId);
29893         return panel;
29894     },
29895     
29896     /**
29897      * Returns the panel specified or null if it's not in this region.
29898      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29899      * @return {Roo.ContentPanel}
29900      */
29901     getPanel : function(id){
29902         if(typeof id == "object"){ // must be panel obj
29903             return id;
29904         }
29905         return this.panels.get(id);
29906     },
29907     
29908     /**
29909      * Returns this regions position (north/south/east/west/center).
29910      * @return {String} 
29911      */
29912     getPosition: function(){
29913         return this.position;    
29914     }
29915 });/*
29916  * Based on:
29917  * Ext JS Library 1.1.1
29918  * Copyright(c) 2006-2007, Ext JS, LLC.
29919  *
29920  * Originally Released Under LGPL - original licence link has changed is not relivant.
29921  *
29922  * Fork - LGPL
29923  * <script type="text/javascript">
29924  */
29925  
29926 /**
29927  * @class Roo.LayoutRegion
29928  * @extends Roo.BasicLayoutRegion
29929  * This class represents a region in a layout manager.
29930  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29931  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29932  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29933  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29934  * @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})
29935  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29936  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29937  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29938  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29939  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29940  * @cfg {String} title The title for the region (overrides panel titles)
29941  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29942  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29943  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29944  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29945  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29946  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29947  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29948  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29949  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29950  * @cfg {Boolean} showPin True to show a pin button
29951 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29952 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29953 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29954 * @cfg {Number} width  For East/West panels
29955 * @cfg {Number} height For North/South panels
29956 * @cfg {Boolean} split To show the splitter
29957  */
29958 Roo.LayoutRegion = function(mgr, config, pos){
29959     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29960     var dh = Roo.DomHelper;
29961     /** This region's container element 
29962     * @type Roo.Element */
29963     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29964     /** This region's title element 
29965     * @type Roo.Element */
29966
29967     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29968         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29969         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29970     ]}, true);
29971     this.titleEl.enableDisplayMode();
29972     /** This region's title text element 
29973     * @type HTMLElement */
29974     this.titleTextEl = this.titleEl.dom.firstChild;
29975     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29976     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29977     this.closeBtn.enableDisplayMode();
29978     this.closeBtn.on("click", this.closeClicked, this);
29979     this.closeBtn.hide();
29980
29981     this.createBody(config);
29982     this.visible = true;
29983     this.collapsed = false;
29984
29985     if(config.hideWhenEmpty){
29986         this.hide();
29987         this.on("paneladded", this.validateVisibility, this);
29988         this.on("panelremoved", this.validateVisibility, this);
29989     }
29990     this.applyConfig(config);
29991 };
29992
29993 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29994
29995     createBody : function(){
29996         /** This region's body element 
29997         * @type Roo.Element */
29998         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29999     },
30000
30001     applyConfig : function(c){
30002         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30003             var dh = Roo.DomHelper;
30004             if(c.titlebar !== false){
30005                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30006                 this.collapseBtn.on("click", this.collapse, this);
30007                 this.collapseBtn.enableDisplayMode();
30008
30009                 if(c.showPin === true || this.showPin){
30010                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30011                     this.stickBtn.enableDisplayMode();
30012                     this.stickBtn.on("click", this.expand, this);
30013                     this.stickBtn.hide();
30014                 }
30015             }
30016             /** This region's collapsed element
30017             * @type Roo.Element */
30018             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30019                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30020             ]}, true);
30021             if(c.floatable !== false){
30022                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30023                this.collapsedEl.on("click", this.collapseClick, this);
30024             }
30025
30026             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30027                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30028                    id: "message", unselectable: "on", style:{"float":"left"}});
30029                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30030              }
30031             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30032             this.expandBtn.on("click", this.expand, this);
30033         }
30034         if(this.collapseBtn){
30035             this.collapseBtn.setVisible(c.collapsible == true);
30036         }
30037         this.cmargins = c.cmargins || this.cmargins ||
30038                          (this.position == "west" || this.position == "east" ?
30039                              {top: 0, left: 2, right:2, bottom: 0} :
30040                              {top: 2, left: 0, right:0, bottom: 2});
30041         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30042         this.bottomTabs = c.tabPosition != "top";
30043         this.autoScroll = c.autoScroll || false;
30044         if(this.autoScroll){
30045             this.bodyEl.setStyle("overflow", "auto");
30046         }else{
30047             this.bodyEl.setStyle("overflow", "hidden");
30048         }
30049         //if(c.titlebar !== false){
30050             if((!c.titlebar && !c.title) || c.titlebar === false){
30051                 this.titleEl.hide();
30052             }else{
30053                 this.titleEl.show();
30054                 if(c.title){
30055                     this.titleTextEl.innerHTML = c.title;
30056                 }
30057             }
30058         //}
30059         this.duration = c.duration || .30;
30060         this.slideDuration = c.slideDuration || .45;
30061         this.config = c;
30062         if(c.collapsed){
30063             this.collapse(true);
30064         }
30065         if(c.hidden){
30066             this.hide();
30067         }
30068     },
30069     /**
30070      * Returns true if this region is currently visible.
30071      * @return {Boolean}
30072      */
30073     isVisible : function(){
30074         return this.visible;
30075     },
30076
30077     /**
30078      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30079      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30080      */
30081     setCollapsedTitle : function(title){
30082         title = title || "&#160;";
30083         if(this.collapsedTitleTextEl){
30084             this.collapsedTitleTextEl.innerHTML = title;
30085         }
30086     },
30087
30088     getBox : function(){
30089         var b;
30090         if(!this.collapsed){
30091             b = this.el.getBox(false, true);
30092         }else{
30093             b = this.collapsedEl.getBox(false, true);
30094         }
30095         return b;
30096     },
30097
30098     getMargins : function(){
30099         return this.collapsed ? this.cmargins : this.margins;
30100     },
30101
30102     highlight : function(){
30103         this.el.addClass("x-layout-panel-dragover");
30104     },
30105
30106     unhighlight : function(){
30107         this.el.removeClass("x-layout-panel-dragover");
30108     },
30109
30110     updateBox : function(box){
30111         this.box = box;
30112         if(!this.collapsed){
30113             this.el.dom.style.left = box.x + "px";
30114             this.el.dom.style.top = box.y + "px";
30115             this.updateBody(box.width, box.height);
30116         }else{
30117             this.collapsedEl.dom.style.left = box.x + "px";
30118             this.collapsedEl.dom.style.top = box.y + "px";
30119             this.collapsedEl.setSize(box.width, box.height);
30120         }
30121         if(this.tabs){
30122             this.tabs.autoSizeTabs();
30123         }
30124     },
30125
30126     updateBody : function(w, h){
30127         if(w !== null){
30128             this.el.setWidth(w);
30129             w -= this.el.getBorderWidth("rl");
30130             if(this.config.adjustments){
30131                 w += this.config.adjustments[0];
30132             }
30133         }
30134         if(h !== null){
30135             this.el.setHeight(h);
30136             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30137             h -= this.el.getBorderWidth("tb");
30138             if(this.config.adjustments){
30139                 h += this.config.adjustments[1];
30140             }
30141             this.bodyEl.setHeight(h);
30142             if(this.tabs){
30143                 h = this.tabs.syncHeight(h);
30144             }
30145         }
30146         if(this.panelSize){
30147             w = w !== null ? w : this.panelSize.width;
30148             h = h !== null ? h : this.panelSize.height;
30149         }
30150         if(this.activePanel){
30151             var el = this.activePanel.getEl();
30152             w = w !== null ? w : el.getWidth();
30153             h = h !== null ? h : el.getHeight();
30154             this.panelSize = {width: w, height: h};
30155             this.activePanel.setSize(w, h);
30156         }
30157         if(Roo.isIE && this.tabs){
30158             this.tabs.el.repaint();
30159         }
30160     },
30161
30162     /**
30163      * Returns the container element for this region.
30164      * @return {Roo.Element}
30165      */
30166     getEl : function(){
30167         return this.el;
30168     },
30169
30170     /**
30171      * Hides this region.
30172      */
30173     hide : function(){
30174         if(!this.collapsed){
30175             this.el.dom.style.left = "-2000px";
30176             this.el.hide();
30177         }else{
30178             this.collapsedEl.dom.style.left = "-2000px";
30179             this.collapsedEl.hide();
30180         }
30181         this.visible = false;
30182         this.fireEvent("visibilitychange", this, false);
30183     },
30184
30185     /**
30186      * Shows this region if it was previously hidden.
30187      */
30188     show : function(){
30189         if(!this.collapsed){
30190             this.el.show();
30191         }else{
30192             this.collapsedEl.show();
30193         }
30194         this.visible = true;
30195         this.fireEvent("visibilitychange", this, true);
30196     },
30197
30198     closeClicked : function(){
30199         if(this.activePanel){
30200             this.remove(this.activePanel);
30201         }
30202     },
30203
30204     collapseClick : function(e){
30205         if(this.isSlid){
30206            e.stopPropagation();
30207            this.slideIn();
30208         }else{
30209            e.stopPropagation();
30210            this.slideOut();
30211         }
30212     },
30213
30214     /**
30215      * Collapses this region.
30216      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30217      */
30218     collapse : function(skipAnim){
30219         if(this.collapsed) return;
30220         this.collapsed = true;
30221         if(this.split){
30222             this.split.el.hide();
30223         }
30224         if(this.config.animate && skipAnim !== true){
30225             this.fireEvent("invalidated", this);
30226             this.animateCollapse();
30227         }else{
30228             this.el.setLocation(-20000,-20000);
30229             this.el.hide();
30230             this.collapsedEl.show();
30231             this.fireEvent("collapsed", this);
30232             this.fireEvent("invalidated", this);
30233         }
30234     },
30235
30236     animateCollapse : function(){
30237         // overridden
30238     },
30239
30240     /**
30241      * Expands this region if it was previously collapsed.
30242      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30243      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30244      */
30245     expand : function(e, skipAnim){
30246         if(e) e.stopPropagation();
30247         if(!this.collapsed || this.el.hasActiveFx()) return;
30248         if(this.isSlid){
30249             this.afterSlideIn();
30250             skipAnim = true;
30251         }
30252         this.collapsed = false;
30253         if(this.config.animate && skipAnim !== true){
30254             this.animateExpand();
30255         }else{
30256             this.el.show();
30257             if(this.split){
30258                 this.split.el.show();
30259             }
30260             this.collapsedEl.setLocation(-2000,-2000);
30261             this.collapsedEl.hide();
30262             this.fireEvent("invalidated", this);
30263             this.fireEvent("expanded", this);
30264         }
30265     },
30266
30267     animateExpand : function(){
30268         // overridden
30269     },
30270
30271     initTabs : function(){
30272         this.bodyEl.setStyle("overflow", "hidden");
30273         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30274             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30275             disableTooltips: this.config.disableTabTips
30276         });
30277         if(this.config.hideTabs){
30278             ts.stripWrap.setDisplayed(false);
30279         }
30280         this.tabs = ts;
30281         ts.resizeTabs = this.config.resizeTabs === true;
30282         ts.minTabWidth = this.config.minTabWidth || 40;
30283         ts.maxTabWidth = this.config.maxTabWidth || 250;
30284         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30285         ts.monitorResize = false;
30286         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30287         ts.bodyEl.addClass('x-layout-tabs-body');
30288         this.panels.each(this.initPanelAsTab, this);
30289     },
30290
30291     initPanelAsTab : function(panel){
30292         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30293                     this.config.closeOnTab && panel.isClosable());
30294         if(panel.tabTip !== undefined){
30295             ti.setTooltip(panel.tabTip);
30296         }
30297         ti.on("activate", function(){
30298               this.setActivePanel(panel);
30299         }, this);
30300         if(this.config.closeOnTab){
30301             ti.on("beforeclose", function(t, e){
30302                 e.cancel = true;
30303                 this.remove(panel);
30304             }, this);
30305         }
30306         return ti;
30307     },
30308
30309     updatePanelTitle : function(panel, title){
30310         if(this.activePanel == panel){
30311             this.updateTitle(title);
30312         }
30313         if(this.tabs){
30314             var ti = this.tabs.getTab(panel.getEl().id);
30315             ti.setText(title);
30316             if(panel.tabTip !== undefined){
30317                 ti.setTooltip(panel.tabTip);
30318             }
30319         }
30320     },
30321
30322     updateTitle : function(title){
30323         if(this.titleTextEl && !this.config.title){
30324             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30325         }
30326     },
30327
30328     setActivePanel : function(panel){
30329         panel = this.getPanel(panel);
30330         if(this.activePanel && this.activePanel != panel){
30331             this.activePanel.setActiveState(false);
30332         }
30333         this.activePanel = panel;
30334         panel.setActiveState(true);
30335         if(this.panelSize){
30336             panel.setSize(this.panelSize.width, this.panelSize.height);
30337         }
30338         if(this.closeBtn){
30339             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30340         }
30341         this.updateTitle(panel.getTitle());
30342         if(this.tabs){
30343             this.fireEvent("invalidated", this);
30344         }
30345         this.fireEvent("panelactivated", this, panel);
30346     },
30347
30348     /**
30349      * Shows the specified panel.
30350      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30351      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30352      */
30353     showPanel : function(panel){
30354         if(panel = this.getPanel(panel)){
30355             if(this.tabs){
30356                 var tab = this.tabs.getTab(panel.getEl().id);
30357                 if(tab.isHidden()){
30358                     this.tabs.unhideTab(tab.id);
30359                 }
30360                 tab.activate();
30361             }else{
30362                 this.setActivePanel(panel);
30363             }
30364         }
30365         return panel;
30366     },
30367
30368     /**
30369      * Get the active panel for this region.
30370      * @return {Roo.ContentPanel} The active panel or null
30371      */
30372     getActivePanel : function(){
30373         return this.activePanel;
30374     },
30375
30376     validateVisibility : function(){
30377         if(this.panels.getCount() < 1){
30378             this.updateTitle("&#160;");
30379             this.closeBtn.hide();
30380             this.hide();
30381         }else{
30382             if(!this.isVisible()){
30383                 this.show();
30384             }
30385         }
30386     },
30387
30388     /**
30389      * Adds the passed ContentPanel(s) to this region.
30390      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30391      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30392      */
30393     add : function(panel){
30394         if(arguments.length > 1){
30395             for(var i = 0, len = arguments.length; i < len; i++) {
30396                 this.add(arguments[i]);
30397             }
30398             return null;
30399         }
30400         if(this.hasPanel(panel)){
30401             this.showPanel(panel);
30402             return panel;
30403         }
30404         panel.setRegion(this);
30405         this.panels.add(panel);
30406         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30407             this.bodyEl.dom.appendChild(panel.getEl().dom);
30408             if(panel.background !== true){
30409                 this.setActivePanel(panel);
30410             }
30411             this.fireEvent("paneladded", this, panel);
30412             return panel;
30413         }
30414         if(!this.tabs){
30415             this.initTabs();
30416         }else{
30417             this.initPanelAsTab(panel);
30418         }
30419         if(panel.background !== true){
30420             this.tabs.activate(panel.getEl().id);
30421         }
30422         this.fireEvent("paneladded", this, panel);
30423         return panel;
30424     },
30425
30426     /**
30427      * Hides the tab for the specified panel.
30428      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30429      */
30430     hidePanel : function(panel){
30431         if(this.tabs && (panel = this.getPanel(panel))){
30432             this.tabs.hideTab(panel.getEl().id);
30433         }
30434     },
30435
30436     /**
30437      * Unhides the tab for a previously hidden panel.
30438      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30439      */
30440     unhidePanel : function(panel){
30441         if(this.tabs && (panel = this.getPanel(panel))){
30442             this.tabs.unhideTab(panel.getEl().id);
30443         }
30444     },
30445
30446     clearPanels : function(){
30447         while(this.panels.getCount() > 0){
30448              this.remove(this.panels.first());
30449         }
30450     },
30451
30452     /**
30453      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30454      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30455      * @param {Boolean} preservePanel Overrides the config preservePanel option
30456      * @return {Roo.ContentPanel} The panel that was removed
30457      */
30458     remove : function(panel, preservePanel){
30459         panel = this.getPanel(panel);
30460         if(!panel){
30461             return null;
30462         }
30463         var e = {};
30464         this.fireEvent("beforeremove", this, panel, e);
30465         if(e.cancel === true){
30466             return null;
30467         }
30468         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30469         var panelId = panel.getId();
30470         this.panels.removeKey(panelId);
30471         if(preservePanel){
30472             document.body.appendChild(panel.getEl().dom);
30473         }
30474         if(this.tabs){
30475             this.tabs.removeTab(panel.getEl().id);
30476         }else if (!preservePanel){
30477             this.bodyEl.dom.removeChild(panel.getEl().dom);
30478         }
30479         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30480             var p = this.panels.first();
30481             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30482             tempEl.appendChild(p.getEl().dom);
30483             this.bodyEl.update("");
30484             this.bodyEl.dom.appendChild(p.getEl().dom);
30485             tempEl = null;
30486             this.updateTitle(p.getTitle());
30487             this.tabs = null;
30488             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30489             this.setActivePanel(p);
30490         }
30491         panel.setRegion(null);
30492         if(this.activePanel == panel){
30493             this.activePanel = null;
30494         }
30495         if(this.config.autoDestroy !== false && preservePanel !== true){
30496             try{panel.destroy();}catch(e){}
30497         }
30498         this.fireEvent("panelremoved", this, panel);
30499         return panel;
30500     },
30501
30502     /**
30503      * Returns the TabPanel component used by this region
30504      * @return {Roo.TabPanel}
30505      */
30506     getTabs : function(){
30507         return this.tabs;
30508     },
30509
30510     createTool : function(parentEl, className){
30511         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30512             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30513         btn.addClassOnOver("x-layout-tools-button-over");
30514         return btn;
30515     }
30516 });/*
30517  * Based on:
30518  * Ext JS Library 1.1.1
30519  * Copyright(c) 2006-2007, Ext JS, LLC.
30520  *
30521  * Originally Released Under LGPL - original licence link has changed is not relivant.
30522  *
30523  * Fork - LGPL
30524  * <script type="text/javascript">
30525  */
30526  
30527
30528
30529 /**
30530  * @class Roo.SplitLayoutRegion
30531  * @extends Roo.LayoutRegion
30532  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30533  */
30534 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30535     this.cursor = cursor;
30536     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30537 };
30538
30539 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30540     splitTip : "Drag to resize.",
30541     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30542     useSplitTips : false,
30543
30544     applyConfig : function(config){
30545         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30546         if(config.split){
30547             if(!this.split){
30548                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30549                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30550                 /** The SplitBar for this region 
30551                 * @type Roo.SplitBar */
30552                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30553                 this.split.on("moved", this.onSplitMove, this);
30554                 this.split.useShim = config.useShim === true;
30555                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30556                 if(this.useSplitTips){
30557                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30558                 }
30559                 if(config.collapsible){
30560                     this.split.el.on("dblclick", this.collapse,  this);
30561                 }
30562             }
30563             if(typeof config.minSize != "undefined"){
30564                 this.split.minSize = config.minSize;
30565             }
30566             if(typeof config.maxSize != "undefined"){
30567                 this.split.maxSize = config.maxSize;
30568             }
30569             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30570                 this.hideSplitter();
30571             }
30572         }
30573     },
30574
30575     getHMaxSize : function(){
30576          var cmax = this.config.maxSize || 10000;
30577          var center = this.mgr.getRegion("center");
30578          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30579     },
30580
30581     getVMaxSize : function(){
30582          var cmax = this.config.maxSize || 10000;
30583          var center = this.mgr.getRegion("center");
30584          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30585     },
30586
30587     onSplitMove : function(split, newSize){
30588         this.fireEvent("resized", this, newSize);
30589     },
30590     
30591     /** 
30592      * Returns the {@link Roo.SplitBar} for this region.
30593      * @return {Roo.SplitBar}
30594      */
30595     getSplitBar : function(){
30596         return this.split;
30597     },
30598     
30599     hide : function(){
30600         this.hideSplitter();
30601         Roo.SplitLayoutRegion.superclass.hide.call(this);
30602     },
30603
30604     hideSplitter : function(){
30605         if(this.split){
30606             this.split.el.setLocation(-2000,-2000);
30607             this.split.el.hide();
30608         }
30609     },
30610
30611     show : function(){
30612         if(this.split){
30613             this.split.el.show();
30614         }
30615         Roo.SplitLayoutRegion.superclass.show.call(this);
30616     },
30617     
30618     beforeSlide: function(){
30619         if(Roo.isGecko){// firefox overflow auto bug workaround
30620             this.bodyEl.clip();
30621             if(this.tabs) this.tabs.bodyEl.clip();
30622             if(this.activePanel){
30623                 this.activePanel.getEl().clip();
30624                 
30625                 if(this.activePanel.beforeSlide){
30626                     this.activePanel.beforeSlide();
30627                 }
30628             }
30629         }
30630     },
30631     
30632     afterSlide : function(){
30633         if(Roo.isGecko){// firefox overflow auto bug workaround
30634             this.bodyEl.unclip();
30635             if(this.tabs) this.tabs.bodyEl.unclip();
30636             if(this.activePanel){
30637                 this.activePanel.getEl().unclip();
30638                 if(this.activePanel.afterSlide){
30639                     this.activePanel.afterSlide();
30640                 }
30641             }
30642         }
30643     },
30644
30645     initAutoHide : function(){
30646         if(this.autoHide !== false){
30647             if(!this.autoHideHd){
30648                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30649                 this.autoHideHd = {
30650                     "mouseout": function(e){
30651                         if(!e.within(this.el, true)){
30652                             st.delay(500);
30653                         }
30654                     },
30655                     "mouseover" : function(e){
30656                         st.cancel();
30657                     },
30658                     scope : this
30659                 };
30660             }
30661             this.el.on(this.autoHideHd);
30662         }
30663     },
30664
30665     clearAutoHide : function(){
30666         if(this.autoHide !== false){
30667             this.el.un("mouseout", this.autoHideHd.mouseout);
30668             this.el.un("mouseover", this.autoHideHd.mouseover);
30669         }
30670     },
30671
30672     clearMonitor : function(){
30673         Roo.get(document).un("click", this.slideInIf, this);
30674     },
30675
30676     // these names are backwards but not changed for compat
30677     slideOut : function(){
30678         if(this.isSlid || this.el.hasActiveFx()){
30679             return;
30680         }
30681         this.isSlid = true;
30682         if(this.collapseBtn){
30683             this.collapseBtn.hide();
30684         }
30685         this.closeBtnState = this.closeBtn.getStyle('display');
30686         this.closeBtn.hide();
30687         if(this.stickBtn){
30688             this.stickBtn.show();
30689         }
30690         this.el.show();
30691         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30692         this.beforeSlide();
30693         this.el.setStyle("z-index", 10001);
30694         this.el.slideIn(this.getSlideAnchor(), {
30695             callback: function(){
30696                 this.afterSlide();
30697                 this.initAutoHide();
30698                 Roo.get(document).on("click", this.slideInIf, this);
30699                 this.fireEvent("slideshow", this);
30700             },
30701             scope: this,
30702             block: true
30703         });
30704     },
30705
30706     afterSlideIn : function(){
30707         this.clearAutoHide();
30708         this.isSlid = false;
30709         this.clearMonitor();
30710         this.el.setStyle("z-index", "");
30711         if(this.collapseBtn){
30712             this.collapseBtn.show();
30713         }
30714         this.closeBtn.setStyle('display', this.closeBtnState);
30715         if(this.stickBtn){
30716             this.stickBtn.hide();
30717         }
30718         this.fireEvent("slidehide", this);
30719     },
30720
30721     slideIn : function(cb){
30722         if(!this.isSlid || this.el.hasActiveFx()){
30723             Roo.callback(cb);
30724             return;
30725         }
30726         this.isSlid = false;
30727         this.beforeSlide();
30728         this.el.slideOut(this.getSlideAnchor(), {
30729             callback: function(){
30730                 this.el.setLeftTop(-10000, -10000);
30731                 this.afterSlide();
30732                 this.afterSlideIn();
30733                 Roo.callback(cb);
30734             },
30735             scope: this,
30736             block: true
30737         });
30738     },
30739     
30740     slideInIf : function(e){
30741         if(!e.within(this.el)){
30742             this.slideIn();
30743         }
30744     },
30745
30746     animateCollapse : function(){
30747         this.beforeSlide();
30748         this.el.setStyle("z-index", 20000);
30749         var anchor = this.getSlideAnchor();
30750         this.el.slideOut(anchor, {
30751             callback : function(){
30752                 this.el.setStyle("z-index", "");
30753                 this.collapsedEl.slideIn(anchor, {duration:.3});
30754                 this.afterSlide();
30755                 this.el.setLocation(-10000,-10000);
30756                 this.el.hide();
30757                 this.fireEvent("collapsed", this);
30758             },
30759             scope: this,
30760             block: true
30761         });
30762     },
30763
30764     animateExpand : function(){
30765         this.beforeSlide();
30766         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30767         this.el.setStyle("z-index", 20000);
30768         this.collapsedEl.hide({
30769             duration:.1
30770         });
30771         this.el.slideIn(this.getSlideAnchor(), {
30772             callback : function(){
30773                 this.el.setStyle("z-index", "");
30774                 this.afterSlide();
30775                 if(this.split){
30776                     this.split.el.show();
30777                 }
30778                 this.fireEvent("invalidated", this);
30779                 this.fireEvent("expanded", this);
30780             },
30781             scope: this,
30782             block: true
30783         });
30784     },
30785
30786     anchors : {
30787         "west" : "left",
30788         "east" : "right",
30789         "north" : "top",
30790         "south" : "bottom"
30791     },
30792
30793     sanchors : {
30794         "west" : "l",
30795         "east" : "r",
30796         "north" : "t",
30797         "south" : "b"
30798     },
30799
30800     canchors : {
30801         "west" : "tl-tr",
30802         "east" : "tr-tl",
30803         "north" : "tl-bl",
30804         "south" : "bl-tl"
30805     },
30806
30807     getAnchor : function(){
30808         return this.anchors[this.position];
30809     },
30810
30811     getCollapseAnchor : function(){
30812         return this.canchors[this.position];
30813     },
30814
30815     getSlideAnchor : function(){
30816         return this.sanchors[this.position];
30817     },
30818
30819     getAlignAdj : function(){
30820         var cm = this.cmargins;
30821         switch(this.position){
30822             case "west":
30823                 return [0, 0];
30824             break;
30825             case "east":
30826                 return [0, 0];
30827             break;
30828             case "north":
30829                 return [0, 0];
30830             break;
30831             case "south":
30832                 return [0, 0];
30833             break;
30834         }
30835     },
30836
30837     getExpandAdj : function(){
30838         var c = this.collapsedEl, cm = this.cmargins;
30839         switch(this.position){
30840             case "west":
30841                 return [-(cm.right+c.getWidth()+cm.left), 0];
30842             break;
30843             case "east":
30844                 return [cm.right+c.getWidth()+cm.left, 0];
30845             break;
30846             case "north":
30847                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30848             break;
30849             case "south":
30850                 return [0, cm.top+cm.bottom+c.getHeight()];
30851             break;
30852         }
30853     }
30854 });/*
30855  * Based on:
30856  * Ext JS Library 1.1.1
30857  * Copyright(c) 2006-2007, Ext JS, LLC.
30858  *
30859  * Originally Released Under LGPL - original licence link has changed is not relivant.
30860  *
30861  * Fork - LGPL
30862  * <script type="text/javascript">
30863  */
30864 /*
30865  * These classes are private internal classes
30866  */
30867 Roo.CenterLayoutRegion = function(mgr, config){
30868     Roo.LayoutRegion.call(this, mgr, config, "center");
30869     this.visible = true;
30870     this.minWidth = config.minWidth || 20;
30871     this.minHeight = config.minHeight || 20;
30872 };
30873
30874 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30875     hide : function(){
30876         // center panel can't be hidden
30877     },
30878     
30879     show : function(){
30880         // center panel can't be hidden
30881     },
30882     
30883     getMinWidth: function(){
30884         return this.minWidth;
30885     },
30886     
30887     getMinHeight: function(){
30888         return this.minHeight;
30889     }
30890 });
30891
30892
30893 Roo.NorthLayoutRegion = function(mgr, config){
30894     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30895     if(this.split){
30896         this.split.placement = Roo.SplitBar.TOP;
30897         this.split.orientation = Roo.SplitBar.VERTICAL;
30898         this.split.el.addClass("x-layout-split-v");
30899     }
30900     var size = config.initialSize || config.height;
30901     if(typeof size != "undefined"){
30902         this.el.setHeight(size);
30903     }
30904 };
30905 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30906     orientation: Roo.SplitBar.VERTICAL,
30907     getBox : function(){
30908         if(this.collapsed){
30909             return this.collapsedEl.getBox();
30910         }
30911         var box = this.el.getBox();
30912         if(this.split){
30913             box.height += this.split.el.getHeight();
30914         }
30915         return box;
30916     },
30917     
30918     updateBox : function(box){
30919         if(this.split && !this.collapsed){
30920             box.height -= this.split.el.getHeight();
30921             this.split.el.setLeft(box.x);
30922             this.split.el.setTop(box.y+box.height);
30923             this.split.el.setWidth(box.width);
30924         }
30925         if(this.collapsed){
30926             this.updateBody(box.width, null);
30927         }
30928         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30929     }
30930 });
30931
30932 Roo.SouthLayoutRegion = function(mgr, config){
30933     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30934     if(this.split){
30935         this.split.placement = Roo.SplitBar.BOTTOM;
30936         this.split.orientation = Roo.SplitBar.VERTICAL;
30937         this.split.el.addClass("x-layout-split-v");
30938     }
30939     var size = config.initialSize || config.height;
30940     if(typeof size != "undefined"){
30941         this.el.setHeight(size);
30942     }
30943 };
30944 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30945     orientation: Roo.SplitBar.VERTICAL,
30946     getBox : function(){
30947         if(this.collapsed){
30948             return this.collapsedEl.getBox();
30949         }
30950         var box = this.el.getBox();
30951         if(this.split){
30952             var sh = this.split.el.getHeight();
30953             box.height += sh;
30954             box.y -= sh;
30955         }
30956         return box;
30957     },
30958     
30959     updateBox : function(box){
30960         if(this.split && !this.collapsed){
30961             var sh = this.split.el.getHeight();
30962             box.height -= sh;
30963             box.y += sh;
30964             this.split.el.setLeft(box.x);
30965             this.split.el.setTop(box.y-sh);
30966             this.split.el.setWidth(box.width);
30967         }
30968         if(this.collapsed){
30969             this.updateBody(box.width, null);
30970         }
30971         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30972     }
30973 });
30974
30975 Roo.EastLayoutRegion = function(mgr, config){
30976     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30977     if(this.split){
30978         this.split.placement = Roo.SplitBar.RIGHT;
30979         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30980         this.split.el.addClass("x-layout-split-h");
30981     }
30982     var size = config.initialSize || config.width;
30983     if(typeof size != "undefined"){
30984         this.el.setWidth(size);
30985     }
30986 };
30987 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30988     orientation: Roo.SplitBar.HORIZONTAL,
30989     getBox : function(){
30990         if(this.collapsed){
30991             return this.collapsedEl.getBox();
30992         }
30993         var box = this.el.getBox();
30994         if(this.split){
30995             var sw = this.split.el.getWidth();
30996             box.width += sw;
30997             box.x -= sw;
30998         }
30999         return box;
31000     },
31001
31002     updateBox : function(box){
31003         if(this.split && !this.collapsed){
31004             var sw = this.split.el.getWidth();
31005             box.width -= sw;
31006             this.split.el.setLeft(box.x);
31007             this.split.el.setTop(box.y);
31008             this.split.el.setHeight(box.height);
31009             box.x += sw;
31010         }
31011         if(this.collapsed){
31012             this.updateBody(null, box.height);
31013         }
31014         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31015     }
31016 });
31017
31018 Roo.WestLayoutRegion = function(mgr, config){
31019     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31020     if(this.split){
31021         this.split.placement = Roo.SplitBar.LEFT;
31022         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31023         this.split.el.addClass("x-layout-split-h");
31024     }
31025     var size = config.initialSize || config.width;
31026     if(typeof size != "undefined"){
31027         this.el.setWidth(size);
31028     }
31029 };
31030 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31031     orientation: Roo.SplitBar.HORIZONTAL,
31032     getBox : function(){
31033         if(this.collapsed){
31034             return this.collapsedEl.getBox();
31035         }
31036         var box = this.el.getBox();
31037         if(this.split){
31038             box.width += this.split.el.getWidth();
31039         }
31040         return box;
31041     },
31042     
31043     updateBox : function(box){
31044         if(this.split && !this.collapsed){
31045             var sw = this.split.el.getWidth();
31046             box.width -= sw;
31047             this.split.el.setLeft(box.x+box.width);
31048             this.split.el.setTop(box.y);
31049             this.split.el.setHeight(box.height);
31050         }
31051         if(this.collapsed){
31052             this.updateBody(null, box.height);
31053         }
31054         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31055     }
31056 });
31057 /*
31058  * Based on:
31059  * Ext JS Library 1.1.1
31060  * Copyright(c) 2006-2007, Ext JS, LLC.
31061  *
31062  * Originally Released Under LGPL - original licence link has changed is not relivant.
31063  *
31064  * Fork - LGPL
31065  * <script type="text/javascript">
31066  */
31067  
31068  
31069 /*
31070  * Private internal class for reading and applying state
31071  */
31072 Roo.LayoutStateManager = function(layout){
31073      // default empty state
31074      this.state = {
31075         north: {},
31076         south: {},
31077         east: {},
31078         west: {}       
31079     };
31080 };
31081
31082 Roo.LayoutStateManager.prototype = {
31083     init : function(layout, provider){
31084         this.provider = provider;
31085         var state = provider.get(layout.id+"-layout-state");
31086         if(state){
31087             var wasUpdating = layout.isUpdating();
31088             if(!wasUpdating){
31089                 layout.beginUpdate();
31090             }
31091             for(var key in state){
31092                 if(typeof state[key] != "function"){
31093                     var rstate = state[key];
31094                     var r = layout.getRegion(key);
31095                     if(r && rstate){
31096                         if(rstate.size){
31097                             r.resizeTo(rstate.size);
31098                         }
31099                         if(rstate.collapsed == true){
31100                             r.collapse(true);
31101                         }else{
31102                             r.expand(null, true);
31103                         }
31104                     }
31105                 }
31106             }
31107             if(!wasUpdating){
31108                 layout.endUpdate();
31109             }
31110             this.state = state; 
31111         }
31112         this.layout = layout;
31113         layout.on("regionresized", this.onRegionResized, this);
31114         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31115         layout.on("regionexpanded", this.onRegionExpanded, this);
31116     },
31117     
31118     storeState : function(){
31119         this.provider.set(this.layout.id+"-layout-state", this.state);
31120     },
31121     
31122     onRegionResized : function(region, newSize){
31123         this.state[region.getPosition()].size = newSize;
31124         this.storeState();
31125     },
31126     
31127     onRegionCollapsed : function(region){
31128         this.state[region.getPosition()].collapsed = true;
31129         this.storeState();
31130     },
31131     
31132     onRegionExpanded : function(region){
31133         this.state[region.getPosition()].collapsed = false;
31134         this.storeState();
31135     }
31136 };/*
31137  * Based on:
31138  * Ext JS Library 1.1.1
31139  * Copyright(c) 2006-2007, Ext JS, LLC.
31140  *
31141  * Originally Released Under LGPL - original licence link has changed is not relivant.
31142  *
31143  * Fork - LGPL
31144  * <script type="text/javascript">
31145  */
31146 /**
31147  * @class Roo.ContentPanel
31148  * @extends Roo.util.Observable
31149  * A basic ContentPanel element.
31150  * @cfg {Boolean} fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31151  * @cfg {Boolean} fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31152  * @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
31153  * @cfg {Boolean} closable      True if the panel can be closed/removed
31154  * @cfg {Boolean} background    True if the panel should not be activated when it is added (defaults to false)
31155  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31156  * @cfg {Toolbar} toolbar       A toolbar for this panel
31157  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31158  * @cfg {String} title          The title for this panel
31159  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31160  * @cfg {String} url            Calls {@link #setUrl} with this value
31161  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31162  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31163  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31164  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
31165
31166  * @constructor
31167  * Create a new ContentPanel.
31168  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31169  * @param {String/Object} config A string to set only the title or a config object
31170  * @param {String} content (optional) Set the HTML content for this panel
31171  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31172  */
31173 Roo.ContentPanel = function(el, config, content){
31174     
31175      
31176     /*
31177     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31178         config = el;
31179         el = Roo.id();
31180     }
31181     if (config && config.parentLayout) { 
31182         el = config.parentLayout.el.createChild(); 
31183     }
31184     */
31185     if(el.autoCreate){ // xtype is available if this is called from factory
31186         config = el;
31187         el = Roo.id();
31188     }
31189     this.el = Roo.get(el);
31190     if(!this.el && config && config.autoCreate){
31191         if(typeof config.autoCreate == "object"){
31192             if(!config.autoCreate.id){
31193                 config.autoCreate.id = config.id||el;
31194             }
31195             this.el = Roo.DomHelper.append(document.body,
31196                         config.autoCreate, true);
31197         }else{
31198             this.el = Roo.DomHelper.append(document.body,
31199                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31200         }
31201     }
31202     this.closable = false;
31203     this.loaded = false;
31204     this.active = false;
31205     if(typeof config == "string"){
31206         this.title = config;
31207     }else{
31208         Roo.apply(this, config);
31209     }
31210     
31211     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31212         this.wrapEl = this.el.wrap();    
31213         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
31214         
31215     }
31216     
31217     
31218     
31219     if(this.resizeEl){
31220         this.resizeEl = Roo.get(this.resizeEl, true);
31221     }else{
31222         this.resizeEl = this.el;
31223     }
31224     this.addEvents({
31225         /**
31226          * @event activate
31227          * Fires when this panel is activated. 
31228          * @param {Roo.ContentPanel} this
31229          */
31230         "activate" : true,
31231         /**
31232          * @event deactivate
31233          * Fires when this panel is activated. 
31234          * @param {Roo.ContentPanel} this
31235          */
31236         "deactivate" : true,
31237
31238         /**
31239          * @event resize
31240          * Fires when this panel is resized if fitToFrame is true.
31241          * @param {Roo.ContentPanel} this
31242          * @param {Number} width The width after any component adjustments
31243          * @param {Number} height The height after any component adjustments
31244          */
31245         "resize" : true
31246     });
31247     if(this.autoScroll){
31248         this.resizeEl.setStyle("overflow", "auto");
31249     } else {
31250         // fix randome scrolling
31251         this.el.on('scroll', function() {
31252             Roo.log('fix random scolling');
31253             this.scrollTo('top',0); 
31254         });
31255     }
31256     content = content || this.content;
31257     if(content){
31258         this.setContent(content);
31259     }
31260     if(config && config.url){
31261         this.setUrl(this.url, this.params, this.loadOnce);
31262     }
31263     
31264     
31265     
31266     Roo.ContentPanel.superclass.constructor.call(this);
31267 };
31268
31269 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31270     tabTip:'',
31271     setRegion : function(region){
31272         this.region = region;
31273         if(region){
31274            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31275         }else{
31276            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31277         } 
31278     },
31279     
31280     /**
31281      * Returns the toolbar for this Panel if one was configured. 
31282      * @return {Roo.Toolbar} 
31283      */
31284     getToolbar : function(){
31285         return this.toolbar;
31286     },
31287     
31288     setActiveState : function(active){
31289         this.active = active;
31290         if(!active){
31291             this.fireEvent("deactivate", this);
31292         }else{
31293             this.fireEvent("activate", this);
31294         }
31295     },
31296     /**
31297      * Updates this panel's element
31298      * @param {String} content The new content
31299      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31300     */
31301     setContent : function(content, loadScripts){
31302         this.el.update(content, loadScripts);
31303     },
31304
31305     ignoreResize : function(w, h){
31306         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31307             return true;
31308         }else{
31309             this.lastSize = {width: w, height: h};
31310             return false;
31311         }
31312     },
31313     /**
31314      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31315      * @return {Roo.UpdateManager} The UpdateManager
31316      */
31317     getUpdateManager : function(){
31318         return this.el.getUpdateManager();
31319     },
31320      /**
31321      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31322      * @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:
31323 <pre><code>
31324 panel.load({
31325     url: "your-url.php",
31326     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31327     callback: yourFunction,
31328     scope: yourObject, //(optional scope)
31329     discardUrl: false,
31330     nocache: false,
31331     text: "Loading...",
31332     timeout: 30,
31333     scripts: false
31334 });
31335 </code></pre>
31336      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31337      * 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.
31338      * @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}
31339      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31340      * @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.
31341      * @return {Roo.ContentPanel} this
31342      */
31343     load : function(){
31344         var um = this.el.getUpdateManager();
31345         um.update.apply(um, arguments);
31346         return this;
31347     },
31348
31349
31350     /**
31351      * 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.
31352      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31353      * @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)
31354      * @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)
31355      * @return {Roo.UpdateManager} The UpdateManager
31356      */
31357     setUrl : function(url, params, loadOnce){
31358         if(this.refreshDelegate){
31359             this.removeListener("activate", this.refreshDelegate);
31360         }
31361         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31362         this.on("activate", this.refreshDelegate);
31363         return this.el.getUpdateManager();
31364     },
31365     
31366     _handleRefresh : function(url, params, loadOnce){
31367         if(!loadOnce || !this.loaded){
31368             var updater = this.el.getUpdateManager();
31369             updater.update(url, params, this._setLoaded.createDelegate(this));
31370         }
31371     },
31372     
31373     _setLoaded : function(){
31374         this.loaded = true;
31375     }, 
31376     
31377     /**
31378      * Returns this panel's id
31379      * @return {String} 
31380      */
31381     getId : function(){
31382         return this.el.id;
31383     },
31384     
31385     /** 
31386      * Returns this panel's element - used by regiosn to add.
31387      * @return {Roo.Element} 
31388      */
31389     getEl : function(){
31390         return this.wrapEl || this.el;
31391     },
31392     
31393     adjustForComponents : function(width, height){
31394         if(this.resizeEl != this.el){
31395             width -= this.el.getFrameWidth('lr');
31396             height -= this.el.getFrameWidth('tb');
31397         }
31398         if(this.toolbar){
31399             var te = this.toolbar.getEl();
31400             height -= te.getHeight();
31401             te.setWidth(width);
31402         }
31403         if(this.adjustments){
31404             width += this.adjustments[0];
31405             height += this.adjustments[1];
31406         }
31407         return {"width": width, "height": height};
31408     },
31409     
31410     setSize : function(width, height){
31411         if(this.fitToFrame && !this.ignoreResize(width, height)){
31412             if(this.fitContainer && this.resizeEl != this.el){
31413                 this.el.setSize(width, height);
31414             }
31415             var size = this.adjustForComponents(width, height);
31416             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31417             this.fireEvent('resize', this, size.width, size.height);
31418         }
31419     },
31420     
31421     /**
31422      * Returns this panel's title
31423      * @return {String} 
31424      */
31425     getTitle : function(){
31426         return this.title;
31427     },
31428     
31429     /**
31430      * Set this panel's title
31431      * @param {String} title
31432      */
31433     setTitle : function(title){
31434         this.title = title;
31435         if(this.region){
31436             this.region.updatePanelTitle(this, title);
31437         }
31438     },
31439     
31440     /**
31441      * Returns true is this panel was configured to be closable
31442      * @return {Boolean} 
31443      */
31444     isClosable : function(){
31445         return this.closable;
31446     },
31447     
31448     beforeSlide : function(){
31449         this.el.clip();
31450         this.resizeEl.clip();
31451     },
31452     
31453     afterSlide : function(){
31454         this.el.unclip();
31455         this.resizeEl.unclip();
31456     },
31457     
31458     /**
31459      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31460      *   Will fail silently if the {@link #setUrl} method has not been called.
31461      *   This does not activate the panel, just updates its content.
31462      */
31463     refresh : function(){
31464         if(this.refreshDelegate){
31465            this.loaded = false;
31466            this.refreshDelegate();
31467         }
31468     },
31469     
31470     /**
31471      * Destroys this panel
31472      */
31473     destroy : function(){
31474         this.el.removeAllListeners();
31475         var tempEl = document.createElement("span");
31476         tempEl.appendChild(this.el.dom);
31477         tempEl.innerHTML = "";
31478         this.el.remove();
31479         this.el = null;
31480     },
31481     
31482       /**
31483      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31484      * <pre><code>
31485
31486 layout.addxtype({
31487        xtype : 'Form',
31488        items: [ .... ]
31489    }
31490 );
31491
31492 </code></pre>
31493      * @param {Object} cfg Xtype definition of item to add.
31494      */
31495     
31496     addxtype : function(cfg) {
31497         // add form..
31498         if (cfg.xtype.match(/^Form$/)) {
31499             var el = this.el.createChild();
31500
31501             this.form = new  Roo.form.Form(cfg);
31502             
31503             
31504             if ( this.form.allItems.length) this.form.render(el.dom);
31505             return this.form;
31506         }
31507         
31508         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31509             // views..
31510             cfg.el = this.el.appendChild(document.createElement("div"));
31511             // factory?
31512             var ret = new Roo[cfg.xtype](cfg);
31513             ret.render(false, ''); // render blank..
31514             return ret;
31515             
31516         }
31517         return false;
31518         
31519     }
31520 });
31521
31522 /**
31523  * @class Roo.GridPanel
31524  * @extends Roo.ContentPanel
31525  * @constructor
31526  * Create a new GridPanel.
31527  * @param {Roo.grid.Grid} grid The grid for this panel
31528  * @param {String/Object} config A string to set only the panel's title, or a config object
31529  */
31530 Roo.GridPanel = function(grid, config){
31531     
31532   
31533     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31534         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31535         
31536     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31537     
31538     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31539     
31540     if(this.toolbar){
31541         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31542     }
31543     // xtype created footer. - not sure if will work as we normally have to render first..
31544     if (this.footer && !this.footer.el && this.footer.xtype) {
31545         
31546         this.footer.container = this.grid.getView().getFooterPanel(true);
31547         this.footer.dataSource = this.grid.dataSource;
31548         this.footer = Roo.factory(this.footer, Roo);
31549         
31550     }
31551     
31552     grid.monitorWindowResize = false; // turn off autosizing
31553     grid.autoHeight = false;
31554     grid.autoWidth = false;
31555     this.grid = grid;
31556     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31557 };
31558
31559 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31560     getId : function(){
31561         return this.grid.id;
31562     },
31563     
31564     /**
31565      * Returns the grid for this panel
31566      * @return {Roo.grid.Grid} 
31567      */
31568     getGrid : function(){
31569         return this.grid;    
31570     },
31571     
31572     setSize : function(width, height){
31573         if(!this.ignoreResize(width, height)){
31574             var grid = this.grid;
31575             var size = this.adjustForComponents(width, height);
31576             grid.getGridEl().setSize(size.width, size.height);
31577             grid.autoSize();
31578         }
31579     },
31580     
31581     beforeSlide : function(){
31582         this.grid.getView().scroller.clip();
31583     },
31584     
31585     afterSlide : function(){
31586         this.grid.getView().scroller.unclip();
31587     },
31588     
31589     destroy : function(){
31590         this.grid.destroy();
31591         delete this.grid;
31592         Roo.GridPanel.superclass.destroy.call(this); 
31593     }
31594 });
31595
31596
31597 /**
31598  * @class Roo.NestedLayoutPanel
31599  * @extends Roo.ContentPanel
31600  * @constructor
31601  * Create a new NestedLayoutPanel.
31602  * 
31603  * 
31604  * @param {Roo.BorderLayout} layout The layout for this panel
31605  * @param {String/Object} config A string to set only the title or a config object
31606  */
31607 Roo.NestedLayoutPanel = function(layout, config)
31608 {
31609     // construct with only one argument..
31610     /* FIXME - implement nicer consturctors
31611     if (layout.layout) {
31612         config = layout;
31613         layout = config.layout;
31614         delete config.layout;
31615     }
31616     if (layout.xtype && !layout.getEl) {
31617         // then layout needs constructing..
31618         layout = Roo.factory(layout, Roo);
31619     }
31620     */
31621     
31622     
31623     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31624     
31625     layout.monitorWindowResize = false; // turn off autosizing
31626     this.layout = layout;
31627     this.layout.getEl().addClass("x-layout-nested-layout");
31628     
31629     
31630     
31631     
31632 };
31633
31634 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31635
31636     setSize : function(width, height){
31637         if(!this.ignoreResize(width, height)){
31638             var size = this.adjustForComponents(width, height);
31639             var el = this.layout.getEl();
31640             el.setSize(size.width, size.height);
31641             var touch = el.dom.offsetWidth;
31642             this.layout.layout();
31643             // ie requires a double layout on the first pass
31644             if(Roo.isIE && !this.initialized){
31645                 this.initialized = true;
31646                 this.layout.layout();
31647             }
31648         }
31649     },
31650     
31651     // activate all subpanels if not currently active..
31652     
31653     setActiveState : function(active){
31654         this.active = active;
31655         if(!active){
31656             this.fireEvent("deactivate", this);
31657             return;
31658         }
31659         
31660         this.fireEvent("activate", this);
31661         // not sure if this should happen before or after..
31662         if (!this.layout) {
31663             return; // should not happen..
31664         }
31665         var reg = false;
31666         for (var r in this.layout.regions) {
31667             reg = this.layout.getRegion(r);
31668             if (reg.getActivePanel()) {
31669                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31670                 reg.setActivePanel(reg.getActivePanel());
31671                 continue;
31672             }
31673             if (!reg.panels.length) {
31674                 continue;
31675             }
31676             reg.showPanel(reg.getPanel(0));
31677         }
31678         
31679         
31680         
31681         
31682     },
31683     
31684     /**
31685      * Returns the nested BorderLayout for this panel
31686      * @return {Roo.BorderLayout} 
31687      */
31688     getLayout : function(){
31689         return this.layout;
31690     },
31691     
31692      /**
31693      * Adds a xtype elements to the layout of the nested panel
31694      * <pre><code>
31695
31696 panel.addxtype({
31697        xtype : 'ContentPanel',
31698        region: 'west',
31699        items: [ .... ]
31700    }
31701 );
31702
31703 panel.addxtype({
31704         xtype : 'NestedLayoutPanel',
31705         region: 'west',
31706         layout: {
31707            center: { },
31708            west: { }   
31709         },
31710         items : [ ... list of content panels or nested layout panels.. ]
31711    }
31712 );
31713 </code></pre>
31714      * @param {Object} cfg Xtype definition of item to add.
31715      */
31716     addxtype : function(cfg) {
31717         return this.layout.addxtype(cfg);
31718     
31719     }
31720 });
31721
31722 Roo.ScrollPanel = function(el, config, content){
31723     config = config || {};
31724     config.fitToFrame = true;
31725     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31726     
31727     this.el.dom.style.overflow = "hidden";
31728     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31729     this.el.removeClass("x-layout-inactive-content");
31730     this.el.on("mousewheel", this.onWheel, this);
31731
31732     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31733     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31734     up.unselectable(); down.unselectable();
31735     up.on("click", this.scrollUp, this);
31736     down.on("click", this.scrollDown, this);
31737     up.addClassOnOver("x-scroller-btn-over");
31738     down.addClassOnOver("x-scroller-btn-over");
31739     up.addClassOnClick("x-scroller-btn-click");
31740     down.addClassOnClick("x-scroller-btn-click");
31741     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31742
31743     this.resizeEl = this.el;
31744     this.el = wrap; this.up = up; this.down = down;
31745 };
31746
31747 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31748     increment : 100,
31749     wheelIncrement : 5,
31750     scrollUp : function(){
31751         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31752     },
31753
31754     scrollDown : function(){
31755         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31756     },
31757
31758     afterScroll : function(){
31759         var el = this.resizeEl;
31760         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31761         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31762         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31763     },
31764
31765     setSize : function(){
31766         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31767         this.afterScroll();
31768     },
31769
31770     onWheel : function(e){
31771         var d = e.getWheelDelta();
31772         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31773         this.afterScroll();
31774         e.stopEvent();
31775     },
31776
31777     setContent : function(content, loadScripts){
31778         this.resizeEl.update(content, loadScripts);
31779     }
31780
31781 });
31782
31783
31784
31785
31786
31787
31788
31789
31790
31791 /**
31792  * @class Roo.TreePanel
31793  * @extends Roo.ContentPanel
31794  * @constructor
31795  * Create a new TreePanel. - defaults to fit/scoll contents.
31796  * @param {String/Object} config A string to set only the panel's title, or a config object
31797  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31798  */
31799 Roo.TreePanel = function(config){
31800     var el = config.el;
31801     var tree = config.tree;
31802     delete config.tree; 
31803     delete config.el; // hopefull!
31804     
31805     // wrapper for IE7 strict & safari scroll issue
31806     
31807     var treeEl = el.createChild();
31808     config.resizeEl = treeEl;
31809     
31810     
31811     
31812     Roo.TreePanel.superclass.constructor.call(this, el, config);
31813  
31814  
31815     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31816     //console.log(tree);
31817     this.on('activate', function()
31818     {
31819         if (this.tree.rendered) {
31820             return;
31821         }
31822         //console.log('render tree');
31823         this.tree.render();
31824     });
31825     
31826     this.on('resize',  function (cp, w, h) {
31827             this.tree.innerCt.setWidth(w);
31828             this.tree.innerCt.setHeight(h);
31829             this.tree.innerCt.setStyle('overflow-y', 'auto');
31830     });
31831
31832         
31833     
31834 };
31835
31836 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31837     fitToFrame : true,
31838     autoScroll : true
31839 });
31840
31841
31842
31843
31844
31845
31846
31847
31848
31849
31850
31851 /*
31852  * Based on:
31853  * Ext JS Library 1.1.1
31854  * Copyright(c) 2006-2007, Ext JS, LLC.
31855  *
31856  * Originally Released Under LGPL - original licence link has changed is not relivant.
31857  *
31858  * Fork - LGPL
31859  * <script type="text/javascript">
31860  */
31861  
31862
31863 /**
31864  * @class Roo.ReaderLayout
31865  * @extends Roo.BorderLayout
31866  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31867  * center region containing two nested regions (a top one for a list view and one for item preview below),
31868  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31869  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31870  * expedites the setup of the overall layout and regions for this common application style.
31871  * Example:
31872  <pre><code>
31873 var reader = new Roo.ReaderLayout();
31874 var CP = Roo.ContentPanel;  // shortcut for adding
31875
31876 reader.beginUpdate();
31877 reader.add("north", new CP("north", "North"));
31878 reader.add("west", new CP("west", {title: "West"}));
31879 reader.add("east", new CP("east", {title: "East"}));
31880
31881 reader.regions.listView.add(new CP("listView", "List"));
31882 reader.regions.preview.add(new CP("preview", "Preview"));
31883 reader.endUpdate();
31884 </code></pre>
31885 * @constructor
31886 * Create a new ReaderLayout
31887 * @param {Object} config Configuration options
31888 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31889 * document.body if omitted)
31890 */
31891 Roo.ReaderLayout = function(config, renderTo){
31892     var c = config || {size:{}};
31893     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31894         north: c.north !== false ? Roo.apply({
31895             split:false,
31896             initialSize: 32,
31897             titlebar: false
31898         }, c.north) : false,
31899         west: c.west !== false ? Roo.apply({
31900             split:true,
31901             initialSize: 200,
31902             minSize: 175,
31903             maxSize: 400,
31904             titlebar: true,
31905             collapsible: true,
31906             animate: true,
31907             margins:{left:5,right:0,bottom:5,top:5},
31908             cmargins:{left:5,right:5,bottom:5,top:5}
31909         }, c.west) : false,
31910         east: c.east !== false ? Roo.apply({
31911             split:true,
31912             initialSize: 200,
31913             minSize: 175,
31914             maxSize: 400,
31915             titlebar: true,
31916             collapsible: true,
31917             animate: true,
31918             margins:{left:0,right:5,bottom:5,top:5},
31919             cmargins:{left:5,right:5,bottom:5,top:5}
31920         }, c.east) : false,
31921         center: Roo.apply({
31922             tabPosition: 'top',
31923             autoScroll:false,
31924             closeOnTab: true,
31925             titlebar:false,
31926             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31927         }, c.center)
31928     });
31929
31930     this.el.addClass('x-reader');
31931
31932     this.beginUpdate();
31933
31934     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31935         south: c.preview !== false ? Roo.apply({
31936             split:true,
31937             initialSize: 200,
31938             minSize: 100,
31939             autoScroll:true,
31940             collapsible:true,
31941             titlebar: true,
31942             cmargins:{top:5,left:0, right:0, bottom:0}
31943         }, c.preview) : false,
31944         center: Roo.apply({
31945             autoScroll:false,
31946             titlebar:false,
31947             minHeight:200
31948         }, c.listView)
31949     });
31950     this.add('center', new Roo.NestedLayoutPanel(inner,
31951             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31952
31953     this.endUpdate();
31954
31955     this.regions.preview = inner.getRegion('south');
31956     this.regions.listView = inner.getRegion('center');
31957 };
31958
31959 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31960  * Based on:
31961  * Ext JS Library 1.1.1
31962  * Copyright(c) 2006-2007, Ext JS, LLC.
31963  *
31964  * Originally Released Under LGPL - original licence link has changed is not relivant.
31965  *
31966  * Fork - LGPL
31967  * <script type="text/javascript">
31968  */
31969  
31970 /**
31971  * @class Roo.grid.Grid
31972  * @extends Roo.util.Observable
31973  * This class represents the primary interface of a component based grid control.
31974  * <br><br>Usage:<pre><code>
31975  var grid = new Roo.grid.Grid("my-container-id", {
31976      ds: myDataStore,
31977      cm: myColModel,
31978      selModel: mySelectionModel,
31979      autoSizeColumns: true,
31980      monitorWindowResize: false,
31981      trackMouseOver: true
31982  });
31983  // set any options
31984  grid.render();
31985  * </code></pre>
31986  * <b>Common Problems:</b><br/>
31987  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31988  * element will correct this<br/>
31989  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31990  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31991  * are unpredictable.<br/>
31992  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31993  * grid to calculate dimensions/offsets.<br/>
31994   * @constructor
31995  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31996  * The container MUST have some type of size defined for the grid to fill. The container will be
31997  * automatically set to position relative if it isn't already.
31998  * @param {Object} config A config object that sets properties on this grid.
31999  */
32000 Roo.grid.Grid = function(container, config){
32001         // initialize the container
32002         this.container = Roo.get(container);
32003         this.container.update("");
32004         this.container.setStyle("overflow", "hidden");
32005     this.container.addClass('x-grid-container');
32006
32007     this.id = this.container.id;
32008
32009     Roo.apply(this, config);
32010     // check and correct shorthanded configs
32011     if(this.ds){
32012         this.dataSource = this.ds;
32013         delete this.ds;
32014     }
32015     if(this.cm){
32016         this.colModel = this.cm;
32017         delete this.cm;
32018     }
32019     if(this.sm){
32020         this.selModel = this.sm;
32021         delete this.sm;
32022     }
32023
32024     if (this.selModel) {
32025         this.selModel = Roo.factory(this.selModel, Roo.grid);
32026         this.sm = this.selModel;
32027         this.sm.xmodule = this.xmodule || false;
32028     }
32029     if (typeof(this.colModel.config) == 'undefined') {
32030         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32031         this.cm = this.colModel;
32032         this.cm.xmodule = this.xmodule || false;
32033     }
32034     if (this.dataSource) {
32035         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32036         this.ds = this.dataSource;
32037         this.ds.xmodule = this.xmodule || false;
32038          
32039     }
32040     
32041     
32042     
32043     if(this.width){
32044         this.container.setWidth(this.width);
32045     }
32046
32047     if(this.height){
32048         this.container.setHeight(this.height);
32049     }
32050     /** @private */
32051         this.addEvents({
32052         // raw events
32053         /**
32054          * @event click
32055          * The raw click event for the entire grid.
32056          * @param {Roo.EventObject} e
32057          */
32058         "click" : true,
32059         /**
32060          * @event dblclick
32061          * The raw dblclick event for the entire grid.
32062          * @param {Roo.EventObject} e
32063          */
32064         "dblclick" : true,
32065         /**
32066          * @event contextmenu
32067          * The raw contextmenu event for the entire grid.
32068          * @param {Roo.EventObject} e
32069          */
32070         "contextmenu" : true,
32071         /**
32072          * @event mousedown
32073          * The raw mousedown event for the entire grid.
32074          * @param {Roo.EventObject} e
32075          */
32076         "mousedown" : true,
32077         /**
32078          * @event mouseup
32079          * The raw mouseup event for the entire grid.
32080          * @param {Roo.EventObject} e
32081          */
32082         "mouseup" : true,
32083         /**
32084          * @event mouseover
32085          * The raw mouseover event for the entire grid.
32086          * @param {Roo.EventObject} e
32087          */
32088         "mouseover" : true,
32089         /**
32090          * @event mouseout
32091          * The raw mouseout event for the entire grid.
32092          * @param {Roo.EventObject} e
32093          */
32094         "mouseout" : true,
32095         /**
32096          * @event keypress
32097          * The raw keypress event for the entire grid.
32098          * @param {Roo.EventObject} e
32099          */
32100         "keypress" : true,
32101         /**
32102          * @event keydown
32103          * The raw keydown event for the entire grid.
32104          * @param {Roo.EventObject} e
32105          */
32106         "keydown" : true,
32107
32108         // custom events
32109
32110         /**
32111          * @event cellclick
32112          * Fires when a cell is clicked
32113          * @param {Grid} this
32114          * @param {Number} rowIndex
32115          * @param {Number} columnIndex
32116          * @param {Roo.EventObject} e
32117          */
32118         "cellclick" : true,
32119         /**
32120          * @event celldblclick
32121          * Fires when a cell is double clicked
32122          * @param {Grid} this
32123          * @param {Number} rowIndex
32124          * @param {Number} columnIndex
32125          * @param {Roo.EventObject} e
32126          */
32127         "celldblclick" : true,
32128         /**
32129          * @event rowclick
32130          * Fires when a row is clicked
32131          * @param {Grid} this
32132          * @param {Number} rowIndex
32133          * @param {Roo.EventObject} e
32134          */
32135         "rowclick" : true,
32136         /**
32137          * @event rowdblclick
32138          * Fires when a row is double clicked
32139          * @param {Grid} this
32140          * @param {Number} rowIndex
32141          * @param {Roo.EventObject} e
32142          */
32143         "rowdblclick" : true,
32144         /**
32145          * @event headerclick
32146          * Fires when a header is clicked
32147          * @param {Grid} this
32148          * @param {Number} columnIndex
32149          * @param {Roo.EventObject} e
32150          */
32151         "headerclick" : true,
32152         /**
32153          * @event headerdblclick
32154          * Fires when a header cell is double clicked
32155          * @param {Grid} this
32156          * @param {Number} columnIndex
32157          * @param {Roo.EventObject} e
32158          */
32159         "headerdblclick" : true,
32160         /**
32161          * @event rowcontextmenu
32162          * Fires when a row is right clicked
32163          * @param {Grid} this
32164          * @param {Number} rowIndex
32165          * @param {Roo.EventObject} e
32166          */
32167         "rowcontextmenu" : true,
32168         /**
32169          * @event cellcontextmenu
32170          * Fires when a cell is right clicked
32171          * @param {Grid} this
32172          * @param {Number} rowIndex
32173          * @param {Number} cellIndex
32174          * @param {Roo.EventObject} e
32175          */
32176          "cellcontextmenu" : true,
32177         /**
32178          * @event headercontextmenu
32179          * Fires when a header is right clicked
32180          * @param {Grid} this
32181          * @param {Number} columnIndex
32182          * @param {Roo.EventObject} e
32183          */
32184         "headercontextmenu" : true,
32185         /**
32186          * @event bodyscroll
32187          * Fires when the body element is scrolled
32188          * @param {Number} scrollLeft
32189          * @param {Number} scrollTop
32190          */
32191         "bodyscroll" : true,
32192         /**
32193          * @event columnresize
32194          * Fires when the user resizes a column
32195          * @param {Number} columnIndex
32196          * @param {Number} newSize
32197          */
32198         "columnresize" : true,
32199         /**
32200          * @event columnmove
32201          * Fires when the user moves a column
32202          * @param {Number} oldIndex
32203          * @param {Number} newIndex
32204          */
32205         "columnmove" : true,
32206         /**
32207          * @event startdrag
32208          * Fires when row(s) start being dragged
32209          * @param {Grid} this
32210          * @param {Roo.GridDD} dd The drag drop object
32211          * @param {event} e The raw browser event
32212          */
32213         "startdrag" : true,
32214         /**
32215          * @event enddrag
32216          * Fires when a drag operation is complete
32217          * @param {Grid} this
32218          * @param {Roo.GridDD} dd The drag drop object
32219          * @param {event} e The raw browser event
32220          */
32221         "enddrag" : true,
32222         /**
32223          * @event dragdrop
32224          * Fires when dragged row(s) are dropped on a valid DD target
32225          * @param {Grid} this
32226          * @param {Roo.GridDD} dd The drag drop object
32227          * @param {String} targetId The target drag drop object
32228          * @param {event} e The raw browser event
32229          */
32230         "dragdrop" : true,
32231         /**
32232          * @event dragover
32233          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32234          * @param {Grid} this
32235          * @param {Roo.GridDD} dd The drag drop object
32236          * @param {String} targetId The target drag drop object
32237          * @param {event} e The raw browser event
32238          */
32239         "dragover" : true,
32240         /**
32241          * @event dragenter
32242          *  Fires when the dragged row(s) first cross another DD target while being dragged
32243          * @param {Grid} this
32244          * @param {Roo.GridDD} dd The drag drop object
32245          * @param {String} targetId The target drag drop object
32246          * @param {event} e The raw browser event
32247          */
32248         "dragenter" : true,
32249         /**
32250          * @event dragout
32251          * Fires when the dragged row(s) leave another DD target while being dragged
32252          * @param {Grid} this
32253          * @param {Roo.GridDD} dd The drag drop object
32254          * @param {String} targetId The target drag drop object
32255          * @param {event} e The raw browser event
32256          */
32257         "dragout" : true,
32258         /**
32259          * @event rowclass
32260          * Fires when a row is rendered, so you can change add a style to it.
32261          * @param {GridView} gridview   The grid view
32262          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32263          */
32264         'rowclass' : true,
32265
32266         /**
32267          * @event render
32268          * Fires when the grid is rendered
32269          * @param {Grid} grid
32270          */
32271         'render' : true
32272     });
32273
32274     Roo.grid.Grid.superclass.constructor.call(this);
32275 };
32276 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32277     
32278     /**
32279      * @cfg {String} ddGroup - drag drop group.
32280      */
32281
32282     /**
32283      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32284      */
32285     minColumnWidth : 25,
32286
32287     /**
32288      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32289      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32290      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32291      */
32292     autoSizeColumns : false,
32293
32294     /**
32295      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32296      */
32297     autoSizeHeaders : true,
32298
32299     /**
32300      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32301      */
32302     monitorWindowResize : true,
32303
32304     /**
32305      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32306      * rows measured to get a columns size. Default is 0 (all rows).
32307      */
32308     maxRowsToMeasure : 0,
32309
32310     /**
32311      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32312      */
32313     trackMouseOver : true,
32314
32315     /**
32316     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32317     */
32318     
32319     /**
32320     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32321     */
32322     enableDragDrop : false,
32323     
32324     /**
32325     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32326     */
32327     enableColumnMove : true,
32328     
32329     /**
32330     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32331     */
32332     enableColumnHide : true,
32333     
32334     /**
32335     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32336     */
32337     enableRowHeightSync : false,
32338     
32339     /**
32340     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32341     */
32342     stripeRows : true,
32343     
32344     /**
32345     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32346     */
32347     autoHeight : false,
32348
32349     /**
32350      * @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.
32351      */
32352     autoExpandColumn : false,
32353
32354     /**
32355     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32356     * Default is 50.
32357     */
32358     autoExpandMin : 50,
32359
32360     /**
32361     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32362     */
32363     autoExpandMax : 1000,
32364
32365     /**
32366     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32367     */
32368     view : null,
32369
32370     /**
32371     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32372     */
32373     loadMask : false,
32374     /**
32375     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32376     */
32377     dropTarget: false,
32378     
32379    
32380     
32381     // private
32382     rendered : false,
32383
32384     /**
32385     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32386     * of a fixed width. Default is false.
32387     */
32388     /**
32389     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32390     */
32391     /**
32392      * Called once after all setup has been completed and the grid is ready to be rendered.
32393      * @return {Roo.grid.Grid} this
32394      */
32395     render : function()
32396     {
32397         var c = this.container;
32398         // try to detect autoHeight/width mode
32399         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32400             this.autoHeight = true;
32401         }
32402         var view = this.getView();
32403         view.init(this);
32404
32405         c.on("click", this.onClick, this);
32406         c.on("dblclick", this.onDblClick, this);
32407         c.on("contextmenu", this.onContextMenu, this);
32408         c.on("keydown", this.onKeyDown, this);
32409
32410         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32411
32412         this.getSelectionModel().init(this);
32413
32414         view.render();
32415
32416         if(this.loadMask){
32417             this.loadMask = new Roo.LoadMask(this.container,
32418                     Roo.apply({store:this.dataSource}, this.loadMask));
32419         }
32420         
32421         
32422         if (this.toolbar && this.toolbar.xtype) {
32423             this.toolbar.container = this.getView().getHeaderPanel(true);
32424             this.toolbar = new Roo.Toolbar(this.toolbar);
32425         }
32426         if (this.footer && this.footer.xtype) {
32427             this.footer.dataSource = this.getDataSource();
32428             this.footer.container = this.getView().getFooterPanel(true);
32429             this.footer = Roo.factory(this.footer, Roo);
32430         }
32431         if (this.dropTarget && this.dropTarget.xtype) {
32432             delete this.dropTarget.xtype;
32433             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32434         }
32435         
32436         
32437         this.rendered = true;
32438         this.fireEvent('render', this);
32439         return this;
32440     },
32441
32442         /**
32443          * Reconfigures the grid to use a different Store and Column Model.
32444          * The View will be bound to the new objects and refreshed.
32445          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32446          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32447          */
32448     reconfigure : function(dataSource, colModel){
32449         if(this.loadMask){
32450             this.loadMask.destroy();
32451             this.loadMask = new Roo.LoadMask(this.container,
32452                     Roo.apply({store:dataSource}, this.loadMask));
32453         }
32454         this.view.bind(dataSource, colModel);
32455         this.dataSource = dataSource;
32456         this.colModel = colModel;
32457         this.view.refresh(true);
32458     },
32459
32460     // private
32461     onKeyDown : function(e){
32462         this.fireEvent("keydown", e);
32463     },
32464
32465     /**
32466      * Destroy this grid.
32467      * @param {Boolean} removeEl True to remove the element
32468      */
32469     destroy : function(removeEl, keepListeners){
32470         if(this.loadMask){
32471             this.loadMask.destroy();
32472         }
32473         var c = this.container;
32474         c.removeAllListeners();
32475         this.view.destroy();
32476         this.colModel.purgeListeners();
32477         if(!keepListeners){
32478             this.purgeListeners();
32479         }
32480         c.update("");
32481         if(removeEl === true){
32482             c.remove();
32483         }
32484     },
32485
32486     // private
32487     processEvent : function(name, e){
32488         this.fireEvent(name, e);
32489         var t = e.getTarget();
32490         var v = this.view;
32491         var header = v.findHeaderIndex(t);
32492         if(header !== false){
32493             this.fireEvent("header" + name, this, header, e);
32494         }else{
32495             var row = v.findRowIndex(t);
32496             var cell = v.findCellIndex(t);
32497             if(row !== false){
32498                 this.fireEvent("row" + name, this, row, e);
32499                 if(cell !== false){
32500                     this.fireEvent("cell" + name, this, row, cell, e);
32501                 }
32502             }
32503         }
32504     },
32505
32506     // private
32507     onClick : function(e){
32508         this.processEvent("click", e);
32509     },
32510
32511     // private
32512     onContextMenu : function(e, t){
32513         this.processEvent("contextmenu", e);
32514     },
32515
32516     // private
32517     onDblClick : function(e){
32518         this.processEvent("dblclick", e);
32519     },
32520
32521     // private
32522     walkCells : function(row, col, step, fn, scope){
32523         var cm = this.colModel, clen = cm.getColumnCount();
32524         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32525         if(step < 0){
32526             if(col < 0){
32527                 row--;
32528                 first = false;
32529             }
32530             while(row >= 0){
32531                 if(!first){
32532                     col = clen-1;
32533                 }
32534                 first = false;
32535                 while(col >= 0){
32536                     if(fn.call(scope || this, row, col, cm) === true){
32537                         return [row, col];
32538                     }
32539                     col--;
32540                 }
32541                 row--;
32542             }
32543         } else {
32544             if(col >= clen){
32545                 row++;
32546                 first = false;
32547             }
32548             while(row < rlen){
32549                 if(!first){
32550                     col = 0;
32551                 }
32552                 first = false;
32553                 while(col < clen){
32554                     if(fn.call(scope || this, row, col, cm) === true){
32555                         return [row, col];
32556                     }
32557                     col++;
32558                 }
32559                 row++;
32560             }
32561         }
32562         return null;
32563     },
32564
32565     // private
32566     getSelections : function(){
32567         return this.selModel.getSelections();
32568     },
32569
32570     /**
32571      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32572      * but if manual update is required this method will initiate it.
32573      */
32574     autoSize : function(){
32575         if(this.rendered){
32576             this.view.layout();
32577             if(this.view.adjustForScroll){
32578                 this.view.adjustForScroll();
32579             }
32580         }
32581     },
32582
32583     /**
32584      * Returns the grid's underlying element.
32585      * @return {Element} The element
32586      */
32587     getGridEl : function(){
32588         return this.container;
32589     },
32590
32591     // private for compatibility, overridden by editor grid
32592     stopEditing : function(){},
32593
32594     /**
32595      * Returns the grid's SelectionModel.
32596      * @return {SelectionModel}
32597      */
32598     getSelectionModel : function(){
32599         if(!this.selModel){
32600             this.selModel = new Roo.grid.RowSelectionModel();
32601         }
32602         return this.selModel;
32603     },
32604
32605     /**
32606      * Returns the grid's DataSource.
32607      * @return {DataSource}
32608      */
32609     getDataSource : function(){
32610         return this.dataSource;
32611     },
32612
32613     /**
32614      * Returns the grid's ColumnModel.
32615      * @return {ColumnModel}
32616      */
32617     getColumnModel : function(){
32618         return this.colModel;
32619     },
32620
32621     /**
32622      * Returns the grid's GridView object.
32623      * @return {GridView}
32624      */
32625     getView : function(){
32626         if(!this.view){
32627             this.view = new Roo.grid.GridView(this.viewConfig);
32628         }
32629         return this.view;
32630     },
32631     /**
32632      * Called to get grid's drag proxy text, by default returns this.ddText.
32633      * @return {String}
32634      */
32635     getDragDropText : function(){
32636         var count = this.selModel.getCount();
32637         return String.format(this.ddText, count, count == 1 ? '' : 's');
32638     }
32639 });
32640 /**
32641  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32642  * %0 is replaced with the number of selected rows.
32643  * @type String
32644  */
32645 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32646  * Based on:
32647  * Ext JS Library 1.1.1
32648  * Copyright(c) 2006-2007, Ext JS, LLC.
32649  *
32650  * Originally Released Under LGPL - original licence link has changed is not relivant.
32651  *
32652  * Fork - LGPL
32653  * <script type="text/javascript">
32654  */
32655  
32656 Roo.grid.AbstractGridView = function(){
32657         this.grid = null;
32658         
32659         this.events = {
32660             "beforerowremoved" : true,
32661             "beforerowsinserted" : true,
32662             "beforerefresh" : true,
32663             "rowremoved" : true,
32664             "rowsinserted" : true,
32665             "rowupdated" : true,
32666             "refresh" : true
32667         };
32668     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32669 };
32670
32671 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32672     rowClass : "x-grid-row",
32673     cellClass : "x-grid-cell",
32674     tdClass : "x-grid-td",
32675     hdClass : "x-grid-hd",
32676     splitClass : "x-grid-hd-split",
32677     
32678         init: function(grid){
32679         this.grid = grid;
32680                 var cid = this.grid.getGridEl().id;
32681         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32682         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32683         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32684         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32685         },
32686         
32687         getColumnRenderers : function(){
32688         var renderers = [];
32689         var cm = this.grid.colModel;
32690         var colCount = cm.getColumnCount();
32691         for(var i = 0; i < colCount; i++){
32692             renderers[i] = cm.getRenderer(i);
32693         }
32694         return renderers;
32695     },
32696     
32697     getColumnIds : function(){
32698         var ids = [];
32699         var cm = this.grid.colModel;
32700         var colCount = cm.getColumnCount();
32701         for(var i = 0; i < colCount; i++){
32702             ids[i] = cm.getColumnId(i);
32703         }
32704         return ids;
32705     },
32706     
32707     getDataIndexes : function(){
32708         if(!this.indexMap){
32709             this.indexMap = this.buildIndexMap();
32710         }
32711         return this.indexMap.colToData;
32712     },
32713     
32714     getColumnIndexByDataIndex : function(dataIndex){
32715         if(!this.indexMap){
32716             this.indexMap = this.buildIndexMap();
32717         }
32718         return this.indexMap.dataToCol[dataIndex];
32719     },
32720     
32721     /**
32722      * Set a css style for a column dynamically. 
32723      * @param {Number} colIndex The index of the column
32724      * @param {String} name The css property name
32725      * @param {String} value The css value
32726      */
32727     setCSSStyle : function(colIndex, name, value){
32728         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32729         Roo.util.CSS.updateRule(selector, name, value);
32730     },
32731     
32732     generateRules : function(cm){
32733         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32734         Roo.util.CSS.removeStyleSheet(rulesId);
32735         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32736             var cid = cm.getColumnId(i);
32737             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32738                          this.tdSelector, cid, " {\n}\n",
32739                          this.hdSelector, cid, " {\n}\n",
32740                          this.splitSelector, cid, " {\n}\n");
32741         }
32742         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32743     }
32744 });/*
32745  * Based on:
32746  * Ext JS Library 1.1.1
32747  * Copyright(c) 2006-2007, Ext JS, LLC.
32748  *
32749  * Originally Released Under LGPL - original licence link has changed is not relivant.
32750  *
32751  * Fork - LGPL
32752  * <script type="text/javascript">
32753  */
32754
32755 // private
32756 // This is a support class used internally by the Grid components
32757 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32758     this.grid = grid;
32759     this.view = grid.getView();
32760     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32761     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32762     if(hd2){
32763         this.setHandleElId(Roo.id(hd));
32764         this.setOuterHandleElId(Roo.id(hd2));
32765     }
32766     this.scroll = false;
32767 };
32768 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32769     maxDragWidth: 120,
32770     getDragData : function(e){
32771         var t = Roo.lib.Event.getTarget(e);
32772         var h = this.view.findHeaderCell(t);
32773         if(h){
32774             return {ddel: h.firstChild, header:h};
32775         }
32776         return false;
32777     },
32778
32779     onInitDrag : function(e){
32780         this.view.headersDisabled = true;
32781         var clone = this.dragData.ddel.cloneNode(true);
32782         clone.id = Roo.id();
32783         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32784         this.proxy.update(clone);
32785         return true;
32786     },
32787
32788     afterValidDrop : function(){
32789         var v = this.view;
32790         setTimeout(function(){
32791             v.headersDisabled = false;
32792         }, 50);
32793     },
32794
32795     afterInvalidDrop : function(){
32796         var v = this.view;
32797         setTimeout(function(){
32798             v.headersDisabled = false;
32799         }, 50);
32800     }
32801 });
32802 /*
32803  * Based on:
32804  * Ext JS Library 1.1.1
32805  * Copyright(c) 2006-2007, Ext JS, LLC.
32806  *
32807  * Originally Released Under LGPL - original licence link has changed is not relivant.
32808  *
32809  * Fork - LGPL
32810  * <script type="text/javascript">
32811  */
32812 // private
32813 // This is a support class used internally by the Grid components
32814 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32815     this.grid = grid;
32816     this.view = grid.getView();
32817     // split the proxies so they don't interfere with mouse events
32818     this.proxyTop = Roo.DomHelper.append(document.body, {
32819         cls:"col-move-top", html:"&#160;"
32820     }, true);
32821     this.proxyBottom = Roo.DomHelper.append(document.body, {
32822         cls:"col-move-bottom", html:"&#160;"
32823     }, true);
32824     this.proxyTop.hide = this.proxyBottom.hide = function(){
32825         this.setLeftTop(-100,-100);
32826         this.setStyle("visibility", "hidden");
32827     };
32828     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32829     // temporarily disabled
32830     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32831     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32832 };
32833 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32834     proxyOffsets : [-4, -9],
32835     fly: Roo.Element.fly,
32836
32837     getTargetFromEvent : function(e){
32838         var t = Roo.lib.Event.getTarget(e);
32839         var cindex = this.view.findCellIndex(t);
32840         if(cindex !== false){
32841             return this.view.getHeaderCell(cindex);
32842         }
32843         return null;
32844     },
32845
32846     nextVisible : function(h){
32847         var v = this.view, cm = this.grid.colModel;
32848         h = h.nextSibling;
32849         while(h){
32850             if(!cm.isHidden(v.getCellIndex(h))){
32851                 return h;
32852             }
32853             h = h.nextSibling;
32854         }
32855         return null;
32856     },
32857
32858     prevVisible : function(h){
32859         var v = this.view, cm = this.grid.colModel;
32860         h = h.prevSibling;
32861         while(h){
32862             if(!cm.isHidden(v.getCellIndex(h))){
32863                 return h;
32864             }
32865             h = h.prevSibling;
32866         }
32867         return null;
32868     },
32869
32870     positionIndicator : function(h, n, e){
32871         var x = Roo.lib.Event.getPageX(e);
32872         var r = Roo.lib.Dom.getRegion(n.firstChild);
32873         var px, pt, py = r.top + this.proxyOffsets[1];
32874         if((r.right - x) <= (r.right-r.left)/2){
32875             px = r.right+this.view.borderWidth;
32876             pt = "after";
32877         }else{
32878             px = r.left;
32879             pt = "before";
32880         }
32881         var oldIndex = this.view.getCellIndex(h);
32882         var newIndex = this.view.getCellIndex(n);
32883
32884         if(this.grid.colModel.isFixed(newIndex)){
32885             return false;
32886         }
32887
32888         var locked = this.grid.colModel.isLocked(newIndex);
32889
32890         if(pt == "after"){
32891             newIndex++;
32892         }
32893         if(oldIndex < newIndex){
32894             newIndex--;
32895         }
32896         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32897             return false;
32898         }
32899         px +=  this.proxyOffsets[0];
32900         this.proxyTop.setLeftTop(px, py);
32901         this.proxyTop.show();
32902         if(!this.bottomOffset){
32903             this.bottomOffset = this.view.mainHd.getHeight();
32904         }
32905         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32906         this.proxyBottom.show();
32907         return pt;
32908     },
32909
32910     onNodeEnter : function(n, dd, e, data){
32911         if(data.header != n){
32912             this.positionIndicator(data.header, n, e);
32913         }
32914     },
32915
32916     onNodeOver : function(n, dd, e, data){
32917         var result = false;
32918         if(data.header != n){
32919             result = this.positionIndicator(data.header, n, e);
32920         }
32921         if(!result){
32922             this.proxyTop.hide();
32923             this.proxyBottom.hide();
32924         }
32925         return result ? this.dropAllowed : this.dropNotAllowed;
32926     },
32927
32928     onNodeOut : function(n, dd, e, data){
32929         this.proxyTop.hide();
32930         this.proxyBottom.hide();
32931     },
32932
32933     onNodeDrop : function(n, dd, e, data){
32934         var h = data.header;
32935         if(h != n){
32936             var cm = this.grid.colModel;
32937             var x = Roo.lib.Event.getPageX(e);
32938             var r = Roo.lib.Dom.getRegion(n.firstChild);
32939             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32940             var oldIndex = this.view.getCellIndex(h);
32941             var newIndex = this.view.getCellIndex(n);
32942             var locked = cm.isLocked(newIndex);
32943             if(pt == "after"){
32944                 newIndex++;
32945             }
32946             if(oldIndex < newIndex){
32947                 newIndex--;
32948             }
32949             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32950                 return false;
32951             }
32952             cm.setLocked(oldIndex, locked, true);
32953             cm.moveColumn(oldIndex, newIndex);
32954             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32955             return true;
32956         }
32957         return false;
32958     }
32959 });
32960 /*
32961  * Based on:
32962  * Ext JS Library 1.1.1
32963  * Copyright(c) 2006-2007, Ext JS, LLC.
32964  *
32965  * Originally Released Under LGPL - original licence link has changed is not relivant.
32966  *
32967  * Fork - LGPL
32968  * <script type="text/javascript">
32969  */
32970   
32971 /**
32972  * @class Roo.grid.GridView
32973  * @extends Roo.util.Observable
32974  *
32975  * @constructor
32976  * @param {Object} config
32977  */
32978 Roo.grid.GridView = function(config){
32979     Roo.grid.GridView.superclass.constructor.call(this);
32980     this.el = null;
32981
32982     Roo.apply(this, config);
32983 };
32984
32985 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32986
32987     /**
32988      * Override this function to apply custom css classes to rows during rendering
32989      * @param {Record} record The record
32990      * @param {Number} index
32991      * @method getRowClass
32992      */
32993     rowClass : "x-grid-row",
32994
32995     cellClass : "x-grid-col",
32996
32997     tdClass : "x-grid-td",
32998
32999     hdClass : "x-grid-hd",
33000
33001     splitClass : "x-grid-split",
33002
33003     sortClasses : ["sort-asc", "sort-desc"],
33004
33005     enableMoveAnim : false,
33006
33007     hlColor: "C3DAF9",
33008
33009     dh : Roo.DomHelper,
33010
33011     fly : Roo.Element.fly,
33012
33013     css : Roo.util.CSS,
33014
33015     borderWidth: 1,
33016
33017     splitOffset: 3,
33018
33019     scrollIncrement : 22,
33020
33021     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33022
33023     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33024
33025     bind : function(ds, cm){
33026         if(this.ds){
33027             this.ds.un("load", this.onLoad, this);
33028             this.ds.un("datachanged", this.onDataChange, this);
33029             this.ds.un("add", this.onAdd, this);
33030             this.ds.un("remove", this.onRemove, this);
33031             this.ds.un("update", this.onUpdate, this);
33032             this.ds.un("clear", this.onClear, this);
33033         }
33034         if(ds){
33035             ds.on("load", this.onLoad, this);
33036             ds.on("datachanged", this.onDataChange, this);
33037             ds.on("add", this.onAdd, this);
33038             ds.on("remove", this.onRemove, this);
33039             ds.on("update", this.onUpdate, this);
33040             ds.on("clear", this.onClear, this);
33041         }
33042         this.ds = ds;
33043
33044         if(this.cm){
33045             this.cm.un("widthchange", this.onColWidthChange, this);
33046             this.cm.un("headerchange", this.onHeaderChange, this);
33047             this.cm.un("hiddenchange", this.onHiddenChange, this);
33048             this.cm.un("columnmoved", this.onColumnMove, this);
33049             this.cm.un("columnlockchange", this.onColumnLock, this);
33050         }
33051         if(cm){
33052             this.generateRules(cm);
33053             cm.on("widthchange", this.onColWidthChange, this);
33054             cm.on("headerchange", this.onHeaderChange, this);
33055             cm.on("hiddenchange", this.onHiddenChange, this);
33056             cm.on("columnmoved", this.onColumnMove, this);
33057             cm.on("columnlockchange", this.onColumnLock, this);
33058         }
33059         this.cm = cm;
33060     },
33061
33062     init: function(grid){
33063         Roo.grid.GridView.superclass.init.call(this, grid);
33064
33065         this.bind(grid.dataSource, grid.colModel);
33066
33067         grid.on("headerclick", this.handleHeaderClick, this);
33068
33069         if(grid.trackMouseOver){
33070             grid.on("mouseover", this.onRowOver, this);
33071             grid.on("mouseout", this.onRowOut, this);
33072         }
33073         grid.cancelTextSelection = function(){};
33074         this.gridId = grid.id;
33075
33076         var tpls = this.templates || {};
33077
33078         if(!tpls.master){
33079             tpls.master = new Roo.Template(
33080                '<div class="x-grid" hidefocus="true">',
33081                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33082                   '<div class="x-grid-topbar"></div>',
33083                   '<div class="x-grid-scroller"><div></div></div>',
33084                   '<div class="x-grid-locked">',
33085                       '<div class="x-grid-header">{lockedHeader}</div>',
33086                       '<div class="x-grid-body">{lockedBody}</div>',
33087                   "</div>",
33088                   '<div class="x-grid-viewport">',
33089                       '<div class="x-grid-header">{header}</div>',
33090                       '<div class="x-grid-body">{body}</div>',
33091                   "</div>",
33092                   '<div class="x-grid-bottombar"></div>',
33093                  
33094                   '<div class="x-grid-resize-proxy">&#160;</div>',
33095                "</div>"
33096             );
33097             tpls.master.disableformats = true;
33098         }
33099
33100         if(!tpls.header){
33101             tpls.header = new Roo.Template(
33102                '<table border="0" cellspacing="0" cellpadding="0">',
33103                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33104                "</table>{splits}"
33105             );
33106             tpls.header.disableformats = true;
33107         }
33108         tpls.header.compile();
33109
33110         if(!tpls.hcell){
33111             tpls.hcell = new Roo.Template(
33112                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33113                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33114                 "</div></td>"
33115              );
33116              tpls.hcell.disableFormats = true;
33117         }
33118         tpls.hcell.compile();
33119
33120         if(!tpls.hsplit){
33121             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
33122             tpls.hsplit.disableFormats = true;
33123         }
33124         tpls.hsplit.compile();
33125
33126         if(!tpls.body){
33127             tpls.body = new Roo.Template(
33128                '<table border="0" cellspacing="0" cellpadding="0">',
33129                "<tbody>{rows}</tbody>",
33130                "</table>"
33131             );
33132             tpls.body.disableFormats = true;
33133         }
33134         tpls.body.compile();
33135
33136         if(!tpls.row){
33137             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33138             tpls.row.disableFormats = true;
33139         }
33140         tpls.row.compile();
33141
33142         if(!tpls.cell){
33143             tpls.cell = new Roo.Template(
33144                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33145                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
33146                 "</td>"
33147             );
33148             tpls.cell.disableFormats = true;
33149         }
33150         tpls.cell.compile();
33151
33152         this.templates = tpls;
33153     },
33154
33155     // remap these for backwards compat
33156     onColWidthChange : function(){
33157         this.updateColumns.apply(this, arguments);
33158     },
33159     onHeaderChange : function(){
33160         this.updateHeaders.apply(this, arguments);
33161     }, 
33162     onHiddenChange : function(){
33163         this.handleHiddenChange.apply(this, arguments);
33164     },
33165     onColumnMove : function(){
33166         this.handleColumnMove.apply(this, arguments);
33167     },
33168     onColumnLock : function(){
33169         this.handleLockChange.apply(this, arguments);
33170     },
33171
33172     onDataChange : function(){
33173         this.refresh();
33174         this.updateHeaderSortState();
33175     },
33176
33177     onClear : function(){
33178         this.refresh();
33179     },
33180
33181     onUpdate : function(ds, record){
33182         this.refreshRow(record);
33183     },
33184
33185     refreshRow : function(record){
33186         var ds = this.ds, index;
33187         if(typeof record == 'number'){
33188             index = record;
33189             record = ds.getAt(index);
33190         }else{
33191             index = ds.indexOf(record);
33192         }
33193         this.insertRows(ds, index, index, true);
33194         this.onRemove(ds, record, index+1, true);
33195         this.syncRowHeights(index, index);
33196         this.layout();
33197         this.fireEvent("rowupdated", this, index, record);
33198     },
33199
33200     onAdd : function(ds, records, index){
33201         this.insertRows(ds, index, index + (records.length-1));
33202     },
33203
33204     onRemove : function(ds, record, index, isUpdate){
33205         if(isUpdate !== true){
33206             this.fireEvent("beforerowremoved", this, index, record);
33207         }
33208         var bt = this.getBodyTable(), lt = this.getLockedTable();
33209         if(bt.rows[index]){
33210             bt.firstChild.removeChild(bt.rows[index]);
33211         }
33212         if(lt.rows[index]){
33213             lt.firstChild.removeChild(lt.rows[index]);
33214         }
33215         if(isUpdate !== true){
33216             this.stripeRows(index);
33217             this.syncRowHeights(index, index);
33218             this.layout();
33219             this.fireEvent("rowremoved", this, index, record);
33220         }
33221     },
33222
33223     onLoad : function(){
33224         this.scrollToTop();
33225     },
33226
33227     /**
33228      * Scrolls the grid to the top
33229      */
33230     scrollToTop : function(){
33231         if(this.scroller){
33232             this.scroller.dom.scrollTop = 0;
33233             this.syncScroll();
33234         }
33235     },
33236
33237     /**
33238      * Gets a panel in the header of the grid that can be used for toolbars etc.
33239      * After modifying the contents of this panel a call to grid.autoSize() may be
33240      * required to register any changes in size.
33241      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33242      * @return Roo.Element
33243      */
33244     getHeaderPanel : function(doShow){
33245         if(doShow){
33246             this.headerPanel.show();
33247         }
33248         return this.headerPanel;
33249     },
33250
33251     /**
33252      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33253      * After modifying the contents of this panel a call to grid.autoSize() may be
33254      * required to register any changes in size.
33255      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33256      * @return Roo.Element
33257      */
33258     getFooterPanel : function(doShow){
33259         if(doShow){
33260             this.footerPanel.show();
33261         }
33262         return this.footerPanel;
33263     },
33264
33265     initElements : function(){
33266         var E = Roo.Element;
33267         var el = this.grid.getGridEl().dom.firstChild;
33268         var cs = el.childNodes;
33269
33270         this.el = new E(el);
33271         
33272          this.focusEl = new E(el.firstChild);
33273         this.focusEl.swallowEvent("click", true);
33274         
33275         this.headerPanel = new E(cs[1]);
33276         this.headerPanel.enableDisplayMode("block");
33277
33278         this.scroller = new E(cs[2]);
33279         this.scrollSizer = new E(this.scroller.dom.firstChild);
33280
33281         this.lockedWrap = new E(cs[3]);
33282         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33283         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33284
33285         this.mainWrap = new E(cs[4]);
33286         this.mainHd = new E(this.mainWrap.dom.firstChild);
33287         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33288
33289         this.footerPanel = new E(cs[5]);
33290         this.footerPanel.enableDisplayMode("block");
33291
33292         this.resizeProxy = new E(cs[6]);
33293
33294         this.headerSelector = String.format(
33295            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33296            this.lockedHd.id, this.mainHd.id
33297         );
33298
33299         this.splitterSelector = String.format(
33300            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33301            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33302         );
33303     },
33304     idToCssName : function(s)
33305     {
33306         return s.replace(/[^a-z0-9]+/ig, '-');
33307     },
33308
33309     getHeaderCell : function(index){
33310         return Roo.DomQuery.select(this.headerSelector)[index];
33311     },
33312
33313     getHeaderCellMeasure : function(index){
33314         return this.getHeaderCell(index).firstChild;
33315     },
33316
33317     getHeaderCellText : function(index){
33318         return this.getHeaderCell(index).firstChild.firstChild;
33319     },
33320
33321     getLockedTable : function(){
33322         return this.lockedBody.dom.firstChild;
33323     },
33324
33325     getBodyTable : function(){
33326         return this.mainBody.dom.firstChild;
33327     },
33328
33329     getLockedRow : function(index){
33330         return this.getLockedTable().rows[index];
33331     },
33332
33333     getRow : function(index){
33334         return this.getBodyTable().rows[index];
33335     },
33336
33337     getRowComposite : function(index){
33338         if(!this.rowEl){
33339             this.rowEl = new Roo.CompositeElementLite();
33340         }
33341         var els = [], lrow, mrow;
33342         if(lrow = this.getLockedRow(index)){
33343             els.push(lrow);
33344         }
33345         if(mrow = this.getRow(index)){
33346             els.push(mrow);
33347         }
33348         this.rowEl.elements = els;
33349         return this.rowEl;
33350     },
33351
33352     getCell : function(rowIndex, colIndex){
33353         var locked = this.cm.getLockedCount();
33354         var source;
33355         if(colIndex < locked){
33356             source = this.lockedBody.dom.firstChild;
33357         }else{
33358             source = this.mainBody.dom.firstChild;
33359             colIndex -= locked;
33360         }
33361         return source.rows[rowIndex].childNodes[colIndex];
33362     },
33363
33364     getCellText : function(rowIndex, colIndex){
33365         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33366     },
33367
33368     getCellBox : function(cell){
33369         var b = this.fly(cell).getBox();
33370         if(Roo.isOpera){ // opera fails to report the Y
33371             b.y = cell.offsetTop + this.mainBody.getY();
33372         }
33373         return b;
33374     },
33375
33376     getCellIndex : function(cell){
33377         var id = String(cell.className).match(this.cellRE);
33378         if(id){
33379             return parseInt(id[1], 10);
33380         }
33381         return 0;
33382     },
33383
33384     findHeaderIndex : function(n){
33385         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33386         return r ? this.getCellIndex(r) : false;
33387     },
33388
33389     findHeaderCell : function(n){
33390         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33391         return r ? r : false;
33392     },
33393
33394     findRowIndex : function(n){
33395         if(!n){
33396             return false;
33397         }
33398         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33399         return r ? r.rowIndex : false;
33400     },
33401
33402     findCellIndex : function(node){
33403         var stop = this.el.dom;
33404         while(node && node != stop){
33405             if(this.findRE.test(node.className)){
33406                 return this.getCellIndex(node);
33407             }
33408             node = node.parentNode;
33409         }
33410         return false;
33411     },
33412
33413     getColumnId : function(index){
33414         return this.cm.getColumnId(index);
33415     },
33416
33417     getSplitters : function()
33418     {
33419         if(this.splitterSelector){
33420            return Roo.DomQuery.select(this.splitterSelector);
33421         }else{
33422             return null;
33423       }
33424     },
33425
33426     getSplitter : function(index){
33427         return this.getSplitters()[index];
33428     },
33429
33430     onRowOver : function(e, t){
33431         var row;
33432         if((row = this.findRowIndex(t)) !== false){
33433             this.getRowComposite(row).addClass("x-grid-row-over");
33434         }
33435     },
33436
33437     onRowOut : function(e, t){
33438         var row;
33439         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33440             this.getRowComposite(row).removeClass("x-grid-row-over");
33441         }
33442     },
33443
33444     renderHeaders : function(){
33445         var cm = this.cm;
33446         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33447         var cb = [], lb = [], sb = [], lsb = [], p = {};
33448         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33449             p.cellId = "x-grid-hd-0-" + i;
33450             p.splitId = "x-grid-csplit-0-" + i;
33451             p.id = cm.getColumnId(i);
33452             p.title = cm.getColumnTooltip(i) || "";
33453             p.value = cm.getColumnHeader(i) || "";
33454             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33455             if(!cm.isLocked(i)){
33456                 cb[cb.length] = ct.apply(p);
33457                 sb[sb.length] = st.apply(p);
33458             }else{
33459                 lb[lb.length] = ct.apply(p);
33460                 lsb[lsb.length] = st.apply(p);
33461             }
33462         }
33463         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33464                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33465     },
33466
33467     updateHeaders : function(){
33468         var html = this.renderHeaders();
33469         this.lockedHd.update(html[0]);
33470         this.mainHd.update(html[1]);
33471     },
33472
33473     /**
33474      * Focuses the specified row.
33475      * @param {Number} row The row index
33476      */
33477     focusRow : function(row)
33478     {
33479         //Roo.log('GridView.focusRow');
33480         var x = this.scroller.dom.scrollLeft;
33481         this.focusCell(row, 0, false);
33482         this.scroller.dom.scrollLeft = x;
33483     },
33484
33485     /**
33486      * Focuses the specified cell.
33487      * @param {Number} row The row index
33488      * @param {Number} col The column index
33489      * @param {Boolean} hscroll false to disable horizontal scrolling
33490      */
33491     focusCell : function(row, col, hscroll)
33492     {
33493         //Roo.log('GridView.focusCell');
33494         var el = this.ensureVisible(row, col, hscroll);
33495         this.focusEl.alignTo(el, "tl-tl");
33496         if(Roo.isGecko){
33497             this.focusEl.focus();
33498         }else{
33499             this.focusEl.focus.defer(1, this.focusEl);
33500         }
33501     },
33502
33503     /**
33504      * Scrolls the specified cell into view
33505      * @param {Number} row The row index
33506      * @param {Number} col The column index
33507      * @param {Boolean} hscroll false to disable horizontal scrolling
33508      */
33509     ensureVisible : function(row, col, hscroll)
33510     {
33511         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33512         //return null; //disable for testing.
33513         if(typeof row != "number"){
33514             row = row.rowIndex;
33515         }
33516         if(row < 0 && row >= this.ds.getCount()){
33517             return  null;
33518         }
33519         col = (col !== undefined ? col : 0);
33520         var cm = this.grid.colModel;
33521         while(cm.isHidden(col)){
33522             col++;
33523         }
33524
33525         var el = this.getCell(row, col);
33526         if(!el){
33527             return null;
33528         }
33529         var c = this.scroller.dom;
33530
33531         var ctop = parseInt(el.offsetTop, 10);
33532         var cleft = parseInt(el.offsetLeft, 10);
33533         var cbot = ctop + el.offsetHeight;
33534         var cright = cleft + el.offsetWidth;
33535         
33536         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33537         var stop = parseInt(c.scrollTop, 10);
33538         var sleft = parseInt(c.scrollLeft, 10);
33539         var sbot = stop + ch;
33540         var sright = sleft + c.clientWidth;
33541         /*
33542         Roo.log('GridView.ensureVisible:' +
33543                 ' ctop:' + ctop +
33544                 ' c.clientHeight:' + c.clientHeight +
33545                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33546                 ' stop:' + stop +
33547                 ' cbot:' + cbot +
33548                 ' sbot:' + sbot +
33549                 ' ch:' + ch  
33550                 );
33551         */
33552         if(ctop < stop){
33553              c.scrollTop = ctop;
33554             //Roo.log("set scrolltop to ctop DISABLE?");
33555         }else if(cbot > sbot){
33556             //Roo.log("set scrolltop to cbot-ch");
33557             c.scrollTop = cbot-ch;
33558         }
33559         
33560         if(hscroll !== false){
33561             if(cleft < sleft){
33562                 c.scrollLeft = cleft;
33563             }else if(cright > sright){
33564                 c.scrollLeft = cright-c.clientWidth;
33565             }
33566         }
33567          
33568         return el;
33569     },
33570
33571     updateColumns : function(){
33572         this.grid.stopEditing();
33573         var cm = this.grid.colModel, colIds = this.getColumnIds();
33574         //var totalWidth = cm.getTotalWidth();
33575         var pos = 0;
33576         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33577             //if(cm.isHidden(i)) continue;
33578             var w = cm.getColumnWidth(i);
33579             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33580             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33581         }
33582         this.updateSplitters();
33583     },
33584
33585     generateRules : function(cm){
33586         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33587         Roo.util.CSS.removeStyleSheet(rulesId);
33588         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33589             var cid = cm.getColumnId(i);
33590             var align = '';
33591             if(cm.config[i].align){
33592                 align = 'text-align:'+cm.config[i].align+';';
33593             }
33594             var hidden = '';
33595             if(cm.isHidden(i)){
33596                 hidden = 'display:none;';
33597             }
33598             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33599             ruleBuf.push(
33600                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33601                     this.hdSelector, cid, " {\n", align, width, "}\n",
33602                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33603                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33604         }
33605         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33606     },
33607
33608     updateSplitters : function(){
33609         var cm = this.cm, s = this.getSplitters();
33610         if(s){ // splitters not created yet
33611             var pos = 0, locked = true;
33612             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33613                 if(cm.isHidden(i)) continue;
33614                 var w = cm.getColumnWidth(i); // make sure it's a number
33615                 if(!cm.isLocked(i) && locked){
33616                     pos = 0;
33617                     locked = false;
33618                 }
33619                 pos += w;
33620                 s[i].style.left = (pos-this.splitOffset) + "px";
33621             }
33622         }
33623     },
33624
33625     handleHiddenChange : function(colModel, colIndex, hidden){
33626         if(hidden){
33627             this.hideColumn(colIndex);
33628         }else{
33629             this.unhideColumn(colIndex);
33630         }
33631     },
33632
33633     hideColumn : function(colIndex){
33634         var cid = this.getColumnId(colIndex);
33635         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33636         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33637         if(Roo.isSafari){
33638             this.updateHeaders();
33639         }
33640         this.updateSplitters();
33641         this.layout();
33642     },
33643
33644     unhideColumn : function(colIndex){
33645         var cid = this.getColumnId(colIndex);
33646         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33647         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33648
33649         if(Roo.isSafari){
33650             this.updateHeaders();
33651         }
33652         this.updateSplitters();
33653         this.layout();
33654     },
33655
33656     insertRows : function(dm, firstRow, lastRow, isUpdate){
33657         if(firstRow == 0 && lastRow == dm.getCount()-1){
33658             this.refresh();
33659         }else{
33660             if(!isUpdate){
33661                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33662             }
33663             var s = this.getScrollState();
33664             var markup = this.renderRows(firstRow, lastRow);
33665             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33666             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33667             this.restoreScroll(s);
33668             if(!isUpdate){
33669                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33670                 this.syncRowHeights(firstRow, lastRow);
33671                 this.stripeRows(firstRow);
33672                 this.layout();
33673             }
33674         }
33675     },
33676
33677     bufferRows : function(markup, target, index){
33678         var before = null, trows = target.rows, tbody = target.tBodies[0];
33679         if(index < trows.length){
33680             before = trows[index];
33681         }
33682         var b = document.createElement("div");
33683         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33684         var rows = b.firstChild.rows;
33685         for(var i = 0, len = rows.length; i < len; i++){
33686             if(before){
33687                 tbody.insertBefore(rows[0], before);
33688             }else{
33689                 tbody.appendChild(rows[0]);
33690             }
33691         }
33692         b.innerHTML = "";
33693         b = null;
33694     },
33695
33696     deleteRows : function(dm, firstRow, lastRow){
33697         if(dm.getRowCount()<1){
33698             this.fireEvent("beforerefresh", this);
33699             this.mainBody.update("");
33700             this.lockedBody.update("");
33701             this.fireEvent("refresh", this);
33702         }else{
33703             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33704             var bt = this.getBodyTable();
33705             var tbody = bt.firstChild;
33706             var rows = bt.rows;
33707             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33708                 tbody.removeChild(rows[firstRow]);
33709             }
33710             this.stripeRows(firstRow);
33711             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33712         }
33713     },
33714
33715     updateRows : function(dataSource, firstRow, lastRow){
33716         var s = this.getScrollState();
33717         this.refresh();
33718         this.restoreScroll(s);
33719     },
33720
33721     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33722         if(!noRefresh){
33723            this.refresh();
33724         }
33725         this.updateHeaderSortState();
33726     },
33727
33728     getScrollState : function(){
33729         
33730         var sb = this.scroller.dom;
33731         return {left: sb.scrollLeft, top: sb.scrollTop};
33732     },
33733
33734     stripeRows : function(startRow){
33735         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33736             return;
33737         }
33738         startRow = startRow || 0;
33739         var rows = this.getBodyTable().rows;
33740         var lrows = this.getLockedTable().rows;
33741         var cls = ' x-grid-row-alt ';
33742         for(var i = startRow, len = rows.length; i < len; i++){
33743             var row = rows[i], lrow = lrows[i];
33744             var isAlt = ((i+1) % 2 == 0);
33745             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33746             if(isAlt == hasAlt){
33747                 continue;
33748             }
33749             if(isAlt){
33750                 row.className += " x-grid-row-alt";
33751             }else{
33752                 row.className = row.className.replace("x-grid-row-alt", "");
33753             }
33754             if(lrow){
33755                 lrow.className = row.className;
33756             }
33757         }
33758     },
33759
33760     restoreScroll : function(state){
33761         //Roo.log('GridView.restoreScroll');
33762         var sb = this.scroller.dom;
33763         sb.scrollLeft = state.left;
33764         sb.scrollTop = state.top;
33765         this.syncScroll();
33766     },
33767
33768     syncScroll : function(){
33769         //Roo.log('GridView.syncScroll');
33770         var sb = this.scroller.dom;
33771         var sh = this.mainHd.dom;
33772         var bs = this.mainBody.dom;
33773         var lv = this.lockedBody.dom;
33774         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33775         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33776     },
33777
33778     handleScroll : function(e){
33779         this.syncScroll();
33780         var sb = this.scroller.dom;
33781         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33782         e.stopEvent();
33783     },
33784
33785     handleWheel : function(e){
33786         var d = e.getWheelDelta();
33787         this.scroller.dom.scrollTop -= d*22;
33788         // set this here to prevent jumpy scrolling on large tables
33789         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33790         e.stopEvent();
33791     },
33792
33793     renderRows : function(startRow, endRow){
33794         // pull in all the crap needed to render rows
33795         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33796         var colCount = cm.getColumnCount();
33797
33798         if(ds.getCount() < 1){
33799             return ["", ""];
33800         }
33801
33802         // build a map for all the columns
33803         var cs = [];
33804         for(var i = 0; i < colCount; i++){
33805             var name = cm.getDataIndex(i);
33806             cs[i] = {
33807                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33808                 renderer : cm.getRenderer(i),
33809                 id : cm.getColumnId(i),
33810                 locked : cm.isLocked(i)
33811             };
33812         }
33813
33814         startRow = startRow || 0;
33815         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33816
33817         // records to render
33818         var rs = ds.getRange(startRow, endRow);
33819
33820         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33821     },
33822
33823     // As much as I hate to duplicate code, this was branched because FireFox really hates
33824     // [].join("") on strings. The performance difference was substantial enough to
33825     // branch this function
33826     doRender : Roo.isGecko ?
33827             function(cs, rs, ds, startRow, colCount, stripe){
33828                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33829                 // buffers
33830                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33831                 
33832                 var hasListener = this.grid.hasListener('rowclass');
33833                 var rowcfg = {};
33834                 for(var j = 0, len = rs.length; j < len; j++){
33835                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33836                     for(var i = 0; i < colCount; i++){
33837                         c = cs[i];
33838                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33839                         p.id = c.id;
33840                         p.css = p.attr = "";
33841                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33842                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33843                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33844                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33845                         }
33846                         var markup = ct.apply(p);
33847                         if(!c.locked){
33848                             cb+= markup;
33849                         }else{
33850                             lcb+= markup;
33851                         }
33852                     }
33853                     var alt = [];
33854                     if(stripe && ((rowIndex+1) % 2 == 0)){
33855                         alt.push("x-grid-row-alt")
33856                     }
33857                     if(r.dirty){
33858                         alt.push(  " x-grid-dirty-row");
33859                     }
33860                     rp.cells = lcb;
33861                     if(this.getRowClass){
33862                         alt.push(this.getRowClass(r, rowIndex));
33863                     }
33864                     if (hasListener) {
33865                         rowcfg = {
33866                              
33867                             record: r,
33868                             rowIndex : rowIndex,
33869                             rowClass : ''
33870                         }
33871                         this.grid.fireEvent('rowclass', this, rowcfg);
33872                         alt.push(rowcfg.rowClass);
33873                     }
33874                     rp.alt = alt.join(" ");
33875                     lbuf+= rt.apply(rp);
33876                     rp.cells = cb;
33877                     buf+=  rt.apply(rp);
33878                 }
33879                 return [lbuf, buf];
33880             } :
33881             function(cs, rs, ds, startRow, colCount, stripe){
33882                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33883                 // buffers
33884                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33885                 var hasListener = this.grid.hasListener('rowclass');
33886                 var rowcfg = {};
33887                 for(var j = 0, len = rs.length; j < len; j++){
33888                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33889                     for(var i = 0; i < colCount; i++){
33890                         c = cs[i];
33891                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33892                         p.id = c.id;
33893                         p.css = p.attr = "";
33894                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33895                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33896                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33897                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33898                         }
33899                         var markup = ct.apply(p);
33900                         if(!c.locked){
33901                             cb[cb.length] = markup;
33902                         }else{
33903                             lcb[lcb.length] = markup;
33904                         }
33905                     }
33906                     var alt = [];
33907                     if(stripe && ((rowIndex+1) % 2 == 0)){
33908                         alt.push( "x-grid-row-alt");
33909                     }
33910                     if(r.dirty){
33911                         alt.push(" x-grid-dirty-row");
33912                     }
33913                     rp.cells = lcb;
33914                     if(this.getRowClass){
33915                         alt.push( this.getRowClass(r, rowIndex));
33916                     }
33917                     if (hasListener) {
33918                         rowcfg = {
33919                              
33920                             record: r,
33921                             rowIndex : rowIndex,
33922                             rowClass : ''
33923                         }
33924                         this.grid.fireEvent('rowclass', this, rowcfg);
33925                         alt.push(rowcfg.rowClass);
33926                     }
33927                     rp.alt = alt.join(" ");
33928                     rp.cells = lcb.join("");
33929                     lbuf[lbuf.length] = rt.apply(rp);
33930                     rp.cells = cb.join("");
33931                     buf[buf.length] =  rt.apply(rp);
33932                 }
33933                 return [lbuf.join(""), buf.join("")];
33934             },
33935
33936     renderBody : function(){
33937         var markup = this.renderRows();
33938         var bt = this.templates.body;
33939         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33940     },
33941
33942     /**
33943      * Refreshes the grid
33944      * @param {Boolean} headersToo
33945      */
33946     refresh : function(headersToo){
33947         this.fireEvent("beforerefresh", this);
33948         this.grid.stopEditing();
33949         var result = this.renderBody();
33950         this.lockedBody.update(result[0]);
33951         this.mainBody.update(result[1]);
33952         if(headersToo === true){
33953             this.updateHeaders();
33954             this.updateColumns();
33955             this.updateSplitters();
33956             this.updateHeaderSortState();
33957         }
33958         this.syncRowHeights();
33959         this.layout();
33960         this.fireEvent("refresh", this);
33961     },
33962
33963     handleColumnMove : function(cm, oldIndex, newIndex){
33964         this.indexMap = null;
33965         var s = this.getScrollState();
33966         this.refresh(true);
33967         this.restoreScroll(s);
33968         this.afterMove(newIndex);
33969     },
33970
33971     afterMove : function(colIndex){
33972         if(this.enableMoveAnim && Roo.enableFx){
33973             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33974         }
33975         // if multisort - fix sortOrder, and reload..
33976         if (this.grid.dataSource.multiSort) {
33977             // the we can call sort again..
33978             var dm = this.grid.dataSource;
33979             var cm = this.grid.colModel;
33980             var so = [];
33981             for(var i = 0; i < cm.config.length; i++ ) {
33982                 
33983                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
33984                     continue; // dont' bother, it's not in sort list or being set.
33985                 }
33986                 
33987                 so.push(cm.config[i].dataIndex);
33988             };
33989             dm.sortOrder = so;
33990             dm.load(dm.lastOptions);
33991             
33992             
33993         }
33994         
33995     },
33996
33997     updateCell : function(dm, rowIndex, dataIndex){
33998         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33999         if(typeof colIndex == "undefined"){ // not present in grid
34000             return;
34001         }
34002         var cm = this.grid.colModel;
34003         var cell = this.getCell(rowIndex, colIndex);
34004         var cellText = this.getCellText(rowIndex, colIndex);
34005
34006         var p = {
34007             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34008             id : cm.getColumnId(colIndex),
34009             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34010         };
34011         var renderer = cm.getRenderer(colIndex);
34012         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34013         if(typeof val == "undefined" || val === "") val = "&#160;";
34014         cellText.innerHTML = val;
34015         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34016         this.syncRowHeights(rowIndex, rowIndex);
34017     },
34018
34019     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34020         var maxWidth = 0;
34021         if(this.grid.autoSizeHeaders){
34022             var h = this.getHeaderCellMeasure(colIndex);
34023             maxWidth = Math.max(maxWidth, h.scrollWidth);
34024         }
34025         var tb, index;
34026         if(this.cm.isLocked(colIndex)){
34027             tb = this.getLockedTable();
34028             index = colIndex;
34029         }else{
34030             tb = this.getBodyTable();
34031             index = colIndex - this.cm.getLockedCount();
34032         }
34033         if(tb && tb.rows){
34034             var rows = tb.rows;
34035             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34036             for(var i = 0; i < stopIndex; i++){
34037                 var cell = rows[i].childNodes[index].firstChild;
34038                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34039             }
34040         }
34041         return maxWidth + /*margin for error in IE*/ 5;
34042     },
34043     /**
34044      * Autofit a column to its content.
34045      * @param {Number} colIndex
34046      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34047      */
34048      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34049          if(this.cm.isHidden(colIndex)){
34050              return; // can't calc a hidden column
34051          }
34052         if(forceMinSize){
34053             var cid = this.cm.getColumnId(colIndex);
34054             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34055            if(this.grid.autoSizeHeaders){
34056                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34057            }
34058         }
34059         var newWidth = this.calcColumnWidth(colIndex);
34060         this.cm.setColumnWidth(colIndex,
34061             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34062         if(!suppressEvent){
34063             this.grid.fireEvent("columnresize", colIndex, newWidth);
34064         }
34065     },
34066
34067     /**
34068      * Autofits all columns to their content and then expands to fit any extra space in the grid
34069      */
34070      autoSizeColumns : function(){
34071         var cm = this.grid.colModel;
34072         var colCount = cm.getColumnCount();
34073         for(var i = 0; i < colCount; i++){
34074             this.autoSizeColumn(i, true, true);
34075         }
34076         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34077             this.fitColumns();
34078         }else{
34079             this.updateColumns();
34080             this.layout();
34081         }
34082     },
34083
34084     /**
34085      * Autofits all columns to the grid's width proportionate with their current size
34086      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34087      */
34088     fitColumns : function(reserveScrollSpace){
34089         var cm = this.grid.colModel;
34090         var colCount = cm.getColumnCount();
34091         var cols = [];
34092         var width = 0;
34093         var i, w;
34094         for (i = 0; i < colCount; i++){
34095             if(!cm.isHidden(i) && !cm.isFixed(i)){
34096                 w = cm.getColumnWidth(i);
34097                 cols.push(i);
34098                 cols.push(w);
34099                 width += w;
34100             }
34101         }
34102         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34103         if(reserveScrollSpace){
34104             avail -= 17;
34105         }
34106         var frac = (avail - cm.getTotalWidth())/width;
34107         while (cols.length){
34108             w = cols.pop();
34109             i = cols.pop();
34110             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34111         }
34112         this.updateColumns();
34113         this.layout();
34114     },
34115
34116     onRowSelect : function(rowIndex){
34117         var row = this.getRowComposite(rowIndex);
34118         row.addClass("x-grid-row-selected");
34119     },
34120
34121     onRowDeselect : function(rowIndex){
34122         var row = this.getRowComposite(rowIndex);
34123         row.removeClass("x-grid-row-selected");
34124     },
34125
34126     onCellSelect : function(row, col){
34127         var cell = this.getCell(row, col);
34128         if(cell){
34129             Roo.fly(cell).addClass("x-grid-cell-selected");
34130         }
34131     },
34132
34133     onCellDeselect : function(row, col){
34134         var cell = this.getCell(row, col);
34135         if(cell){
34136             Roo.fly(cell).removeClass("x-grid-cell-selected");
34137         }
34138     },
34139
34140     updateHeaderSortState : function(){
34141         
34142         // sort state can be single { field: xxx, direction : yyy}
34143         // or   { xxx=>ASC , yyy : DESC ..... }
34144         
34145         var mstate = {};
34146         if (!this.ds.multiSort) { 
34147             var state = this.ds.getSortState();
34148             if(!state){
34149                 return;
34150             }
34151             mstate[state.field] = state.direction;
34152             // FIXME... - this is not used here.. but might be elsewhere..
34153             this.sortState = state;
34154             
34155         } else {
34156             mstate = this.ds.sortToggle;
34157         }
34158         //remove existing sort classes..
34159         
34160         var sc = this.sortClasses;
34161         var hds = this.el.select(this.headerSelector).removeClass(sc);
34162         
34163         for(var f in mstate) {
34164         
34165             var sortColumn = this.cm.findColumnIndex(f);
34166             
34167             if(sortColumn != -1){
34168                 var sortDir = mstate[f];        
34169                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34170             }
34171         }
34172         
34173          
34174         
34175     },
34176
34177
34178     handleHeaderClick : function(g, index){
34179         if(this.headersDisabled){
34180             return;
34181         }
34182         var dm = g.dataSource, cm = g.colModel;
34183         if(!cm.isSortable(index)){
34184             return;
34185         }
34186         g.stopEditing();
34187         
34188         if (dm.multiSort) {
34189             // update the sortOrder
34190             var so = [];
34191             for(var i = 0; i < cm.config.length; i++ ) {
34192                 
34193                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34194                     continue; // dont' bother, it's not in sort list or being set.
34195                 }
34196                 
34197                 so.push(cm.config[i].dataIndex);
34198             };
34199             dm.sortOrder = so;
34200         }
34201         
34202         
34203         dm.sort(cm.getDataIndex(index));
34204     },
34205
34206
34207     destroy : function(){
34208         if(this.colMenu){
34209             this.colMenu.removeAll();
34210             Roo.menu.MenuMgr.unregister(this.colMenu);
34211             this.colMenu.getEl().remove();
34212             delete this.colMenu;
34213         }
34214         if(this.hmenu){
34215             this.hmenu.removeAll();
34216             Roo.menu.MenuMgr.unregister(this.hmenu);
34217             this.hmenu.getEl().remove();
34218             delete this.hmenu;
34219         }
34220         if(this.grid.enableColumnMove){
34221             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34222             if(dds){
34223                 for(var dd in dds){
34224                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34225                         var elid = dds[dd].dragElId;
34226                         dds[dd].unreg();
34227                         Roo.get(elid).remove();
34228                     } else if(dds[dd].config.isTarget){
34229                         dds[dd].proxyTop.remove();
34230                         dds[dd].proxyBottom.remove();
34231                         dds[dd].unreg();
34232                     }
34233                     if(Roo.dd.DDM.locationCache[dd]){
34234                         delete Roo.dd.DDM.locationCache[dd];
34235                     }
34236                 }
34237                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34238             }
34239         }
34240         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34241         this.bind(null, null);
34242         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34243     },
34244
34245     handleLockChange : function(){
34246         this.refresh(true);
34247     },
34248
34249     onDenyColumnLock : function(){
34250
34251     },
34252
34253     onDenyColumnHide : function(){
34254
34255     },
34256
34257     handleHdMenuClick : function(item){
34258         var index = this.hdCtxIndex;
34259         var cm = this.cm, ds = this.ds;
34260         switch(item.id){
34261             case "asc":
34262                 ds.sort(cm.getDataIndex(index), "ASC");
34263                 break;
34264             case "desc":
34265                 ds.sort(cm.getDataIndex(index), "DESC");
34266                 break;
34267             case "lock":
34268                 var lc = cm.getLockedCount();
34269                 if(cm.getColumnCount(true) <= lc+1){
34270                     this.onDenyColumnLock();
34271                     return;
34272                 }
34273                 if(lc != index){
34274                     cm.setLocked(index, true, true);
34275                     cm.moveColumn(index, lc);
34276                     this.grid.fireEvent("columnmove", index, lc);
34277                 }else{
34278                     cm.setLocked(index, true);
34279                 }
34280             break;
34281             case "unlock":
34282                 var lc = cm.getLockedCount();
34283                 if((lc-1) != index){
34284                     cm.setLocked(index, false, true);
34285                     cm.moveColumn(index, lc-1);
34286                     this.grid.fireEvent("columnmove", index, lc-1);
34287                 }else{
34288                     cm.setLocked(index, false);
34289                 }
34290             break;
34291             default:
34292                 index = cm.getIndexById(item.id.substr(4));
34293                 if(index != -1){
34294                     if(item.checked && cm.getColumnCount(true) <= 1){
34295                         this.onDenyColumnHide();
34296                         return false;
34297                     }
34298                     cm.setHidden(index, item.checked);
34299                 }
34300         }
34301         return true;
34302     },
34303
34304     beforeColMenuShow : function(){
34305         var cm = this.cm,  colCount = cm.getColumnCount();
34306         this.colMenu.removeAll();
34307         for(var i = 0; i < colCount; i++){
34308             this.colMenu.add(new Roo.menu.CheckItem({
34309                 id: "col-"+cm.getColumnId(i),
34310                 text: cm.getColumnHeader(i),
34311                 checked: !cm.isHidden(i),
34312                 hideOnClick:false
34313             }));
34314         }
34315     },
34316
34317     handleHdCtx : function(g, index, e){
34318         e.stopEvent();
34319         var hd = this.getHeaderCell(index);
34320         this.hdCtxIndex = index;
34321         var ms = this.hmenu.items, cm = this.cm;
34322         ms.get("asc").setDisabled(!cm.isSortable(index));
34323         ms.get("desc").setDisabled(!cm.isSortable(index));
34324         if(this.grid.enableColLock !== false){
34325             ms.get("lock").setDisabled(cm.isLocked(index));
34326             ms.get("unlock").setDisabled(!cm.isLocked(index));
34327         }
34328         this.hmenu.show(hd, "tl-bl");
34329     },
34330
34331     handleHdOver : function(e){
34332         var hd = this.findHeaderCell(e.getTarget());
34333         if(hd && !this.headersDisabled){
34334             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34335                this.fly(hd).addClass("x-grid-hd-over");
34336             }
34337         }
34338     },
34339
34340     handleHdOut : function(e){
34341         var hd = this.findHeaderCell(e.getTarget());
34342         if(hd){
34343             this.fly(hd).removeClass("x-grid-hd-over");
34344         }
34345     },
34346
34347     handleSplitDblClick : function(e, t){
34348         var i = this.getCellIndex(t);
34349         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34350             this.autoSizeColumn(i, true);
34351             this.layout();
34352         }
34353     },
34354
34355     render : function(){
34356
34357         var cm = this.cm;
34358         var colCount = cm.getColumnCount();
34359
34360         if(this.grid.monitorWindowResize === true){
34361             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34362         }
34363         var header = this.renderHeaders();
34364         var body = this.templates.body.apply({rows:""});
34365         var html = this.templates.master.apply({
34366             lockedBody: body,
34367             body: body,
34368             lockedHeader: header[0],
34369             header: header[1]
34370         });
34371
34372         //this.updateColumns();
34373
34374         this.grid.getGridEl().dom.innerHTML = html;
34375
34376         this.initElements();
34377         
34378         // a kludge to fix the random scolling effect in webkit
34379         this.el.on("scroll", function() {
34380             this.el.dom.scrollTop=0; // hopefully not recursive..
34381         },this);
34382
34383         this.scroller.on("scroll", this.handleScroll, this);
34384         this.lockedBody.on("mousewheel", this.handleWheel, this);
34385         this.mainBody.on("mousewheel", this.handleWheel, this);
34386
34387         this.mainHd.on("mouseover", this.handleHdOver, this);
34388         this.mainHd.on("mouseout", this.handleHdOut, this);
34389         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34390                 {delegate: "."+this.splitClass});
34391
34392         this.lockedHd.on("mouseover", this.handleHdOver, this);
34393         this.lockedHd.on("mouseout", this.handleHdOut, this);
34394         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34395                 {delegate: "."+this.splitClass});
34396
34397         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34398             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34399         }
34400
34401         this.updateSplitters();
34402
34403         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34404             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34405             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34406         }
34407
34408         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34409             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34410             this.hmenu.add(
34411                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34412                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34413             );
34414             if(this.grid.enableColLock !== false){
34415                 this.hmenu.add('-',
34416                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34417                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34418                 );
34419             }
34420             if(this.grid.enableColumnHide !== false){
34421
34422                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34423                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34424                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34425
34426                 this.hmenu.add('-',
34427                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34428                 );
34429             }
34430             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34431
34432             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34433         }
34434
34435         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34436             this.dd = new Roo.grid.GridDragZone(this.grid, {
34437                 ddGroup : this.grid.ddGroup || 'GridDD'
34438             });
34439         }
34440
34441         /*
34442         for(var i = 0; i < colCount; i++){
34443             if(cm.isHidden(i)){
34444                 this.hideColumn(i);
34445             }
34446             if(cm.config[i].align){
34447                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34448                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34449             }
34450         }*/
34451         
34452         this.updateHeaderSortState();
34453
34454         this.beforeInitialResize();
34455         this.layout(true);
34456
34457         // two part rendering gives faster view to the user
34458         this.renderPhase2.defer(1, this);
34459     },
34460
34461     renderPhase2 : function(){
34462         // render the rows now
34463         this.refresh();
34464         if(this.grid.autoSizeColumns){
34465             this.autoSizeColumns();
34466         }
34467     },
34468
34469     beforeInitialResize : function(){
34470
34471     },
34472
34473     onColumnSplitterMoved : function(i, w){
34474         this.userResized = true;
34475         var cm = this.grid.colModel;
34476         cm.setColumnWidth(i, w, true);
34477         var cid = cm.getColumnId(i);
34478         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34479         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34480         this.updateSplitters();
34481         this.layout();
34482         this.grid.fireEvent("columnresize", i, w);
34483     },
34484
34485     syncRowHeights : function(startIndex, endIndex){
34486         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34487             startIndex = startIndex || 0;
34488             var mrows = this.getBodyTable().rows;
34489             var lrows = this.getLockedTable().rows;
34490             var len = mrows.length-1;
34491             endIndex = Math.min(endIndex || len, len);
34492             for(var i = startIndex; i <= endIndex; i++){
34493                 var m = mrows[i], l = lrows[i];
34494                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34495                 m.style.height = l.style.height = h + "px";
34496             }
34497         }
34498     },
34499
34500     layout : function(initialRender, is2ndPass){
34501         var g = this.grid;
34502         var auto = g.autoHeight;
34503         var scrollOffset = 16;
34504         var c = g.getGridEl(), cm = this.cm,
34505                 expandCol = g.autoExpandColumn,
34506                 gv = this;
34507         //c.beginMeasure();
34508
34509         if(!c.dom.offsetWidth){ // display:none?
34510             if(initialRender){
34511                 this.lockedWrap.show();
34512                 this.mainWrap.show();
34513             }
34514             return;
34515         }
34516
34517         var hasLock = this.cm.isLocked(0);
34518
34519         var tbh = this.headerPanel.getHeight();
34520         var bbh = this.footerPanel.getHeight();
34521
34522         if(auto){
34523             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34524             var newHeight = ch + c.getBorderWidth("tb");
34525             if(g.maxHeight){
34526                 newHeight = Math.min(g.maxHeight, newHeight);
34527             }
34528             c.setHeight(newHeight);
34529         }
34530
34531         if(g.autoWidth){
34532             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34533         }
34534
34535         var s = this.scroller;
34536
34537         var csize = c.getSize(true);
34538
34539         this.el.setSize(csize.width, csize.height);
34540
34541         this.headerPanel.setWidth(csize.width);
34542         this.footerPanel.setWidth(csize.width);
34543
34544         var hdHeight = this.mainHd.getHeight();
34545         var vw = csize.width;
34546         var vh = csize.height - (tbh + bbh);
34547
34548         s.setSize(vw, vh);
34549
34550         var bt = this.getBodyTable();
34551         var ltWidth = hasLock ?
34552                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34553
34554         var scrollHeight = bt.offsetHeight;
34555         var scrollWidth = ltWidth + bt.offsetWidth;
34556         var vscroll = false, hscroll = false;
34557
34558         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34559
34560         var lw = this.lockedWrap, mw = this.mainWrap;
34561         var lb = this.lockedBody, mb = this.mainBody;
34562
34563         setTimeout(function(){
34564             var t = s.dom.offsetTop;
34565             var w = s.dom.clientWidth,
34566                 h = s.dom.clientHeight;
34567
34568             lw.setTop(t);
34569             lw.setSize(ltWidth, h);
34570
34571             mw.setLeftTop(ltWidth, t);
34572             mw.setSize(w-ltWidth, h);
34573
34574             lb.setHeight(h-hdHeight);
34575             mb.setHeight(h-hdHeight);
34576
34577             if(is2ndPass !== true && !gv.userResized && expandCol){
34578                 // high speed resize without full column calculation
34579                 
34580                 var ci = cm.getIndexById(expandCol);
34581                 if (ci < 0) {
34582                     ci = cm.findColumnIndex(expandCol);
34583                 }
34584                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34585                 var expandId = cm.getColumnId(ci);
34586                 var  tw = cm.getTotalWidth(false);
34587                 var currentWidth = cm.getColumnWidth(ci);
34588                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34589                 if(currentWidth != cw){
34590                     cm.setColumnWidth(ci, cw, true);
34591                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34592                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34593                     gv.updateSplitters();
34594                     gv.layout(false, true);
34595                 }
34596             }
34597
34598             if(initialRender){
34599                 lw.show();
34600                 mw.show();
34601             }
34602             //c.endMeasure();
34603         }, 10);
34604     },
34605
34606     onWindowResize : function(){
34607         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34608             return;
34609         }
34610         this.layout();
34611     },
34612
34613     appendFooter : function(parentEl){
34614         return null;
34615     },
34616
34617     sortAscText : "Sort Ascending",
34618     sortDescText : "Sort Descending",
34619     lockText : "Lock Column",
34620     unlockText : "Unlock Column",
34621     columnsText : "Columns"
34622 });
34623
34624
34625 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34626     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34627     this.proxy.el.addClass('x-grid3-col-dd');
34628 };
34629
34630 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34631     handleMouseDown : function(e){
34632
34633     },
34634
34635     callHandleMouseDown : function(e){
34636         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34637     }
34638 });
34639 /*
34640  * Based on:
34641  * Ext JS Library 1.1.1
34642  * Copyright(c) 2006-2007, Ext JS, LLC.
34643  *
34644  * Originally Released Under LGPL - original licence link has changed is not relivant.
34645  *
34646  * Fork - LGPL
34647  * <script type="text/javascript">
34648  */
34649  
34650 // private
34651 // This is a support class used internally by the Grid components
34652 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34653     this.grid = grid;
34654     this.view = grid.getView();
34655     this.proxy = this.view.resizeProxy;
34656     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34657         "gridSplitters" + this.grid.getGridEl().id, {
34658         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34659     });
34660     this.setHandleElId(Roo.id(hd));
34661     this.setOuterHandleElId(Roo.id(hd2));
34662     this.scroll = false;
34663 };
34664 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34665     fly: Roo.Element.fly,
34666
34667     b4StartDrag : function(x, y){
34668         this.view.headersDisabled = true;
34669         this.proxy.setHeight(this.view.mainWrap.getHeight());
34670         var w = this.cm.getColumnWidth(this.cellIndex);
34671         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34672         this.resetConstraints();
34673         this.setXConstraint(minw, 1000);
34674         this.setYConstraint(0, 0);
34675         this.minX = x - minw;
34676         this.maxX = x + 1000;
34677         this.startPos = x;
34678         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34679     },
34680
34681
34682     handleMouseDown : function(e){
34683         ev = Roo.EventObject.setEvent(e);
34684         var t = this.fly(ev.getTarget());
34685         if(t.hasClass("x-grid-split")){
34686             this.cellIndex = this.view.getCellIndex(t.dom);
34687             this.split = t.dom;
34688             this.cm = this.grid.colModel;
34689             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34690                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34691             }
34692         }
34693     },
34694
34695     endDrag : function(e){
34696         this.view.headersDisabled = false;
34697         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34698         var diff = endX - this.startPos;
34699         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34700     },
34701
34702     autoOffset : function(){
34703         this.setDelta(0,0);
34704     }
34705 });/*
34706  * Based on:
34707  * Ext JS Library 1.1.1
34708  * Copyright(c) 2006-2007, Ext JS, LLC.
34709  *
34710  * Originally Released Under LGPL - original licence link has changed is not relivant.
34711  *
34712  * Fork - LGPL
34713  * <script type="text/javascript">
34714  */
34715  
34716 // private
34717 // This is a support class used internally by the Grid components
34718 Roo.grid.GridDragZone = function(grid, config){
34719     this.view = grid.getView();
34720     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34721     if(this.view.lockedBody){
34722         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34723         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34724     }
34725     this.scroll = false;
34726     this.grid = grid;
34727     this.ddel = document.createElement('div');
34728     this.ddel.className = 'x-grid-dd-wrap';
34729 };
34730
34731 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34732     ddGroup : "GridDD",
34733
34734     getDragData : function(e){
34735         var t = Roo.lib.Event.getTarget(e);
34736         var rowIndex = this.view.findRowIndex(t);
34737         if(rowIndex !== false){
34738             var sm = this.grid.selModel;
34739             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34740               //  sm.mouseDown(e, t);
34741             //}
34742             if (e.hasModifier()){
34743                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34744             }
34745             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34746         }
34747         return false;
34748     },
34749
34750     onInitDrag : function(e){
34751         var data = this.dragData;
34752         this.ddel.innerHTML = this.grid.getDragDropText();
34753         this.proxy.update(this.ddel);
34754         // fire start drag?
34755     },
34756
34757     afterRepair : function(){
34758         this.dragging = false;
34759     },
34760
34761     getRepairXY : function(e, data){
34762         return false;
34763     },
34764
34765     onEndDrag : function(data, e){
34766         // fire end drag?
34767     },
34768
34769     onValidDrop : function(dd, e, id){
34770         // fire drag drop?
34771         this.hideProxy();
34772     },
34773
34774     beforeInvalidDrop : function(e, id){
34775
34776     }
34777 });/*
34778  * Based on:
34779  * Ext JS Library 1.1.1
34780  * Copyright(c) 2006-2007, Ext JS, LLC.
34781  *
34782  * Originally Released Under LGPL - original licence link has changed is not relivant.
34783  *
34784  * Fork - LGPL
34785  * <script type="text/javascript">
34786  */
34787  
34788
34789 /**
34790  * @class Roo.grid.ColumnModel
34791  * @extends Roo.util.Observable
34792  * This is the default implementation of a ColumnModel used by the Grid. It defines
34793  * the columns in the grid.
34794  * <br>Usage:<br>
34795  <pre><code>
34796  var colModel = new Roo.grid.ColumnModel([
34797         {header: "Ticker", width: 60, sortable: true, locked: true},
34798         {header: "Company Name", width: 150, sortable: true},
34799         {header: "Market Cap.", width: 100, sortable: true},
34800         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34801         {header: "Employees", width: 100, sortable: true, resizable: false}
34802  ]);
34803  </code></pre>
34804  * <p>
34805  
34806  * The config options listed for this class are options which may appear in each
34807  * individual column definition.
34808  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34809  * @constructor
34810  * @param {Object} config An Array of column config objects. See this class's
34811  * config objects for details.
34812 */
34813 Roo.grid.ColumnModel = function(config){
34814         /**
34815      * The config passed into the constructor
34816      */
34817     this.config = config;
34818     this.lookup = {};
34819
34820     // if no id, create one
34821     // if the column does not have a dataIndex mapping,
34822     // map it to the order it is in the config
34823     for(var i = 0, len = config.length; i < len; i++){
34824         var c = config[i];
34825         if(typeof c.dataIndex == "undefined"){
34826             c.dataIndex = i;
34827         }
34828         if(typeof c.renderer == "string"){
34829             c.renderer = Roo.util.Format[c.renderer];
34830         }
34831         if(typeof c.id == "undefined"){
34832             c.id = Roo.id();
34833         }
34834         if(c.editor && c.editor.xtype){
34835             c.editor  = Roo.factory(c.editor, Roo.grid);
34836         }
34837         if(c.editor && c.editor.isFormField){
34838             c.editor = new Roo.grid.GridEditor(c.editor);
34839         }
34840         this.lookup[c.id] = c;
34841     }
34842
34843     /**
34844      * The width of columns which have no width specified (defaults to 100)
34845      * @type Number
34846      */
34847     this.defaultWidth = 100;
34848
34849     /**
34850      * Default sortable of columns which have no sortable specified (defaults to false)
34851      * @type Boolean
34852      */
34853     this.defaultSortable = false;
34854
34855     this.addEvents({
34856         /**
34857              * @event widthchange
34858              * Fires when the width of a column changes.
34859              * @param {ColumnModel} this
34860              * @param {Number} columnIndex The column index
34861              * @param {Number} newWidth The new width
34862              */
34863             "widthchange": true,
34864         /**
34865              * @event headerchange
34866              * Fires when the text of a header changes.
34867              * @param {ColumnModel} this
34868              * @param {Number} columnIndex The column index
34869              * @param {Number} newText The new header text
34870              */
34871             "headerchange": true,
34872         /**
34873              * @event hiddenchange
34874              * Fires when a column is hidden or "unhidden".
34875              * @param {ColumnModel} this
34876              * @param {Number} columnIndex The column index
34877              * @param {Boolean} hidden true if hidden, false otherwise
34878              */
34879             "hiddenchange": true,
34880             /**
34881          * @event columnmoved
34882          * Fires when a column is moved.
34883          * @param {ColumnModel} this
34884          * @param {Number} oldIndex
34885          * @param {Number} newIndex
34886          */
34887         "columnmoved" : true,
34888         /**
34889          * @event columlockchange
34890          * Fires when a column's locked state is changed
34891          * @param {ColumnModel} this
34892          * @param {Number} colIndex
34893          * @param {Boolean} locked true if locked
34894          */
34895         "columnlockchange" : true
34896     });
34897     Roo.grid.ColumnModel.superclass.constructor.call(this);
34898 };
34899 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34900     /**
34901      * @cfg {String} header The header text to display in the Grid view.
34902      */
34903     /**
34904      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34905      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34906      * specified, the column's index is used as an index into the Record's data Array.
34907      */
34908     /**
34909      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34910      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34911      */
34912     /**
34913      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34914      * Defaults to the value of the {@link #defaultSortable} property.
34915      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34916      */
34917     /**
34918      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34919      */
34920     /**
34921      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34922      */
34923     /**
34924      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34925      */
34926     /**
34927      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34928      */
34929     /**
34930      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34931      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34932      * default renderer uses the raw data value.
34933      */
34934        /**
34935      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34936      */
34937     /**
34938      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34939      */
34940
34941     /**
34942      * Returns the id of the column at the specified index.
34943      * @param {Number} index The column index
34944      * @return {String} the id
34945      */
34946     getColumnId : function(index){
34947         return this.config[index].id;
34948     },
34949
34950     /**
34951      * Returns the column for a specified id.
34952      * @param {String} id The column id
34953      * @return {Object} the column
34954      */
34955     getColumnById : function(id){
34956         return this.lookup[id];
34957     },
34958
34959     
34960     /**
34961      * Returns the column for a specified dataIndex.
34962      * @param {String} dataIndex The column dataIndex
34963      * @return {Object|Boolean} the column or false if not found
34964      */
34965     getColumnByDataIndex: function(dataIndex){
34966         var index = this.findColumnIndex(dataIndex);
34967         return index > -1 ? this.config[index] : false;
34968     },
34969     
34970     /**
34971      * Returns the index for a specified column id.
34972      * @param {String} id The column id
34973      * @return {Number} the index, or -1 if not found
34974      */
34975     getIndexById : function(id){
34976         for(var i = 0, len = this.config.length; i < len; i++){
34977             if(this.config[i].id == id){
34978                 return i;
34979             }
34980         }
34981         return -1;
34982     },
34983     
34984     /**
34985      * Returns the index for a specified column dataIndex.
34986      * @param {String} dataIndex The column dataIndex
34987      * @return {Number} the index, or -1 if not found
34988      */
34989     
34990     findColumnIndex : function(dataIndex){
34991         for(var i = 0, len = this.config.length; i < len; i++){
34992             if(this.config[i].dataIndex == dataIndex){
34993                 return i;
34994             }
34995         }
34996         return -1;
34997     },
34998     
34999     
35000     moveColumn : function(oldIndex, newIndex){
35001         var c = this.config[oldIndex];
35002         this.config.splice(oldIndex, 1);
35003         this.config.splice(newIndex, 0, c);
35004         this.dataMap = null;
35005         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35006     },
35007
35008     isLocked : function(colIndex){
35009         return this.config[colIndex].locked === true;
35010     },
35011
35012     setLocked : function(colIndex, value, suppressEvent){
35013         if(this.isLocked(colIndex) == value){
35014             return;
35015         }
35016         this.config[colIndex].locked = value;
35017         if(!suppressEvent){
35018             this.fireEvent("columnlockchange", this, colIndex, value);
35019         }
35020     },
35021
35022     getTotalLockedWidth : function(){
35023         var totalWidth = 0;
35024         for(var i = 0; i < this.config.length; i++){
35025             if(this.isLocked(i) && !this.isHidden(i)){
35026                 this.totalWidth += this.getColumnWidth(i);
35027             }
35028         }
35029         return totalWidth;
35030     },
35031
35032     getLockedCount : function(){
35033         for(var i = 0, len = this.config.length; i < len; i++){
35034             if(!this.isLocked(i)){
35035                 return i;
35036             }
35037         }
35038     },
35039
35040     /**
35041      * Returns the number of columns.
35042      * @return {Number}
35043      */
35044     getColumnCount : function(visibleOnly){
35045         if(visibleOnly === true){
35046             var c = 0;
35047             for(var i = 0, len = this.config.length; i < len; i++){
35048                 if(!this.isHidden(i)){
35049                     c++;
35050                 }
35051             }
35052             return c;
35053         }
35054         return this.config.length;
35055     },
35056
35057     /**
35058      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35059      * @param {Function} fn
35060      * @param {Object} scope (optional)
35061      * @return {Array} result
35062      */
35063     getColumnsBy : function(fn, scope){
35064         var r = [];
35065         for(var i = 0, len = this.config.length; i < len; i++){
35066             var c = this.config[i];
35067             if(fn.call(scope||this, c, i) === true){
35068                 r[r.length] = c;
35069             }
35070         }
35071         return r;
35072     },
35073
35074     /**
35075      * Returns true if the specified column is sortable.
35076      * @param {Number} col The column index
35077      * @return {Boolean}
35078      */
35079     isSortable : function(col){
35080         if(typeof this.config[col].sortable == "undefined"){
35081             return this.defaultSortable;
35082         }
35083         return this.config[col].sortable;
35084     },
35085
35086     /**
35087      * Returns the rendering (formatting) function defined for the column.
35088      * @param {Number} col The column index.
35089      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35090      */
35091     getRenderer : function(col){
35092         if(!this.config[col].renderer){
35093             return Roo.grid.ColumnModel.defaultRenderer;
35094         }
35095         return this.config[col].renderer;
35096     },
35097
35098     /**
35099      * Sets the rendering (formatting) function for a column.
35100      * @param {Number} col The column index
35101      * @param {Function} fn The function to use to process the cell's raw data
35102      * to return HTML markup for the grid view. The render function is called with
35103      * the following parameters:<ul>
35104      * <li>Data value.</li>
35105      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35106      * <li>css A CSS style string to apply to the table cell.</li>
35107      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35108      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35109      * <li>Row index</li>
35110      * <li>Column index</li>
35111      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35112      */
35113     setRenderer : function(col, fn){
35114         this.config[col].renderer = fn;
35115     },
35116
35117     /**
35118      * Returns the width for the specified column.
35119      * @param {Number} col The column index
35120      * @return {Number}
35121      */
35122     getColumnWidth : function(col){
35123         return this.config[col].width * 1 || this.defaultWidth;
35124     },
35125
35126     /**
35127      * Sets the width for a column.
35128      * @param {Number} col The column index
35129      * @param {Number} width The new width
35130      */
35131     setColumnWidth : function(col, width, suppressEvent){
35132         this.config[col].width = width;
35133         this.totalWidth = null;
35134         if(!suppressEvent){
35135              this.fireEvent("widthchange", this, col, width);
35136         }
35137     },
35138
35139     /**
35140      * Returns the total width of all columns.
35141      * @param {Boolean} includeHidden True to include hidden column widths
35142      * @return {Number}
35143      */
35144     getTotalWidth : function(includeHidden){
35145         if(!this.totalWidth){
35146             this.totalWidth = 0;
35147             for(var i = 0, len = this.config.length; i < len; i++){
35148                 if(includeHidden || !this.isHidden(i)){
35149                     this.totalWidth += this.getColumnWidth(i);
35150                 }
35151             }
35152         }
35153         return this.totalWidth;
35154     },
35155
35156     /**
35157      * Returns the header for the specified column.
35158      * @param {Number} col The column index
35159      * @return {String}
35160      */
35161     getColumnHeader : function(col){
35162         return this.config[col].header;
35163     },
35164
35165     /**
35166      * Sets the header for a column.
35167      * @param {Number} col The column index
35168      * @param {String} header The new header
35169      */
35170     setColumnHeader : function(col, header){
35171         this.config[col].header = header;
35172         this.fireEvent("headerchange", this, col, header);
35173     },
35174
35175     /**
35176      * Returns the tooltip for the specified column.
35177      * @param {Number} col The column index
35178      * @return {String}
35179      */
35180     getColumnTooltip : function(col){
35181             return this.config[col].tooltip;
35182     },
35183     /**
35184      * Sets the tooltip for a column.
35185      * @param {Number} col The column index
35186      * @param {String} tooltip The new tooltip
35187      */
35188     setColumnTooltip : function(col, tooltip){
35189             this.config[col].tooltip = tooltip;
35190     },
35191
35192     /**
35193      * Returns the dataIndex for the specified column.
35194      * @param {Number} col The column index
35195      * @return {Number}
35196      */
35197     getDataIndex : function(col){
35198         return this.config[col].dataIndex;
35199     },
35200
35201     /**
35202      * Sets the dataIndex for a column.
35203      * @param {Number} col The column index
35204      * @param {Number} dataIndex The new dataIndex
35205      */
35206     setDataIndex : function(col, dataIndex){
35207         this.config[col].dataIndex = dataIndex;
35208     },
35209
35210     
35211     
35212     /**
35213      * Returns true if the cell is editable.
35214      * @param {Number} colIndex The column index
35215      * @param {Number} rowIndex The row index
35216      * @return {Boolean}
35217      */
35218     isCellEditable : function(colIndex, rowIndex){
35219         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35220     },
35221
35222     /**
35223      * Returns the editor defined for the cell/column.
35224      * return false or null to disable editing.
35225      * @param {Number} colIndex The column index
35226      * @param {Number} rowIndex The row index
35227      * @return {Object}
35228      */
35229     getCellEditor : function(colIndex, rowIndex){
35230         return this.config[colIndex].editor;
35231     },
35232
35233     /**
35234      * Sets if a column is editable.
35235      * @param {Number} col The column index
35236      * @param {Boolean} editable True if the column is editable
35237      */
35238     setEditable : function(col, editable){
35239         this.config[col].editable = editable;
35240     },
35241
35242
35243     /**
35244      * Returns true if the column is hidden.
35245      * @param {Number} colIndex The column index
35246      * @return {Boolean}
35247      */
35248     isHidden : function(colIndex){
35249         return this.config[colIndex].hidden;
35250     },
35251
35252
35253     /**
35254      * Returns true if the column width cannot be changed
35255      */
35256     isFixed : function(colIndex){
35257         return this.config[colIndex].fixed;
35258     },
35259
35260     /**
35261      * Returns true if the column can be resized
35262      * @return {Boolean}
35263      */
35264     isResizable : function(colIndex){
35265         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35266     },
35267     /**
35268      * Sets if a column is hidden.
35269      * @param {Number} colIndex The column index
35270      * @param {Boolean} hidden True if the column is hidden
35271      */
35272     setHidden : function(colIndex, hidden){
35273         this.config[colIndex].hidden = hidden;
35274         this.totalWidth = null;
35275         this.fireEvent("hiddenchange", this, colIndex, hidden);
35276     },
35277
35278     /**
35279      * Sets the editor for a column.
35280      * @param {Number} col The column index
35281      * @param {Object} editor The editor object
35282      */
35283     setEditor : function(col, editor){
35284         this.config[col].editor = editor;
35285     }
35286 });
35287
35288 Roo.grid.ColumnModel.defaultRenderer = function(value){
35289         if(typeof value == "string" && value.length < 1){
35290             return "&#160;";
35291         }
35292         return value;
35293 };
35294
35295 // Alias for backwards compatibility
35296 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35297 /*
35298  * Based on:
35299  * Ext JS Library 1.1.1
35300  * Copyright(c) 2006-2007, Ext JS, LLC.
35301  *
35302  * Originally Released Under LGPL - original licence link has changed is not relivant.
35303  *
35304  * Fork - LGPL
35305  * <script type="text/javascript">
35306  */
35307
35308 /**
35309  * @class Roo.grid.AbstractSelectionModel
35310  * @extends Roo.util.Observable
35311  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35312  * implemented by descendant classes.  This class should not be directly instantiated.
35313  * @constructor
35314  */
35315 Roo.grid.AbstractSelectionModel = function(){
35316     this.locked = false;
35317     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35318 };
35319
35320 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35321     /** @ignore Called by the grid automatically. Do not call directly. */
35322     init : function(grid){
35323         this.grid = grid;
35324         this.initEvents();
35325     },
35326
35327     /**
35328      * Locks the selections.
35329      */
35330     lock : function(){
35331         this.locked = true;
35332     },
35333
35334     /**
35335      * Unlocks the selections.
35336      */
35337     unlock : function(){
35338         this.locked = false;
35339     },
35340
35341     /**
35342      * Returns true if the selections are locked.
35343      * @return {Boolean}
35344      */
35345     isLocked : function(){
35346         return this.locked;
35347     }
35348 });/*
35349  * Based on:
35350  * Ext JS Library 1.1.1
35351  * Copyright(c) 2006-2007, Ext JS, LLC.
35352  *
35353  * Originally Released Under LGPL - original licence link has changed is not relivant.
35354  *
35355  * Fork - LGPL
35356  * <script type="text/javascript">
35357  */
35358 /**
35359  * @extends Roo.grid.AbstractSelectionModel
35360  * @class Roo.grid.RowSelectionModel
35361  * The default SelectionModel used by {@link Roo.grid.Grid}.
35362  * It supports multiple selections and keyboard selection/navigation. 
35363  * @constructor
35364  * @param {Object} config
35365  */
35366 Roo.grid.RowSelectionModel = function(config){
35367     Roo.apply(this, config);
35368     this.selections = new Roo.util.MixedCollection(false, function(o){
35369         return o.id;
35370     });
35371
35372     this.last = false;
35373     this.lastActive = false;
35374
35375     this.addEvents({
35376         /**
35377              * @event selectionchange
35378              * Fires when the selection changes
35379              * @param {SelectionModel} this
35380              */
35381             "selectionchange" : true,
35382         /**
35383              * @event afterselectionchange
35384              * Fires after the selection changes (eg. by key press or clicking)
35385              * @param {SelectionModel} this
35386              */
35387             "afterselectionchange" : true,
35388         /**
35389              * @event beforerowselect
35390              * Fires when a row is selected being selected, return false to cancel.
35391              * @param {SelectionModel} this
35392              * @param {Number} rowIndex The selected index
35393              * @param {Boolean} keepExisting False if other selections will be cleared
35394              */
35395             "beforerowselect" : true,
35396         /**
35397              * @event rowselect
35398              * Fires when a row is selected.
35399              * @param {SelectionModel} this
35400              * @param {Number} rowIndex The selected index
35401              * @param {Roo.data.Record} r The record
35402              */
35403             "rowselect" : true,
35404         /**
35405              * @event rowdeselect
35406              * Fires when a row is deselected.
35407              * @param {SelectionModel} this
35408              * @param {Number} rowIndex The selected index
35409              */
35410         "rowdeselect" : true
35411     });
35412     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35413     this.locked = false;
35414 };
35415
35416 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35417     /**
35418      * @cfg {Boolean} singleSelect
35419      * True to allow selection of only one row at a time (defaults to false)
35420      */
35421     singleSelect : false,
35422
35423     // private
35424     initEvents : function(){
35425
35426         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35427             this.grid.on("mousedown", this.handleMouseDown, this);
35428         }else{ // allow click to work like normal
35429             this.grid.on("rowclick", this.handleDragableRowClick, this);
35430         }
35431
35432         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35433             "up" : function(e){
35434                 if(!e.shiftKey){
35435                     this.selectPrevious(e.shiftKey);
35436                 }else if(this.last !== false && this.lastActive !== false){
35437                     var last = this.last;
35438                     this.selectRange(this.last,  this.lastActive-1);
35439                     this.grid.getView().focusRow(this.lastActive);
35440                     if(last !== false){
35441                         this.last = last;
35442                     }
35443                 }else{
35444                     this.selectFirstRow();
35445                 }
35446                 this.fireEvent("afterselectionchange", this);
35447             },
35448             "down" : function(e){
35449                 if(!e.shiftKey){
35450                     this.selectNext(e.shiftKey);
35451                 }else if(this.last !== false && this.lastActive !== false){
35452                     var last = this.last;
35453                     this.selectRange(this.last,  this.lastActive+1);
35454                     this.grid.getView().focusRow(this.lastActive);
35455                     if(last !== false){
35456                         this.last = last;
35457                     }
35458                 }else{
35459                     this.selectFirstRow();
35460                 }
35461                 this.fireEvent("afterselectionchange", this);
35462             },
35463             scope: this
35464         });
35465
35466         var view = this.grid.view;
35467         view.on("refresh", this.onRefresh, this);
35468         view.on("rowupdated", this.onRowUpdated, this);
35469         view.on("rowremoved", this.onRemove, this);
35470     },
35471
35472     // private
35473     onRefresh : function(){
35474         var ds = this.grid.dataSource, i, v = this.grid.view;
35475         var s = this.selections;
35476         s.each(function(r){
35477             if((i = ds.indexOfId(r.id)) != -1){
35478                 v.onRowSelect(i);
35479             }else{
35480                 s.remove(r);
35481             }
35482         });
35483     },
35484
35485     // private
35486     onRemove : function(v, index, r){
35487         this.selections.remove(r);
35488     },
35489
35490     // private
35491     onRowUpdated : function(v, index, r){
35492         if(this.isSelected(r)){
35493             v.onRowSelect(index);
35494         }
35495     },
35496
35497     /**
35498      * Select records.
35499      * @param {Array} records The records to select
35500      * @param {Boolean} keepExisting (optional) True to keep existing selections
35501      */
35502     selectRecords : function(records, keepExisting){
35503         if(!keepExisting){
35504             this.clearSelections();
35505         }
35506         var ds = this.grid.dataSource;
35507         for(var i = 0, len = records.length; i < len; i++){
35508             this.selectRow(ds.indexOf(records[i]), true);
35509         }
35510     },
35511
35512     /**
35513      * Gets the number of selected rows.
35514      * @return {Number}
35515      */
35516     getCount : function(){
35517         return this.selections.length;
35518     },
35519
35520     /**
35521      * Selects the first row in the grid.
35522      */
35523     selectFirstRow : function(){
35524         this.selectRow(0);
35525     },
35526
35527     /**
35528      * Select the last row.
35529      * @param {Boolean} keepExisting (optional) True to keep existing selections
35530      */
35531     selectLastRow : function(keepExisting){
35532         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35533     },
35534
35535     /**
35536      * Selects the row immediately following the last selected row.
35537      * @param {Boolean} keepExisting (optional) True to keep existing selections
35538      */
35539     selectNext : function(keepExisting){
35540         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35541             this.selectRow(this.last+1, keepExisting);
35542             this.grid.getView().focusRow(this.last);
35543         }
35544     },
35545
35546     /**
35547      * Selects the row that precedes the last selected row.
35548      * @param {Boolean} keepExisting (optional) True to keep existing selections
35549      */
35550     selectPrevious : function(keepExisting){
35551         if(this.last){
35552             this.selectRow(this.last-1, keepExisting);
35553             this.grid.getView().focusRow(this.last);
35554         }
35555     },
35556
35557     /**
35558      * Returns the selected records
35559      * @return {Array} Array of selected records
35560      */
35561     getSelections : function(){
35562         return [].concat(this.selections.items);
35563     },
35564
35565     /**
35566      * Returns the first selected record.
35567      * @return {Record}
35568      */
35569     getSelected : function(){
35570         return this.selections.itemAt(0);
35571     },
35572
35573
35574     /**
35575      * Clears all selections.
35576      */
35577     clearSelections : function(fast){
35578         if(this.locked) return;
35579         if(fast !== true){
35580             var ds = this.grid.dataSource;
35581             var s = this.selections;
35582             s.each(function(r){
35583                 this.deselectRow(ds.indexOfId(r.id));
35584             }, this);
35585             s.clear();
35586         }else{
35587             this.selections.clear();
35588         }
35589         this.last = false;
35590     },
35591
35592
35593     /**
35594      * Selects all rows.
35595      */
35596     selectAll : function(){
35597         if(this.locked) return;
35598         this.selections.clear();
35599         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35600             this.selectRow(i, true);
35601         }
35602     },
35603
35604     /**
35605      * Returns True if there is a selection.
35606      * @return {Boolean}
35607      */
35608     hasSelection : function(){
35609         return this.selections.length > 0;
35610     },
35611
35612     /**
35613      * Returns True if the specified row is selected.
35614      * @param {Number/Record} record The record or index of the record to check
35615      * @return {Boolean}
35616      */
35617     isSelected : function(index){
35618         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35619         return (r && this.selections.key(r.id) ? true : false);
35620     },
35621
35622     /**
35623      * Returns True if the specified record id is selected.
35624      * @param {String} id The id of record to check
35625      * @return {Boolean}
35626      */
35627     isIdSelected : function(id){
35628         return (this.selections.key(id) ? true : false);
35629     },
35630
35631     // private
35632     handleMouseDown : function(e, t){
35633         var view = this.grid.getView(), rowIndex;
35634         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35635             return;
35636         };
35637         if(e.shiftKey && this.last !== false){
35638             var last = this.last;
35639             this.selectRange(last, rowIndex, e.ctrlKey);
35640             this.last = last; // reset the last
35641             view.focusRow(rowIndex);
35642         }else{
35643             var isSelected = this.isSelected(rowIndex);
35644             if(e.button !== 0 && isSelected){
35645                 view.focusRow(rowIndex);
35646             }else if(e.ctrlKey && isSelected){
35647                 this.deselectRow(rowIndex);
35648             }else if(!isSelected){
35649                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35650                 view.focusRow(rowIndex);
35651             }
35652         }
35653         this.fireEvent("afterselectionchange", this);
35654     },
35655     // private
35656     handleDragableRowClick :  function(grid, rowIndex, e) 
35657     {
35658         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35659             this.selectRow(rowIndex, false);
35660             grid.view.focusRow(rowIndex);
35661              this.fireEvent("afterselectionchange", this);
35662         }
35663     },
35664     
35665     /**
35666      * Selects multiple rows.
35667      * @param {Array} rows Array of the indexes of the row to select
35668      * @param {Boolean} keepExisting (optional) True to keep existing selections
35669      */
35670     selectRows : function(rows, keepExisting){
35671         if(!keepExisting){
35672             this.clearSelections();
35673         }
35674         for(var i = 0, len = rows.length; i < len; i++){
35675             this.selectRow(rows[i], true);
35676         }
35677     },
35678
35679     /**
35680      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35681      * @param {Number} startRow The index of the first row in the range
35682      * @param {Number} endRow The index of the last row in the range
35683      * @param {Boolean} keepExisting (optional) True to retain existing selections
35684      */
35685     selectRange : function(startRow, endRow, keepExisting){
35686         if(this.locked) return;
35687         if(!keepExisting){
35688             this.clearSelections();
35689         }
35690         if(startRow <= endRow){
35691             for(var i = startRow; i <= endRow; i++){
35692                 this.selectRow(i, true);
35693             }
35694         }else{
35695             for(var i = startRow; i >= endRow; i--){
35696                 this.selectRow(i, true);
35697             }
35698         }
35699     },
35700
35701     /**
35702      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35703      * @param {Number} startRow The index of the first row in the range
35704      * @param {Number} endRow The index of the last row in the range
35705      */
35706     deselectRange : function(startRow, endRow, preventViewNotify){
35707         if(this.locked) return;
35708         for(var i = startRow; i <= endRow; i++){
35709             this.deselectRow(i, preventViewNotify);
35710         }
35711     },
35712
35713     /**
35714      * Selects a row.
35715      * @param {Number} row The index of the row to select
35716      * @param {Boolean} keepExisting (optional) True to keep existing selections
35717      */
35718     selectRow : function(index, keepExisting, preventViewNotify){
35719         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35720         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35721             if(!keepExisting || this.singleSelect){
35722                 this.clearSelections();
35723             }
35724             var r = this.grid.dataSource.getAt(index);
35725             this.selections.add(r);
35726             this.last = this.lastActive = index;
35727             if(!preventViewNotify){
35728                 this.grid.getView().onRowSelect(index);
35729             }
35730             this.fireEvent("rowselect", this, index, r);
35731             this.fireEvent("selectionchange", this);
35732         }
35733     },
35734
35735     /**
35736      * Deselects a row.
35737      * @param {Number} row The index of the row to deselect
35738      */
35739     deselectRow : function(index, preventViewNotify){
35740         if(this.locked) return;
35741         if(this.last == index){
35742             this.last = false;
35743         }
35744         if(this.lastActive == index){
35745             this.lastActive = false;
35746         }
35747         var r = this.grid.dataSource.getAt(index);
35748         this.selections.remove(r);
35749         if(!preventViewNotify){
35750             this.grid.getView().onRowDeselect(index);
35751         }
35752         this.fireEvent("rowdeselect", this, index);
35753         this.fireEvent("selectionchange", this);
35754     },
35755
35756     // private
35757     restoreLast : function(){
35758         if(this._last){
35759             this.last = this._last;
35760         }
35761     },
35762
35763     // private
35764     acceptsNav : function(row, col, cm){
35765         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35766     },
35767
35768     // private
35769     onEditorKey : function(field, e){
35770         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35771         if(k == e.TAB){
35772             e.stopEvent();
35773             ed.completeEdit();
35774             if(e.shiftKey){
35775                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35776             }else{
35777                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35778             }
35779         }else if(k == e.ENTER && !e.ctrlKey){
35780             e.stopEvent();
35781             ed.completeEdit();
35782             if(e.shiftKey){
35783                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35784             }else{
35785                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35786             }
35787         }else if(k == e.ESC){
35788             ed.cancelEdit();
35789         }
35790         if(newCell){
35791             g.startEditing(newCell[0], newCell[1]);
35792         }
35793     }
35794 });/*
35795  * Based on:
35796  * Ext JS Library 1.1.1
35797  * Copyright(c) 2006-2007, Ext JS, LLC.
35798  *
35799  * Originally Released Under LGPL - original licence link has changed is not relivant.
35800  *
35801  * Fork - LGPL
35802  * <script type="text/javascript">
35803  */
35804 /**
35805  * @class Roo.grid.CellSelectionModel
35806  * @extends Roo.grid.AbstractSelectionModel
35807  * This class provides the basic implementation for cell selection in a grid.
35808  * @constructor
35809  * @param {Object} config The object containing the configuration of this model.
35810  */
35811 Roo.grid.CellSelectionModel = function(config){
35812     Roo.apply(this, config);
35813
35814     this.selection = null;
35815
35816     this.addEvents({
35817         /**
35818              * @event beforerowselect
35819              * Fires before a cell is selected.
35820              * @param {SelectionModel} this
35821              * @param {Number} rowIndex The selected row index
35822              * @param {Number} colIndex The selected cell index
35823              */
35824             "beforecellselect" : true,
35825         /**
35826              * @event cellselect
35827              * Fires when a cell is selected.
35828              * @param {SelectionModel} this
35829              * @param {Number} rowIndex The selected row index
35830              * @param {Number} colIndex The selected cell index
35831              */
35832             "cellselect" : true,
35833         /**
35834              * @event selectionchange
35835              * Fires when the active selection changes.
35836              * @param {SelectionModel} this
35837              * @param {Object} selection null for no selection or an object (o) with two properties
35838                 <ul>
35839                 <li>o.record: the record object for the row the selection is in</li>
35840                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35841                 </ul>
35842              */
35843             "selectionchange" : true
35844     });
35845     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35846 };
35847
35848 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35849
35850     /** @ignore */
35851     initEvents : function(){
35852         this.grid.on("mousedown", this.handleMouseDown, this);
35853         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35854         var view = this.grid.view;
35855         view.on("refresh", this.onViewChange, this);
35856         view.on("rowupdated", this.onRowUpdated, this);
35857         view.on("beforerowremoved", this.clearSelections, this);
35858         view.on("beforerowsinserted", this.clearSelections, this);
35859         if(this.grid.isEditor){
35860             this.grid.on("beforeedit", this.beforeEdit,  this);
35861         }
35862     },
35863
35864         //private
35865     beforeEdit : function(e){
35866         this.select(e.row, e.column, false, true, e.record);
35867     },
35868
35869         //private
35870     onRowUpdated : function(v, index, r){
35871         if(this.selection && this.selection.record == r){
35872             v.onCellSelect(index, this.selection.cell[1]);
35873         }
35874     },
35875
35876         //private
35877     onViewChange : function(){
35878         this.clearSelections(true);
35879     },
35880
35881         /**
35882          * Returns the currently selected cell,.
35883          * @return {Array} The selected cell (row, column) or null if none selected.
35884          */
35885     getSelectedCell : function(){
35886         return this.selection ? this.selection.cell : null;
35887     },
35888
35889     /**
35890      * Clears all selections.
35891      * @param {Boolean} true to prevent the gridview from being notified about the change.
35892      */
35893     clearSelections : function(preventNotify){
35894         var s = this.selection;
35895         if(s){
35896             if(preventNotify !== true){
35897                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35898             }
35899             this.selection = null;
35900             this.fireEvent("selectionchange", this, null);
35901         }
35902     },
35903
35904     /**
35905      * Returns true if there is a selection.
35906      * @return {Boolean}
35907      */
35908     hasSelection : function(){
35909         return this.selection ? true : false;
35910     },
35911
35912     /** @ignore */
35913     handleMouseDown : function(e, t){
35914         var v = this.grid.getView();
35915         if(this.isLocked()){
35916             return;
35917         };
35918         var row = v.findRowIndex(t);
35919         var cell = v.findCellIndex(t);
35920         if(row !== false && cell !== false){
35921             this.select(row, cell);
35922         }
35923     },
35924
35925     /**
35926      * Selects a cell.
35927      * @param {Number} rowIndex
35928      * @param {Number} collIndex
35929      */
35930     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35931         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35932             this.clearSelections();
35933             r = r || this.grid.dataSource.getAt(rowIndex);
35934             this.selection = {
35935                 record : r,
35936                 cell : [rowIndex, colIndex]
35937             };
35938             if(!preventViewNotify){
35939                 var v = this.grid.getView();
35940                 v.onCellSelect(rowIndex, colIndex);
35941                 if(preventFocus !== true){
35942                     v.focusCell(rowIndex, colIndex);
35943                 }
35944             }
35945             this.fireEvent("cellselect", this, rowIndex, colIndex);
35946             this.fireEvent("selectionchange", this, this.selection);
35947         }
35948     },
35949
35950         //private
35951     isSelectable : function(rowIndex, colIndex, cm){
35952         return !cm.isHidden(colIndex);
35953     },
35954
35955     /** @ignore */
35956     handleKeyDown : function(e){
35957         Roo.log('Cell Sel Model handleKeyDown');
35958         if(!e.isNavKeyPress()){
35959             return;
35960         }
35961         var g = this.grid, s = this.selection;
35962         if(!s){
35963             e.stopEvent();
35964             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35965             if(cell){
35966                 this.select(cell[0], cell[1]);
35967             }
35968             return;
35969         }
35970         var sm = this;
35971         var walk = function(row, col, step){
35972             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35973         };
35974         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35975         var newCell;
35976
35977         switch(k){
35978             case e.TAB:
35979                 // handled by onEditorKey
35980                 if (g.isEditor && g.editing) {
35981                     return;
35982                 }
35983                 if(e.shiftKey){
35984                      newCell = walk(r, c-1, -1);
35985                 }else{
35986                      newCell = walk(r, c+1, 1);
35987                 }
35988              break;
35989              case e.DOWN:
35990                  newCell = walk(r+1, c, 1);
35991              break;
35992              case e.UP:
35993                  newCell = walk(r-1, c, -1);
35994              break;
35995              case e.RIGHT:
35996                  newCell = walk(r, c+1, 1);
35997              break;
35998              case e.LEFT:
35999                  newCell = walk(r, c-1, -1);
36000              break;
36001              case e.ENTER:
36002                  if(g.isEditor && !g.editing){
36003                     g.startEditing(r, c);
36004                     e.stopEvent();
36005                     return;
36006                 }
36007              break;
36008         };
36009         if(newCell){
36010             this.select(newCell[0], newCell[1]);
36011             e.stopEvent();
36012         }
36013     },
36014
36015     acceptsNav : function(row, col, cm){
36016         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36017     },
36018
36019     onEditorKey : function(field, e){
36020         
36021         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36022         ///Roo.log('onEditorKey' + k);
36023         
36024         if(k == e.TAB){
36025             if(e.shiftKey){
36026                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36027             }else{
36028                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36029             }
36030             e.stopEvent();
36031         }else if(k == e.ENTER && !e.ctrlKey){
36032             ed.completeEdit();
36033             e.stopEvent();
36034             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36035         }else if(k == e.ESC){
36036             ed.cancelEdit();
36037         }
36038         
36039         
36040         if(newCell){
36041             //Roo.log('next cell after edit');
36042             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36043         }
36044     }
36045 });/*
36046  * Based on:
36047  * Ext JS Library 1.1.1
36048  * Copyright(c) 2006-2007, Ext JS, LLC.
36049  *
36050  * Originally Released Under LGPL - original licence link has changed is not relivant.
36051  *
36052  * Fork - LGPL
36053  * <script type="text/javascript">
36054  */
36055  
36056 /**
36057  * @class Roo.grid.EditorGrid
36058  * @extends Roo.grid.Grid
36059  * Class for creating and editable grid.
36060  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36061  * The container MUST have some type of size defined for the grid to fill. The container will be 
36062  * automatically set to position relative if it isn't already.
36063  * @param {Object} dataSource The data model to bind to
36064  * @param {Object} colModel The column model with info about this grid's columns
36065  */
36066 Roo.grid.EditorGrid = function(container, config){
36067     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36068     this.getGridEl().addClass("xedit-grid");
36069
36070     if(!this.selModel){
36071         this.selModel = new Roo.grid.CellSelectionModel();
36072     }
36073
36074     this.activeEditor = null;
36075
36076         this.addEvents({
36077             /**
36078              * @event beforeedit
36079              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36080              * <ul style="padding:5px;padding-left:16px;">
36081              * <li>grid - This grid</li>
36082              * <li>record - The record being edited</li>
36083              * <li>field - The field name being edited</li>
36084              * <li>value - The value for the field being edited.</li>
36085              * <li>row - The grid row index</li>
36086              * <li>column - The grid column index</li>
36087              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36088              * </ul>
36089              * @param {Object} e An edit event (see above for description)
36090              */
36091             "beforeedit" : true,
36092             /**
36093              * @event afteredit
36094              * Fires after a cell is edited. <br />
36095              * <ul style="padding:5px;padding-left:16px;">
36096              * <li>grid - This grid</li>
36097              * <li>record - The record being edited</li>
36098              * <li>field - The field name being edited</li>
36099              * <li>value - The value being set</li>
36100              * <li>originalValue - The original value for the field, before the edit.</li>
36101              * <li>row - The grid row index</li>
36102              * <li>column - The grid column index</li>
36103              * </ul>
36104              * @param {Object} e An edit event (see above for description)
36105              */
36106             "afteredit" : true,
36107             /**
36108              * @event validateedit
36109              * Fires after a cell is edited, but before the value is set in the record. 
36110          * You can use this to modify the value being set in the field, Return false
36111              * to cancel the change. The edit event object has the following properties <br />
36112              * <ul style="padding:5px;padding-left:16px;">
36113          * <li>editor - This editor</li>
36114              * <li>grid - This grid</li>
36115              * <li>record - The record being edited</li>
36116              * <li>field - The field name being edited</li>
36117              * <li>value - The value being set</li>
36118              * <li>originalValue - The original value for the field, before the edit.</li>
36119              * <li>row - The grid row index</li>
36120              * <li>column - The grid column index</li>
36121              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36122              * </ul>
36123              * @param {Object} e An edit event (see above for description)
36124              */
36125             "validateedit" : true
36126         });
36127     this.on("bodyscroll", this.stopEditing,  this);
36128     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36129 };
36130
36131 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36132     /**
36133      * @cfg {Number} clicksToEdit
36134      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36135      */
36136     clicksToEdit: 2,
36137
36138     // private
36139     isEditor : true,
36140     // private
36141     trackMouseOver: false, // causes very odd FF errors
36142
36143     onCellDblClick : function(g, row, col){
36144         this.startEditing(row, col);
36145     },
36146
36147     onEditComplete : function(ed, value, startValue){
36148         this.editing = false;
36149         this.activeEditor = null;
36150         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36151         var r = ed.record;
36152         var field = this.colModel.getDataIndex(ed.col);
36153         var e = {
36154             grid: this,
36155             record: r,
36156             field: field,
36157             originalValue: startValue,
36158             value: value,
36159             row: ed.row,
36160             column: ed.col,
36161             cancel:false,
36162             editor: ed
36163         };
36164         if(String(value) !== String(startValue)){
36165             
36166             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36167                 r.set(field, e.value);
36168                 // if we are dealing with a combo box..
36169                 // then we also set the 'name' colum to be the displayField
36170                 if (ed.field.displayField && ed.field.name) {
36171                     r.set(ed.field.name, ed.field.el.dom.value);
36172                 }
36173                 
36174                 delete e.cancel; //?? why!!!
36175                 this.fireEvent("afteredit", e);
36176             }
36177         } else {
36178             this.fireEvent("afteredit", e); // always fire it!
36179         }
36180         this.view.focusCell(ed.row, ed.col);
36181     },
36182
36183     /**
36184      * Starts editing the specified for the specified row/column
36185      * @param {Number} rowIndex
36186      * @param {Number} colIndex
36187      */
36188     startEditing : function(row, col){
36189         this.stopEditing();
36190         if(this.colModel.isCellEditable(col, row)){
36191             this.view.ensureVisible(row, col, true);
36192             var r = this.dataSource.getAt(row);
36193             var field = this.colModel.getDataIndex(col);
36194             var e = {
36195                 grid: this,
36196                 record: r,
36197                 field: field,
36198                 value: r.data[field],
36199                 row: row,
36200                 column: col,
36201                 cancel:false
36202             };
36203             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36204                 this.editing = true;
36205                 var ed = this.colModel.getCellEditor(col, row);
36206                 
36207                 if (!ed) {
36208                     return;
36209                 }
36210                 if(!ed.rendered){
36211                     ed.render(ed.parentEl || document.body);
36212                 }
36213                 ed.field.reset();
36214                 (function(){ // complex but required for focus issues in safari, ie and opera
36215                     ed.row = row;
36216                     ed.col = col;
36217                     ed.record = r;
36218                     ed.on("complete", this.onEditComplete, this, {single: true});
36219                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
36220                     this.activeEditor = ed;
36221                     var v = r.data[field];
36222                     ed.startEdit(this.view.getCell(row, col), v);
36223                     // combo's with 'displayField and name set
36224                     if (ed.field.displayField && ed.field.name) {
36225                         ed.field.el.dom.value = r.data[ed.field.name];
36226                     }
36227                     
36228                     
36229                 }).defer(50, this);
36230             }
36231         }
36232     },
36233         
36234     /**
36235      * Stops any active editing
36236      */
36237     stopEditing : function(){
36238         if(this.activeEditor){
36239             this.activeEditor.completeEdit();
36240         }
36241         this.activeEditor = null;
36242     }
36243 });/*
36244  * Based on:
36245  * Ext JS Library 1.1.1
36246  * Copyright(c) 2006-2007, Ext JS, LLC.
36247  *
36248  * Originally Released Under LGPL - original licence link has changed is not relivant.
36249  *
36250  * Fork - LGPL
36251  * <script type="text/javascript">
36252  */
36253
36254 // private - not really -- you end up using it !
36255 // This is a support class used internally by the Grid components
36256
36257 /**
36258  * @class Roo.grid.GridEditor
36259  * @extends Roo.Editor
36260  * Class for creating and editable grid elements.
36261  * @param {Object} config any settings (must include field)
36262  */
36263 Roo.grid.GridEditor = function(field, config){
36264     if (!config && field.field) {
36265         config = field;
36266         field = Roo.factory(config.field, Roo.form);
36267     }
36268     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36269     field.monitorTab = false;
36270 };
36271
36272 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36273     
36274     /**
36275      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36276      */
36277     
36278     alignment: "tl-tl",
36279     autoSize: "width",
36280     hideEl : false,
36281     cls: "x-small-editor x-grid-editor",
36282     shim:false,
36283     shadow:"frame"
36284 });/*
36285  * Based on:
36286  * Ext JS Library 1.1.1
36287  * Copyright(c) 2006-2007, Ext JS, LLC.
36288  *
36289  * Originally Released Under LGPL - original licence link has changed is not relivant.
36290  *
36291  * Fork - LGPL
36292  * <script type="text/javascript">
36293  */
36294   
36295
36296   
36297 Roo.grid.PropertyRecord = Roo.data.Record.create([
36298     {name:'name',type:'string'},  'value'
36299 ]);
36300
36301
36302 Roo.grid.PropertyStore = function(grid, source){
36303     this.grid = grid;
36304     this.store = new Roo.data.Store({
36305         recordType : Roo.grid.PropertyRecord
36306     });
36307     this.store.on('update', this.onUpdate,  this);
36308     if(source){
36309         this.setSource(source);
36310     }
36311     Roo.grid.PropertyStore.superclass.constructor.call(this);
36312 };
36313
36314
36315
36316 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36317     setSource : function(o){
36318         this.source = o;
36319         this.store.removeAll();
36320         var data = [];
36321         for(var k in o){
36322             if(this.isEditableValue(o[k])){
36323                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36324             }
36325         }
36326         this.store.loadRecords({records: data}, {}, true);
36327     },
36328
36329     onUpdate : function(ds, record, type){
36330         if(type == Roo.data.Record.EDIT){
36331             var v = record.data['value'];
36332             var oldValue = record.modified['value'];
36333             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36334                 this.source[record.id] = v;
36335                 record.commit();
36336                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36337             }else{
36338                 record.reject();
36339             }
36340         }
36341     },
36342
36343     getProperty : function(row){
36344        return this.store.getAt(row);
36345     },
36346
36347     isEditableValue: function(val){
36348         if(val && val instanceof Date){
36349             return true;
36350         }else if(typeof val == 'object' || typeof val == 'function'){
36351             return false;
36352         }
36353         return true;
36354     },
36355
36356     setValue : function(prop, value){
36357         this.source[prop] = value;
36358         this.store.getById(prop).set('value', value);
36359     },
36360
36361     getSource : function(){
36362         return this.source;
36363     }
36364 });
36365
36366 Roo.grid.PropertyColumnModel = function(grid, store){
36367     this.grid = grid;
36368     var g = Roo.grid;
36369     g.PropertyColumnModel.superclass.constructor.call(this, [
36370         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36371         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36372     ]);
36373     this.store = store;
36374     this.bselect = Roo.DomHelper.append(document.body, {
36375         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36376             {tag: 'option', value: 'true', html: 'true'},
36377             {tag: 'option', value: 'false', html: 'false'}
36378         ]
36379     });
36380     Roo.id(this.bselect);
36381     var f = Roo.form;
36382     this.editors = {
36383         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36384         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36385         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36386         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36387         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36388     };
36389     this.renderCellDelegate = this.renderCell.createDelegate(this);
36390     this.renderPropDelegate = this.renderProp.createDelegate(this);
36391 };
36392
36393 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36394     
36395     
36396     nameText : 'Name',
36397     valueText : 'Value',
36398     
36399     dateFormat : 'm/j/Y',
36400     
36401     
36402     renderDate : function(dateVal){
36403         return dateVal.dateFormat(this.dateFormat);
36404     },
36405
36406     renderBool : function(bVal){
36407         return bVal ? 'true' : 'false';
36408     },
36409
36410     isCellEditable : function(colIndex, rowIndex){
36411         return colIndex == 1;
36412     },
36413
36414     getRenderer : function(col){
36415         return col == 1 ?
36416             this.renderCellDelegate : this.renderPropDelegate;
36417     },
36418
36419     renderProp : function(v){
36420         return this.getPropertyName(v);
36421     },
36422
36423     renderCell : function(val){
36424         var rv = val;
36425         if(val instanceof Date){
36426             rv = this.renderDate(val);
36427         }else if(typeof val == 'boolean'){
36428             rv = this.renderBool(val);
36429         }
36430         return Roo.util.Format.htmlEncode(rv);
36431     },
36432
36433     getPropertyName : function(name){
36434         var pn = this.grid.propertyNames;
36435         return pn && pn[name] ? pn[name] : name;
36436     },
36437
36438     getCellEditor : function(colIndex, rowIndex){
36439         var p = this.store.getProperty(rowIndex);
36440         var n = p.data['name'], val = p.data['value'];
36441         
36442         if(typeof(this.grid.customEditors[n]) == 'string'){
36443             return this.editors[this.grid.customEditors[n]];
36444         }
36445         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36446             return this.grid.customEditors[n];
36447         }
36448         if(val instanceof Date){
36449             return this.editors['date'];
36450         }else if(typeof val == 'number'){
36451             return this.editors['number'];
36452         }else if(typeof val == 'boolean'){
36453             return this.editors['boolean'];
36454         }else{
36455             return this.editors['string'];
36456         }
36457     }
36458 });
36459
36460 /**
36461  * @class Roo.grid.PropertyGrid
36462  * @extends Roo.grid.EditorGrid
36463  * This class represents the  interface of a component based property grid control.
36464  * <br><br>Usage:<pre><code>
36465  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36466       
36467  });
36468  // set any options
36469  grid.render();
36470  * </code></pre>
36471   
36472  * @constructor
36473  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36474  * The container MUST have some type of size defined for the grid to fill. The container will be
36475  * automatically set to position relative if it isn't already.
36476  * @param {Object} config A config object that sets properties on this grid.
36477  */
36478 Roo.grid.PropertyGrid = function(container, config){
36479     config = config || {};
36480     var store = new Roo.grid.PropertyStore(this);
36481     this.store = store;
36482     var cm = new Roo.grid.PropertyColumnModel(this, store);
36483     store.store.sort('name', 'ASC');
36484     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36485         ds: store.store,
36486         cm: cm,
36487         enableColLock:false,
36488         enableColumnMove:false,
36489         stripeRows:false,
36490         trackMouseOver: false,
36491         clicksToEdit:1
36492     }, config));
36493     this.getGridEl().addClass('x-props-grid');
36494     this.lastEditRow = null;
36495     this.on('columnresize', this.onColumnResize, this);
36496     this.addEvents({
36497          /**
36498              * @event beforepropertychange
36499              * Fires before a property changes (return false to stop?)
36500              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36501              * @param {String} id Record Id
36502              * @param {String} newval New Value
36503          * @param {String} oldval Old Value
36504              */
36505         "beforepropertychange": true,
36506         /**
36507              * @event propertychange
36508              * Fires after a property changes
36509              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36510              * @param {String} id Record Id
36511              * @param {String} newval New Value
36512          * @param {String} oldval Old Value
36513              */
36514         "propertychange": true
36515     });
36516     this.customEditors = this.customEditors || {};
36517 };
36518 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36519     
36520      /**
36521      * @cfg {Object} customEditors map of colnames=> custom editors.
36522      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36523      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36524      * false disables editing of the field.
36525          */
36526     
36527       /**
36528      * @cfg {Object} propertyNames map of property Names to their displayed value
36529          */
36530     
36531     render : function(){
36532         Roo.grid.PropertyGrid.superclass.render.call(this);
36533         this.autoSize.defer(100, this);
36534     },
36535
36536     autoSize : function(){
36537         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36538         if(this.view){
36539             this.view.fitColumns();
36540         }
36541     },
36542
36543     onColumnResize : function(){
36544         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36545         this.autoSize();
36546     },
36547     /**
36548      * Sets the data for the Grid
36549      * accepts a Key => Value object of all the elements avaiable.
36550      * @param {Object} data  to appear in grid.
36551      */
36552     setSource : function(source){
36553         this.store.setSource(source);
36554         //this.autoSize();
36555     },
36556     /**
36557      * Gets all the data from the grid.
36558      * @return {Object} data  data stored in grid
36559      */
36560     getSource : function(){
36561         return this.store.getSource();
36562     }
36563 });/*
36564  * Based on:
36565  * Ext JS Library 1.1.1
36566  * Copyright(c) 2006-2007, Ext JS, LLC.
36567  *
36568  * Originally Released Under LGPL - original licence link has changed is not relivant.
36569  *
36570  * Fork - LGPL
36571  * <script type="text/javascript">
36572  */
36573  
36574 /**
36575  * @class Roo.LoadMask
36576  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36577  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36578  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36579  * element's UpdateManager load indicator and will be destroyed after the initial load.
36580  * @constructor
36581  * Create a new LoadMask
36582  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36583  * @param {Object} config The config object
36584  */
36585 Roo.LoadMask = function(el, config){
36586     this.el = Roo.get(el);
36587     Roo.apply(this, config);
36588     if(this.store){
36589         this.store.on('beforeload', this.onBeforeLoad, this);
36590         this.store.on('load', this.onLoad, this);
36591         this.store.on('loadexception', this.onLoad, this);
36592         this.removeMask = false;
36593     }else{
36594         var um = this.el.getUpdateManager();
36595         um.showLoadIndicator = false; // disable the default indicator
36596         um.on('beforeupdate', this.onBeforeLoad, this);
36597         um.on('update', this.onLoad, this);
36598         um.on('failure', this.onLoad, this);
36599         this.removeMask = true;
36600     }
36601 };
36602
36603 Roo.LoadMask.prototype = {
36604     /**
36605      * @cfg {Boolean} removeMask
36606      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36607      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36608      */
36609     /**
36610      * @cfg {String} msg
36611      * The text to display in a centered loading message box (defaults to 'Loading...')
36612      */
36613     msg : 'Loading...',
36614     /**
36615      * @cfg {String} msgCls
36616      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36617      */
36618     msgCls : 'x-mask-loading',
36619
36620     /**
36621      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36622      * @type Boolean
36623      */
36624     disabled: false,
36625
36626     /**
36627      * Disables the mask to prevent it from being displayed
36628      */
36629     disable : function(){
36630        this.disabled = true;
36631     },
36632
36633     /**
36634      * Enables the mask so that it can be displayed
36635      */
36636     enable : function(){
36637         this.disabled = false;
36638     },
36639
36640     // private
36641     onLoad : function(){
36642         this.el.unmask(this.removeMask);
36643     },
36644
36645     // private
36646     onBeforeLoad : function(){
36647         if(!this.disabled){
36648             this.el.mask(this.msg, this.msgCls);
36649         }
36650     },
36651
36652     // private
36653     destroy : function(){
36654         if(this.store){
36655             this.store.un('beforeload', this.onBeforeLoad, this);
36656             this.store.un('load', this.onLoad, this);
36657             this.store.un('loadexception', this.onLoad, this);
36658         }else{
36659             var um = this.el.getUpdateManager();
36660             um.un('beforeupdate', this.onBeforeLoad, this);
36661             um.un('update', this.onLoad, this);
36662             um.un('failure', this.onLoad, this);
36663         }
36664     }
36665 };/*
36666  * Based on:
36667  * Ext JS Library 1.1.1
36668  * Copyright(c) 2006-2007, Ext JS, LLC.
36669  *
36670  * Originally Released Under LGPL - original licence link has changed is not relivant.
36671  *
36672  * Fork - LGPL
36673  * <script type="text/javascript">
36674  */
36675 Roo.XTemplate = function(){
36676     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36677     var s = this.html;
36678
36679     s = ['<tpl>', s, '</tpl>'].join('');
36680
36681     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36682
36683     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36684     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36685     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36686     var m, id = 0;
36687     var tpls = [];
36688
36689     while(m = s.match(re)){
36690        var m2 = m[0].match(nameRe);
36691        var m3 = m[0].match(ifRe);
36692        var m4 = m[0].match(execRe);
36693        var exp = null, fn = null, exec = null;
36694        var name = m2 && m2[1] ? m2[1] : '';
36695        if(m3){
36696            exp = m3 && m3[1] ? m3[1] : null;
36697            if(exp){
36698                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36699            }
36700        }
36701        if(m4){
36702            exp = m4 && m4[1] ? m4[1] : null;
36703            if(exp){
36704                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36705            }
36706        }
36707        if(name){
36708            switch(name){
36709                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36710                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36711                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36712            }
36713        }
36714        tpls.push({
36715             id: id,
36716             target: name,
36717             exec: exec,
36718             test: fn,
36719             body: m[1]||''
36720         });
36721        s = s.replace(m[0], '{xtpl'+ id + '}');
36722        ++id;
36723     }
36724     for(var i = tpls.length-1; i >= 0; --i){
36725         this.compileTpl(tpls[i]);
36726     }
36727     this.master = tpls[tpls.length-1];
36728     this.tpls = tpls;
36729 };
36730 Roo.extend(Roo.XTemplate, Roo.Template, {
36731
36732     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36733
36734     applySubTemplate : function(id, values, parent){
36735         var t = this.tpls[id];
36736         if(t.test && !t.test.call(this, values, parent)){
36737             return '';
36738         }
36739         if(t.exec && t.exec.call(this, values, parent)){
36740             return '';
36741         }
36742         var vs = t.target ? t.target.call(this, values, parent) : values;
36743         parent = t.target ? values : parent;
36744         if(t.target && vs instanceof Array){
36745             var buf = [];
36746             for(var i = 0, len = vs.length; i < len; i++){
36747                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36748             }
36749             return buf.join('');
36750         }
36751         return t.compiled.call(this, vs, parent);
36752     },
36753
36754     compileTpl : function(tpl){
36755         var fm = Roo.util.Format;
36756         var useF = this.disableFormats !== true;
36757         var sep = Roo.isGecko ? "+" : ",";
36758         var fn = function(m, name, format, args){
36759             if(name.substr(0, 4) == 'xtpl'){
36760                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36761             }
36762             var v;
36763             if(name.indexOf('.') != -1){
36764                 v = name;
36765             }else{
36766                 v = "values['" + name + "']";
36767             }
36768             if(format && useF){
36769                 args = args ? ',' + args : "";
36770                 if(format.substr(0, 5) != "this."){
36771                     format = "fm." + format + '(';
36772                 }else{
36773                     format = 'this.call("'+ format.substr(5) + '", ';
36774                     args = ", values";
36775                 }
36776             }else{
36777                 args= ''; format = "("+v+" === undefined ? '' : ";
36778             }
36779             return "'"+ sep + format + v + args + ")"+sep+"'";
36780         };
36781         var body;
36782         // branched to use + in gecko and [].join() in others
36783         if(Roo.isGecko){
36784             body = "tpl.compiled = function(values, parent){ return '" +
36785                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36786                     "';};";
36787         }else{
36788             body = ["tpl.compiled = function(values, parent){ return ['"];
36789             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36790             body.push("'].join('');};");
36791             body = body.join('');
36792         }
36793         /** eval:var:zzzzzzz */
36794         eval(body);
36795         return this;
36796     },
36797
36798     applyTemplate : function(values){
36799         return this.master.compiled.call(this, values, {});
36800         var s = this.subs;
36801     },
36802
36803     apply : function(){
36804         return this.applyTemplate.apply(this, arguments);
36805     },
36806
36807     compile : function(){return this;}
36808 });
36809
36810 Roo.XTemplate.from = function(el){
36811     el = Roo.getDom(el);
36812     return new Roo.XTemplate(el.value || el.innerHTML);
36813 };/*
36814  * Original code for Roojs - LGPL
36815  * <script type="text/javascript">
36816  */
36817  
36818 /**
36819  * @class Roo.XComponent
36820  * A delayed Element creator...
36821  * Or a way to group chunks of interface together.
36822  * 
36823  * Mypart.xyx = new Roo.XComponent({
36824
36825     parent : 'Mypart.xyz', // empty == document.element.!!
36826     order : '001',
36827     name : 'xxxx'
36828     region : 'xxxx'
36829     disabled : function() {} 
36830      
36831     tree : function() { // return an tree of xtype declared components
36832         var MODULE = this;
36833         return 
36834         {
36835             xtype : 'NestedLayoutPanel',
36836             // technicall
36837         }
36838      ]
36839  *})
36840  *
36841  *
36842  * It can be used to build a big heiracy, with parent etc.
36843  * or you can just use this to render a single compoent to a dom element
36844  * MYPART.render(Roo.Element | String(id) | dom_element )
36845  * 
36846  * @extends Roo.util.Observable
36847  * @constructor
36848  * @param cfg {Object} configuration of component
36849  * 
36850  */
36851 Roo.XComponent = function(cfg) {
36852     Roo.apply(this, cfg);
36853     this.addEvents({ 
36854         /**
36855              * @event built
36856              * Fires when this the componnt is built
36857              * @param {Roo.XComponent} c the component
36858              */
36859         'built' : true,
36860         /**
36861              * @event buildcomplete
36862              * Fires on the top level element when all elements have been built
36863              * @param {Roo.XComponent} c the top level component.
36864          */
36865         'buildcomplete' : true
36866         
36867     });
36868     this.region = this.region || 'center'; // default..
36869     Roo.XComponent.register(this);
36870     this.modules = false;
36871     this.el = false; // where the layout goes..
36872     
36873     
36874 }
36875 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36876     /**
36877      * @property el
36878      * The created element (with Roo.factory())
36879      * @type {Roo.Layout}
36880      */
36881     el  : false,
36882     
36883     /**
36884      * @property el
36885      * for BC  - use el in new code
36886      * @type {Roo.Layout}
36887      */
36888     panel : false,
36889     
36890     /**
36891      * @property layout
36892      * for BC  - use el in new code
36893      * @type {Roo.Layout}
36894      */
36895     layout : false,
36896     
36897      /**
36898      * @cfg {Function|boolean} disabled
36899      * If this module is disabled by some rule, return true from the funtion
36900      */
36901     disabled : false,
36902     
36903     /**
36904      * @cfg {String} parent 
36905      * Name of parent element which it get xtype added to..
36906      */
36907     parent: false,
36908     
36909     /**
36910      * @cfg {String} order
36911      * Used to set the order in which elements are created (usefull for multiple tabs)
36912      */
36913     
36914     order : false,
36915     /**
36916      * @cfg {String} name
36917      * String to display while loading.
36918      */
36919     name : false,
36920     /**
36921      * @cfg {String} region
36922      * Region to render component to (defaults to center)
36923      */
36924     region : 'center',
36925     
36926     /**
36927      * @cfg {Array} items
36928      * A single item array - the first element is the root of the tree..
36929      * It's done this way to stay compatible with the Xtype system...
36930      */
36931     items : false,
36932     
36933     
36934      /**
36935      * render
36936      * render element to dom or tree
36937      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
36938      */
36939     
36940     render : function(el)
36941     {
36942         
36943         el = el || false;
36944         
36945         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
36946             // if parent is a '#.....' string, then let's use that..
36947             var ename = this.parent.substr(1)
36948             this.parent = false;
36949             el = Roo.get(ename);
36950             if (!el) {
36951                 Roo.log("Warning - element can not be found :#" + ename );
36952                 return;
36953             }
36954         }
36955         if (!this.parent) {
36956             
36957             el = el ? Roo.get(el) : false;
36958             
36959             // it's a top level one..
36960             this.parent =  {
36961                 el : new Roo.BorderLayout(el || document.body, {
36962                 
36963                      center: {
36964                          titlebar: false,
36965                          autoScroll:false,
36966                          closeOnTab: true,
36967                          tabPosition: 'top',
36968                           //resizeTabs: true,
36969                          alwaysShowTabs: el ? false :  true,
36970                          hideTabs: el ? true :  false,
36971                          minTabWidth: 140
36972                      }
36973                  })
36974             }
36975         }
36976         
36977         
36978             
36979         var tree = this.tree();
36980         tree.region = tree.region || this.region;
36981         this.el = this.parent.el.addxtype(tree);
36982         this.fireEvent('built', this);
36983         
36984         this.panel = this.el;
36985         this.layout = this.panel.layout;    
36986          
36987     }
36988     
36989 });
36990
36991 Roo.apply(Roo.XComponent, {
36992     
36993     /**
36994      * @property  buildCompleted
36995      * True when the builder has completed building the interface.
36996      * @type Boolean
36997      */
36998     buildCompleted : false,
36999      
37000     /**
37001      * @property  topModule
37002      * the upper most module - uses document.element as it's constructor.
37003      * @type Object
37004      */
37005      
37006     topModule  : false,
37007       
37008     /**
37009      * @property  modules
37010      * array of modules to be created by registration system.
37011      * @type {Array} of Roo.XComponent
37012      */
37013     
37014     modules : [],
37015     /**
37016      * @property  elmodules
37017      * array of modules to be created by which use #ID 
37018      * @type {Array} of Roo.XComponent
37019      */
37020      
37021     elmodules : [],
37022
37023     
37024     /**
37025      * Register components to be built later.
37026      *
37027      * This solves the following issues
37028      * - Building is not done on page load, but after an authentication process has occured.
37029      * - Interface elements are registered on page load
37030      * - Parent Interface elements may not be loaded before child, so this handles that..
37031      * 
37032      *
37033      * example:
37034      * 
37035      * MyApp.register({
37036           order : '000001',
37037           module : 'Pman.Tab.projectMgr',
37038           region : 'center',
37039           parent : 'Pman.layout',
37040           disabled : false,  // or use a function..
37041         })
37042      
37043      * * @param {Object} details about module
37044      */
37045     register : function(obj) {
37046         this.modules.push(obj);
37047          
37048     },
37049     /**
37050      * convert a string to an object..
37051      * eg. 'AAA.BBB' -> finds AAA.BBB
37052
37053      */
37054     
37055     toObject : function(str)
37056     {
37057         if (!str || typeof(str) == 'object') {
37058             return str;
37059         }
37060         if (str[0]=='#') {
37061             return str;
37062         }
37063
37064         var ar = str.split('.');
37065         var rt, o;
37066         rt = ar.shift();
37067             /** eval:var:o */
37068         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
37069         if (o === false) {
37070             throw "Module not found : " + str;
37071         }
37072         Roo.each(ar, function(e) {
37073             if (typeof(o[e]) == 'undefined') {
37074                 throw "Module not found : " + str;
37075             }
37076             o = o[e];
37077         });
37078         return o;
37079         
37080     },
37081     
37082     
37083     /**
37084      * move modules into their correct place in the tree..
37085      * 
37086      */
37087     preBuild : function ()
37088     {
37089         
37090         Roo.each(this.modules , function (obj)
37091         {
37092             obj.parent = this.toObject(obj.parent);
37093             
37094             if (!obj.parent) {
37095                 this.topModule = obj;
37096                 return;
37097             }
37098             if (typeof(obj.parent) == 'string') {
37099                 this.elmodules.push(obj);
37100                 return;
37101             }
37102             
37103             if (!obj.parent.modules) {
37104                 obj.parent.modules = new Roo.util.MixedCollection(false, 
37105                     function(o) { return o.order + '' }
37106                 );
37107             }
37108             
37109             obj.parent.modules.add(obj);
37110         }, this);
37111     },
37112     
37113      /**
37114      * make a list of modules to build.
37115      * @return {Array} list of modules. 
37116      */ 
37117     
37118     buildOrder : function()
37119     {
37120         var _this = this;
37121         var cmp = function(a,b) {   
37122             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
37123         };
37124         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
37125             throw "No top level modules to build";
37126         }
37127         
37128         // make a flat list in order of modules to build.
37129         var mods = this.topModule ? [ this.topModule ] : [];
37130         Roo.each(this.elmodules,function(e) { mods.push(e) });
37131
37132         
37133         // add modules to their parents..
37134         var addMod = function(m) {
37135            // Roo.debug && Roo.log(m.modKey);
37136             
37137             mods.push(m);
37138             if (m.modules) {
37139                 m.modules.keySort('ASC',  cmp );
37140                 m.modules.each(addMod);
37141             }
37142             // not sure if this is used any more..
37143             if (m.finalize) {
37144                 m.finalize.name = m.name + " (clean up) ";
37145                 mods.push(m.finalize);
37146             }
37147             
37148         }
37149         if (this.topModule) { 
37150             this.topModule.modules.keySort('ASC',  cmp );
37151             this.topModule.modules.each(addMod);
37152         }
37153         return mods;
37154     },
37155     
37156      /**
37157      * Build the registered modules.
37158      * @param {Object} parent element.
37159      * @param {Function} optional method to call after module has been added.
37160      * 
37161      */ 
37162    
37163     build : function() 
37164     {
37165         
37166         this.preBuild();
37167         var mods = this.buildOrder();
37168       
37169         //this.allmods = mods;
37170         //Roo.debug && Roo.log(mods);
37171         //return;
37172         if (!mods.length) { // should not happen
37173             throw "NO modules!!!";
37174         }
37175         
37176         
37177         
37178         // flash it up as modal - so we store the mask!?
37179         Roo.MessageBox.show({ title: 'loading' });
37180         Roo.MessageBox.show({
37181            title: "Please wait...",
37182            msg: "Building Interface...",
37183            width:450,
37184            progress:true,
37185            closable:false,
37186            modal: false
37187           
37188         });
37189         var total = mods.length;
37190         
37191         var _this = this;
37192         var progressRun = function() {
37193             if (!mods.length) {
37194                 Roo.debug && Roo.log('hide?');
37195                 Roo.MessageBox.hide();
37196                 if (_this.topModule) { 
37197                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
37198                 }
37199                 // THE END...
37200                 return false;   
37201             }
37202             
37203             var m = mods.shift();
37204             
37205             
37206             Roo.debug && Roo.log(m);
37207             // not sure if this is supported any more.. - modules that are are just function
37208             if (typeof(m) == 'function') { 
37209                 m.call(this);
37210                 return progressRun.defer(10, _this);
37211             } 
37212             
37213             
37214             
37215             Roo.MessageBox.updateProgress(
37216                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
37217                     " of " + total + 
37218                     (m.name ? (' - ' + m.name) : '')
37219                     );
37220             
37221          
37222             // is the module disabled?
37223             var disabled = (typeof(m.disabled) == 'function') ?
37224                 m.disabled.call(m.module.disabled) : m.disabled;    
37225             
37226             
37227             if (disabled) {
37228                 return progressRun(); // we do not update the display!
37229             }
37230             
37231             // now build 
37232             
37233             m.render();
37234             // it's 10 on top level, and 1 on others??? why...
37235             return progressRun.defer(10, _this);
37236              
37237         }
37238         progressRun.defer(1, _this);
37239      
37240         
37241         
37242     }
37243     
37244      
37245    
37246     
37247     
37248 });
37249  //<script type="text/javascript">
37250
37251
37252 /**
37253  * @class Roo.Login
37254  * @extends Roo.LayoutDialog
37255  * A generic Login Dialog..... - only one needed in theory!?!?
37256  *
37257  * Fires XComponent builder on success...
37258  * 
37259  * Sends 
37260  *    username,password, lang = for login actions.
37261  *    check = 1 for periodic checking that sesion is valid.
37262  *    passwordRequest = email request password
37263  *    logout = 1 = to logout
37264  * 
37265  * Affects: (this id="????" elements)
37266  *   loading  (removed) (used to indicate application is loading)
37267  *   loading-mask (hides) (used to hide application when it's building loading)
37268  *   
37269  * 
37270  * Usage: 
37271  *    
37272  * 
37273  * Myapp.login = Roo.Login({
37274      url: xxxx,
37275    
37276      realm : 'Myapp', 
37277      
37278      
37279      method : 'POST',
37280      
37281      
37282      * 
37283  })
37284  * 
37285  * 
37286  * 
37287  **/
37288  
37289 Roo.Login = function(cfg)
37290 {
37291     this.addEvents({
37292         'refreshed' : true
37293     });
37294     
37295     Roo.apply(this,cfg);
37296     
37297     Roo.onReady(function() {
37298         this.onLoad();
37299     }, this);
37300     // call parent..
37301     
37302    
37303     Roo.Login.superclass.constructor.call(this, this);
37304     //this.addxtype(this.items[0]);
37305     
37306     
37307 }
37308
37309
37310 Roo.extend(Roo.Login, Roo.LayoutDialog, {
37311     
37312     /**
37313      * @cfg {String} method
37314      * Method used to query for login details.
37315      */
37316     
37317     method : 'POST',
37318     /**
37319      * @cfg {String} url
37320      * URL to query login data. - eg. baseURL + '/Login.php'
37321      */
37322     url : '',
37323     
37324     /**
37325      * @property user
37326      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
37327      * @type {Object} 
37328      */
37329     user : false,
37330     /**
37331      * @property checkFails
37332      * Number of times we have attempted to get authentication check, and failed.
37333      * @type {Number} 
37334      */
37335     checkFails : 0,
37336       /**
37337      * @property intervalID
37338      * The window interval that does the constant login checking.
37339      * @type {Number} 
37340      */
37341     intervalID : 0,
37342     
37343     
37344     onLoad : function() // called on page load...
37345     {
37346         // load 
37347          
37348         if (Roo.get('loading')) { // clear any loading indicator..
37349             Roo.get('loading').remove();
37350         }
37351         
37352         //this.switchLang('en'); // set the language to english..
37353        
37354         this.check({
37355             success:  function(response, opts)  {  // check successfull...
37356             
37357                 var res = this.processResponse(response);
37358                 this.checkFails =0;
37359                 if (!res.success) { // error!
37360                     this.checkFails = 5;
37361                     //console.log('call failure');
37362                     return this.failure(response,opts);
37363                 }
37364                 
37365                 if (!res.data.id) { // id=0 == login failure.
37366                     return this.show();
37367                 }
37368                 
37369                               
37370                         //console.log(success);
37371                 this.fillAuth(res.data);   
37372                 this.checkFails =0;
37373                 Roo.XComponent.build();
37374             },
37375             failure : this.show
37376         });
37377         
37378     }, 
37379     
37380     
37381     check: function(cfg) // called every so often to refresh cookie etc..
37382     {
37383         if (cfg.again) { // could be undefined..
37384             this.checkFails++;
37385         } else {
37386             this.checkFails = 0;
37387         }
37388         var _this = this;
37389         if (this.sending) {
37390             if ( this.checkFails > 4) {
37391                 Roo.MessageBox.alert("Error",  
37392                     "Error getting authentication status. - try reloading, or wait a while", function() {
37393                         _this.sending = false;
37394                     }); 
37395                 return;
37396             }
37397             cfg.again = true;
37398             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
37399             return;
37400         }
37401         this.sending = true;
37402         
37403         Roo.Ajax.request({  
37404             url: this.url,
37405             params: {
37406                 getAuthUser: true
37407             },  
37408             method: this.method,
37409             success:  cfg.success || this.success,
37410             failure : cfg.failure || this.failure,
37411             scope : this,
37412             callCfg : cfg
37413               
37414         });  
37415     }, 
37416     
37417     
37418     logout: function()
37419     {
37420         window.onbeforeunload = function() { }; // false does not work for IE..
37421         this.user = false;
37422         var _this = this;
37423         
37424         Roo.Ajax.request({  
37425             url: this.url,
37426             params: {
37427                 logout: 1
37428             },  
37429             method: 'GET',
37430             failure : function() {
37431                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
37432                     document.location = document.location.toString() + '?ts=' + Math.random();
37433                 });
37434                 
37435             },
37436             success : function() {
37437                 _this.user = false;
37438                 this.checkFails =0;
37439                 // fixme..
37440                 document.location = document.location.toString() + '?ts=' + Math.random();
37441             }
37442               
37443               
37444         }); 
37445     },
37446     
37447     processResponse : function (response)
37448     {
37449         var res = '';
37450         try {
37451             res = Roo.decode(response.responseText);
37452             // oops...
37453             if (typeof(res) != 'object') {
37454                 res = { success : false, errorMsg : res, errors : true };
37455             }
37456             if (typeof(res.success) == 'undefined') {
37457                 res.success = false;
37458             }
37459             
37460         } catch(e) {
37461             res = { success : false,  errorMsg : response.responseText, errors : true };
37462         }
37463         return res;
37464     },
37465     
37466     success : function(response, opts)  // check successfull...
37467     {  
37468         this.sending = false;
37469         var res = this.processResponse(response);
37470         if (!res.success) {
37471             return this.failure(response, opts);
37472         }
37473         if (!res.data || !res.data.id) {
37474             return this.failure(response,opts);
37475         }
37476         //console.log(res);
37477         this.fillAuth(res.data);
37478         
37479         this.checkFails =0;
37480         
37481     },
37482     
37483     
37484     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37485     {
37486         this.authUser = -1;
37487         this.sending = false;
37488         var res = this.processResponse(response);
37489         //console.log(res);
37490         if ( this.checkFails > 2) {
37491         
37492             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37493                 "Error getting authentication status. - try reloading"); 
37494             return;
37495         }
37496         opts.callCfg.again = true;
37497         this.check.defer(1000, this, [ opts.callCfg ]);
37498         return;  
37499     },
37500     
37501     
37502     
37503     fillAuth: function(au) {
37504         this.startAuthCheck();
37505         this.authUserId = au.id;
37506         this.authUser = au;
37507         this.lastChecked = new Date();
37508         this.fireEvent('refreshed', au);
37509         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37510         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37511         au.lang = au.lang || 'en';
37512         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37513         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37514         this.switchLang(au.lang );
37515         
37516      
37517         // open system... - -on setyp..
37518         if (this.authUserId  < 0) {
37519             Roo.MessageBox.alert("Warning", 
37520                 "This is an open system - please set up a admin user with a password.");  
37521         }
37522          
37523         //Pman.onload(); // which should do nothing if it's a re-auth result...
37524         
37525              
37526     },
37527     
37528     startAuthCheck : function() // starter for timeout checking..
37529     {
37530         if (this.intervalID) { // timer already in place...
37531             return false;
37532         }
37533         var _this = this;
37534         this.intervalID =  window.setInterval(function() {
37535               _this.check(false);
37536             }, 120000); // every 120 secs = 2mins..
37537         
37538         
37539     },
37540          
37541     
37542     switchLang : function (lang) 
37543     {
37544         _T = typeof(_T) == 'undefined' ? false : _T;
37545           if (!_T || !lang.length) {
37546             return;
37547         }
37548         
37549         if (!_T && lang != 'en') {
37550             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37551             return;
37552         }
37553         
37554         if (typeof(_T.en) == 'undefined') {
37555             _T.en = {};
37556             Roo.apply(_T.en, _T);
37557         }
37558         
37559         if (typeof(_T[lang]) == 'undefined') {
37560             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37561             return;
37562         }
37563         
37564         
37565         Roo.apply(_T, _T[lang]);
37566         // just need to set the text values for everything...
37567         var _this = this;
37568         /* this will not work ...
37569         if (this.form) { 
37570             
37571                
37572             function formLabel(name, val) {
37573                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37574             }
37575             
37576             formLabel('password', "Password"+':');
37577             formLabel('username', "Email Address"+':');
37578             formLabel('lang', "Language"+':');
37579             this.dialog.setTitle("Login");
37580             this.dialog.buttons[0].setText("Forgot Password");
37581             this.dialog.buttons[1].setText("Login");
37582         }
37583         */
37584         
37585         
37586     },
37587     
37588     
37589     title: "Login",
37590     modal: true,
37591     width:  350,
37592     //height: 230,
37593     height: 180,
37594     shadow: true,
37595     minWidth:200,
37596     minHeight:180,
37597     //proxyDrag: true,
37598     closable: false,
37599     draggable: false,
37600     collapsible: false,
37601     resizable: false,
37602     center: {  // needed??
37603         autoScroll:false,
37604         titlebar: false,
37605        // tabPosition: 'top',
37606         hideTabs: true,
37607         closeOnTab: true,
37608         alwaysShowTabs: false
37609     } ,
37610     listeners : {
37611         
37612         show  : function(dlg)
37613         {
37614             //console.log(this);
37615             this.form = this.layout.getRegion('center').activePanel.form;
37616             this.form.dialog = dlg;
37617             this.buttons[0].form = this.form;
37618             this.buttons[0].dialog = dlg;
37619             this.buttons[1].form = this.form;
37620             this.buttons[1].dialog = dlg;
37621            
37622            //this.resizeToLogo.defer(1000,this);
37623             // this is all related to resizing for logos..
37624             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37625            //// if (!sz) {
37626              //   this.resizeToLogo.defer(1000,this);
37627              //   return;
37628            // }
37629             //var w = Ext.lib.Dom.getViewWidth() - 100;
37630             //var h = Ext.lib.Dom.getViewHeight() - 100;
37631             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37632             //this.center();
37633             if (this.disabled) {
37634                 this.hide();
37635                 return;
37636             }
37637             
37638             if (this.user.id < 0) { // used for inital setup situations.
37639                 return;
37640             }
37641             
37642             if (this.intervalID) {
37643                 // remove the timer
37644                 window.clearInterval(this.intervalID);
37645                 this.intervalID = false;
37646             }
37647             
37648             
37649             if (Roo.get('loading')) {
37650                 Roo.get('loading').remove();
37651             }
37652             if (Roo.get('loading-mask')) {
37653                 Roo.get('loading-mask').hide();
37654             }
37655             
37656             //incomming._node = tnode;
37657             this.form.reset();
37658             //this.dialog.modal = !modal;
37659             //this.dialog.show();
37660             this.el.unmask(); 
37661             
37662             
37663             this.form.setValues({
37664                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37665                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37666             });
37667             
37668             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37669             if (this.form.findField('username').getValue().length > 0 ){
37670                 this.form.findField('password').focus();
37671             } else {
37672                this.form.findField('username').focus();
37673             }
37674     
37675         }
37676     },
37677     items : [
37678          {
37679        
37680             xtype : 'ContentPanel',
37681             xns : Roo,
37682             region: 'center',
37683             fitToFrame : true,
37684             
37685             items : [
37686     
37687                 {
37688                
37689                     xtype : 'Form',
37690                     xns : Roo.form,
37691                     labelWidth: 100,
37692                     style : 'margin: 10px;',
37693                     
37694                     listeners : {
37695                         actionfailed : function(f, act) {
37696                             // form can return { errors: .... }
37697                                 
37698                             //act.result.errors // invalid form element list...
37699                             //act.result.errorMsg// invalid form element list...
37700                             
37701                             this.dialog.el.unmask();
37702                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37703                                         "Login failed - communication error - try again.");
37704                                       
37705                         },
37706                         actioncomplete: function(re, act) {
37707                              
37708                             Roo.state.Manager.set(
37709                                 this.dialog.realm + '.username',  
37710                                     this.findField('username').getValue()
37711                             );
37712                             Roo.state.Manager.set(
37713                                 this.dialog.realm + '.lang',  
37714                                 this.findField('lang').getValue() 
37715                             );
37716                             
37717                             this.dialog.fillAuth(act.result.data);
37718                               
37719                             this.dialog.hide();
37720                             
37721                             if (Roo.get('loading-mask')) {
37722                                 Roo.get('loading-mask').show();
37723                             }
37724                             Roo.XComponent.build();
37725                             
37726                              
37727                             
37728                         }
37729                     },
37730                     items : [
37731                         {
37732                             xtype : 'TextField',
37733                             xns : Roo.form,
37734                             fieldLabel: "Email Address",
37735                             name: 'username',
37736                             width:200,
37737                             autoCreate : {tag: "input", type: "text", size: "20"}
37738                         },
37739                         {
37740                             xtype : 'TextField',
37741                             xns : Roo.form,
37742                             fieldLabel: "Password",
37743                             inputType: 'password',
37744                             name: 'password',
37745                             width:200,
37746                             autoCreate : {tag: "input", type: "text", size: "20"},
37747                             listeners : {
37748                                 specialkey : function(e,ev) {
37749                                     if (ev.keyCode == 13) {
37750                                         this.form.dialog.el.mask("Logging in");
37751                                         this.form.doAction('submit', {
37752                                             url: this.form.dialog.url,
37753                                             method: this.form.dialog.method
37754                                         });
37755                                     }
37756                                 }
37757                             }  
37758                         },
37759                         {
37760                             xtype : 'ComboBox',
37761                             xns : Roo.form,
37762                             fieldLabel: "Language",
37763                             name : 'langdisp',
37764                             store: {
37765                                 xtype : 'SimpleStore',
37766                                 fields: ['lang', 'ldisp'],
37767                                 data : [
37768                                     [ 'en', 'English' ],
37769                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37770                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37771                                 ]
37772                             },
37773                             
37774                             valueField : 'lang',
37775                             hiddenName:  'lang',
37776                             width: 200,
37777                             displayField:'ldisp',
37778                             typeAhead: false,
37779                             editable: false,
37780                             mode: 'local',
37781                             triggerAction: 'all',
37782                             emptyText:'Select a Language...',
37783                             selectOnFocus:true,
37784                             listeners : {
37785                                 select :  function(cb, rec, ix) {
37786                                     this.form.switchLang(rec.data.lang);
37787                                 }
37788                             }
37789                         
37790                         }
37791                     ]
37792                 }
37793                   
37794                 
37795             ]
37796         }
37797     ],
37798     buttons : [
37799         {
37800             xtype : 'Button',
37801             xns : 'Roo',
37802             text : "Forgot Password",
37803             listeners : {
37804                 click : function() {
37805                     //console.log(this);
37806                     var n = this.form.findField('username').getValue();
37807                     if (!n.length) {
37808                         Roo.MessageBox.alert("Error", "Fill in your email address");
37809                         return;
37810                     }
37811                     Roo.Ajax.request({
37812                         url: this.dialog.url,
37813                         params: {
37814                             passwordRequest: n
37815                         },
37816                         method: this.dialog.method,
37817                         success:  function(response, opts)  {  // check successfull...
37818                         
37819                             var res = this.dialog.processResponse(response);
37820                             if (!res.success) { // error!
37821                                Roo.MessageBox.alert("Error" ,
37822                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37823                                return;
37824                             }
37825                             Roo.MessageBox.alert("Notice" ,
37826                                 "Please check you email for the Password Reset message");
37827                         },
37828                         failure : function() {
37829                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37830                         }
37831                         
37832                     });
37833                 }
37834             }
37835         },
37836         {
37837             xtype : 'Button',
37838             xns : 'Roo',
37839             text : "Login",
37840             listeners : {
37841                 
37842                 click : function () {
37843                         
37844                     this.dialog.el.mask("Logging in");
37845                     this.form.doAction('submit', {
37846                             url: this.dialog.url,
37847                             method: this.dialog.method
37848                     });
37849                 }
37850             }
37851         }
37852     ]
37853   
37854   
37855 })
37856  
37857
37858
37859